////////////////////////////////////////////////////////////////////////////////
//
//  Microsoft Research Singularity
//
//  Copyright (c) Microsoft Corporation.  All rights reserved.
//
//  File:   S3Trio64.cs
//
//  Note:
//

using Microsoft.Singularity;

using System;
using System;
using System.Text;
using System.Configuration.Assemblies;
using System.Runtime.Remoting;
using System.Runtime.InteropServices;
using System.Collections;

namespace Microsoft.Singularity.Drivers
{
        public void BitBltPng(int x, int y, byte[] buffer)
        {
            if (!Ready) {
                return;
            }

            //Png.Png.DisplayPngImage(x,y,buffer,lineBuffer);
            Png obj=new Png();
            obj.chunk_list= new Hashtable();
            ArrayList idat_list = new ArrayList();
            CHUNK chunk;
            obj.buffer = buffer;
            obj.ptr=0;
            if (!obj.chksignature()) {
                throw new Exception("Invalid Signature");
            }

            // step 1 : chk signature
            // look for header
            while (obj.ptr < obj.buffer.Length) {
                chunk = new CHUNK();
                chunk.init_CHUNK(obj.buffer, ref obj.ptr,out obj.tp);
                switch (obj.tp) {
                    case TYPE.IHDR:
                        cIHDR ihdr = new cIHDR(chunk,x,y);
                        obj.scanlength = ihdr.scanlength;
                        obj.bpp= ihdr.bpp;
                        // do some error checking

                        if (ihdr.width == 0 || ihdr.height == 0) {
                            return;
                        }

                        if (x < 0) {
                            ihdr.x = (int)(ActiveMode.ScreenWidth + x - ihdr.width);
                        }
                        if (y < 0) {
                            ihdr.y = (int)(ActiveMode.ScreenHeight + y - ihdr.height);
                        }

                        if (ihdr.x < 0 || ihdr.x + ihdr.width > ActiveMode.ScreenWidth ||
                            ihdr.y < 0 || ihdr.y + ihdr.height > ActiveMode.ScreenHeight)
                        {

                            throw new OverflowException("Draw bounds invalid.");
                        }
                        obj.chunk_list.Add("IHDR",ihdr);
                        break;
                    case TYPE.bKGD:// define later
                        break;
                    case TYPE.cHRM:
                        break;
                    case TYPE.gAMA:
                        cgAMA gama = new cgAMA(chunk);
                        obj.chunk_list.Add("gAMA",gama);
                        break;
                    case TYPE.hIST:
                        break;
                    case TYPE.iCCP:
                        break;
                    case TYPE.IDAT:cIDAT idat = new cIDAT(chunk);
                        idat_list.Add(idat);
                        if (!obj.chunk_list.ContainsKey("IDAT")) {
                            obj.chunk_list.Add("IDAT",idat_list);
                        }
//                        DebugStub.Print("Chkpoint 1");
//                        try
//                        {
//                            obj.chunk_list.Add("IDAT",idat_list);
//                            DebugStub.Print("Chkpoint 1");
//                        }
//                        catch(System.ArgumentException)
//                        {// do nothing
//                            DebugStub.Break();
//                        }
                        break;
                    case TYPE.IEND:
                        cIEND iend= new cIEND(chunk);
                        obj.chunk_list.Add("IEND",iend);
                        break;
                    case TYPE.iTXt:
                        break;
                    case TYPE.meTa:
                        break;
                    case TYPE.pHYs:
                        break;
                    case TYPE.PLTE:
                        break;
                    case TYPE.sBIT:
                        break;
                    case TYPE.sPLT:
                        break;
                    case TYPE.sRGB:
                        break;
                    case TYPE.tEXt:
                        break;
                    case TYPE.tIME:
                        break;
                    case TYPE.tRNS:
                        break;
                    case TYPE.zTXt:
                        break;
                    default:throw new Exception("Invalid chunk type");

                }
            }
            try {
                obj.buffer=((cIDAT)((ArrayList)obj.chunk_list["IDAT"])[0]).decompressandfilter(obj.scanlength,obj.bpp,idat_list);
            }
                //decompress it
                //defilter it

            catch (Exception e) {
                DebugStub.Print("message from exception {0} {1}.\n",
                                __arglist(e.Message, e));// to be removed later
                DebugStub.Print(" IDAT CHUNK MISSING");
                throw new Exception(" IDAT CHUNK MISSING or Corrupt");
            }
            try {
                byte[] correctedbuffer; // buffer after gamma correction
                //add later ((cgAMA)obj.chunk_list[TYPE.gAMA]).gammacorrection(obj.buffer,out correctedbuffer);
                // add later    obj.buffer=correctedbuffer;
                //gamma correction
            }
            catch (Exception) {
                // Do nothing as ancilliary chunk
            }
//            DebugStub.Break();
//            for (long i=0;i<obj.buffer.Length;i++)
//                DebugStub.Print("{0:x} ", __arglist(obj.buffer[i]));
            try {
                Display.display(obj.buffer,obj.chunk_list,lineBuffer,screenBuffer,ActiveMode);
                //display
            }
            catch (Exception e) {
                DebugStub.Print("Exception {0} {1}", __arglist(e.Message, e));
                DebugStub.Print("IHDR CHUNK MISSING");
            }
//            DebugStub.Print("Exitting");
//            DebugStub.Break();
            // end
        }

    enum FILTER_METHOD:byte{ADAPTIVE_FILTER};
    enum COMPRESSION_METHOD:byte{DEFLATE_INFLATE};
    enum COLOR_TYPE:byte{GRAY_SCALE=0,RGBTRIPLATE=2,PALETTEINDEX=3,GRAYSCALE_ALPHA=4,RGB_ALPHA=6};
    enum INTERLACE_METHOD:byte{NO_INTERLACE,ADAM7};
    enum CHUNK_TYPE:byte{ANCILLARY,CRITICAL};
    enum TYPE:uint
    {
        IHDR=0x49484452,
        PLTE=0x504c5445,
        IDAT=0x49444154,
        IEND=0x49454e44,
        tRNS=0x74524e53,
        gAMA=0x67414d41,
        cHRM=0x6348524d,
        sRGB=0x73524742,
        iCCP=0x69434350,
        iTXt=0x69545874,
        tEXt=0x74455874,
        zTXt=0x7A545874,
        bKGD=0x624b4744,
        pHYs=0x70485973,
        sPLT=0x73504c54,
        sBIT=0x73424954,
        hIST=0x68495354,
        tIME=0x74494d45,
        meTa=0x6d655461
    };
    enum FILTER_TYPE:byte{NONE,SUB,UP,AVERAGE,PAETH};
    enum FILTER_OPERATION:byte{ENCODE,DECODE};

    public class Buffer
    {
        public byte[] buf;
        public int startptr;
        public int length;
        public Buffer(byte[] buffer , int startpointer , int len)
        {
            buf= buffer;
            startptr = startpointer;
            length = len;
        }
    }

    class cIHDR : CHUNK
    {
        public int x;
        public int y;
        public uint width;          // width of image in pixels: 0 not valid
        public uint height;     // height of image in pixels: 0 not valid
        public byte bit_depth;      // no of bits per sample or per palette index
        public byte sample_depth;   // not present in the IHDR but initialized depending on data present in it
        public COLOR_TYPE color_type;   // describes the interpretion of image data
        COMPRESSION_METHOD compression_method;  // compression method
        FILTER_METHOD filter_method;            // filter method
        INTERLACE_METHOD interlace_method;      // transmission order of the image
        public uint scanlength;
        public byte bpp;

        public cIHDR(CHUNK obj,int x,int y):base(obj)
        {
            this.x = x;
            this.y =y;
            color_type=COLOR_TYPE.GRAY_SCALE;
            compression_method=COMPRESSION_METHOD.DEFLATE_INFLATE;
            filter_method = FILTER_METHOD.ADAPTIVE_FILTER;
            interlace_method=INTERLACE_METHOD.NO_INTERLACE;
            init();
        }
        public void init( ) // throws an exception if this chunk found invalid or corrupt
        {
            uint ptr=(uint)data.startptr;
            uint startindex=ptr;
            try {
                width=(256*256*256*(uint)data.buf[ptr++]+256*256*(uint)data.buf[ptr++]
                    +256*(uint)data.buf[ptr++]+(uint)data.buf[ptr++]);
                height=256*256*256*(uint)data.buf[ptr++]+256*256*(uint)data.buf[ptr++]
                    +256*(uint)data.buf[ptr++]+(uint)data.buf[ptr++];
                bit_depth=data.buf[ptr++];
                color_type=(COLOR_TYPE)(data.buf[ptr++]);
//                DebugStub.Print("color_type : {0} data.buf[ptr-1]:{1}",__arglist((int)color_type,data.buf[ptr-1]));
//                DebugStub.Break();

                compression_method=(COMPRESSION_METHOD)(data.buf[ptr++]);
                filter_method=(FILTER_METHOD)(data.buf[ptr++]);
                interlace_method=(INTERLACE_METHOD)(data.buf[ptr++]);
                if (color_type == COLOR_TYPE.PALETTEINDEX)sample_depth = 3; // setting sample_depth
                else sample_depth = bit_depth;
            }
            catch (Exception) // if any exception then invalid header
            {
                throw new Exception("invalid IHDR");
            }
            if ((ptr - length) != startindex)   //if incorrect length then invalid header
                throw new Exception("invalid IHDR");
            switch (bit_depth) {
                case 1:if (color_type!=COLOR_TYPE.GRAY_SCALE && color_type!=COLOR_TYPE.PALETTEINDEX)
                           throw new Exception("invalid IHDR");
                    break;
                case 2:if (color_type!=COLOR_TYPE.GRAY_SCALE && color_type!=COLOR_TYPE.PALETTEINDEX)
                           throw new Exception("invalid IHDR");
                    break;
                case 4:if (color_type!=COLOR_TYPE.GRAY_SCALE && color_type!=COLOR_TYPE.PALETTEINDEX)
                           throw new Exception("invalid IHDR");
                    break;
                case 8:if (color_type!=COLOR_TYPE.GRAY_SCALE && color_type!=COLOR_TYPE.PALETTEINDEX
                           && color_type!=COLOR_TYPE.RGB_ALPHA && color_type!=COLOR_TYPE.GRAYSCALE_ALPHA && color_type!=COLOR_TYPE.RGBTRIPLATE)
                           throw new Exception("invalid IHDR");
                    break;
                case 16:if (color_type!=COLOR_TYPE.GRAY_SCALE && color_type!=COLOR_TYPE.RGB_ALPHA
                            && color_type!=COLOR_TYPE.GRAYSCALE_ALPHA && color_type!=COLOR_TYPE.RGBTRIPLATE)
                            throw new Exception("invalid IHDR");
                    break;
                default:throw new Exception("invalid IHDR");
            }
            switch (color_type) {
                case COLOR_TYPE.GRAY_SCALE:bpp=(byte)((bit_depth%8==0)?bit_depth/8:bit_depth/8+1);break;
                case COLOR_TYPE.GRAYSCALE_ALPHA:bpp=(byte)(((bit_depth%8==0)?bit_depth/8:bit_depth/8+1)+1);break;
                case COLOR_TYPE.PALETTEINDEX:bpp=(byte)(((bit_depth*3)%8==0)?(bit_depth*3/8):(bit_depth*3)/8+1);break;
                case COLOR_TYPE.RGBTRIPLATE:bpp=(byte)(((bit_depth*3)%8==0)?(bit_depth*3/8):(bit_depth*3)/8+1);break;
                case COLOR_TYPE.RGB_ALPHA:bpp=(byte)((((bit_depth*3)%8==0)?(bit_depth*3/8):(bit_depth*3)/8+1)+1);break;
                default : throw new Exception("Invalid color type");
            }
            scanlength= width * bpp + 1; // 1 for the filter type
        }
    }// end of class cIHDR

    class cgAMA:CHUNK
    {
        uint gamma;         // stores gamma times 100000
        public cgAMA(CHUNK obj):base(obj)
        {
            gamma = 100000;
            init();

        }
        public void init( ) // throws an exception if this chunk found invalid or corrupt
        {
            uint ptr=(uint)data.startptr;
            uint startindex=ptr;
            try {
                gamma=(256*256*256*(uint)data.buf[ptr++]+256*256*(uint)data.buf[ptr++] +256*(uint)data.buf[ptr++]+(uint)data.buf[ptr++]);
            }
                // it won't throw any exception since its an ancilliary chunk but if
                // length is invalid then it will throw an exception
            catch (IndexOutOfRangeException) // if index out of range then invalid header
            {
                throw new Exception("invalid gAMA");
            }
            if ((ptr - length) != startindex)   //if incorrect length then invalid header
                throw new Exception("invalid gAMA");
        }
        public void gammacorrection(byte[] srcbuf,out byte[] dstbuf)
        {
            dstbuf = new byte[10];
        }
        private byte gammacorrection(byte integer_sample)
        {
            //      double sample = integer_sample/((2<<bitdepth)- 1.0);
            //      display_output = pow(sample,(1.0/gamma));
            //      display_input = inverse_display_transfer(display_output);
            //      framebuf_sample = (byte)(display_input * MAX_FRAMEBUF_SAMPLE);
            //      return framebuf_sample;
            return integer_sample;
        }
        private ushort gammacorrection(ushort integer_sample)
        {
            //          sample = integer_sample/(pow(2,bitdepth)- 1.0);
            //          display_output = pow(sample,(1.0/gamma));
            //          display_input = inverse_display_transfer(display_output);
            //          framebuf_sample = ROUND(display_input * MAX_FRAMEBUF_SAMPLE);
            //          return framebuf_sample;
            return integer_sample;
        }
    }// end of class cgAMA

    class cIDAT:CHUNK
    {
        public cIDAT(CHUNK obj):base(obj)
        {
            init();
        }
        public void init( ) // throws an exception if this chunk found invalid or corrupt
        {
            if (data.length!=length) throw new Exception("Invalid IHDR chunk");
        }
        public byte[] decompressandfilter(uint scanlength,byte bpp,ArrayList idat_list)
        {
            byte[] decompressedbuffer;
            // combine data buffers of all the cIDAT objects in ArrayList idat_list
            MemoryStream ms = new MemoryStream();
            foreach (cIDAT idat in idat_list) {
                ms.Write(idat.data.buf,idat.data.startptr,idat.data.length);
            }

            //idat_list = null;
            Decompress.decompress(ms,out decompressedbuffer);
           // ms = null;
            byte[] defilteredbuffer;
             FILTER.decode(decompressedbuffer,out defilteredbuffer,scanlength,bpp);
//            DebugStub.Break();
//            for (long i=0;i<decompressedbuffer.Length;i++)
//                DebugStub.Print("{0:x} ", __argist(decompressedbuffer[i]));
             return defilteredbuffer;
        }

    }// end of class cIDAT
    class cIEND:CHUNK
    {
        public cIEND(CHUNK obj):base(obj)
        {
            init();

        }
        public void init( ) // throws an exception if this chunk found invalid or corrupt
        {
            if (length != 0) throw new Exception("invalid IEND");
            //            if (data.length!=startindex)   //if incorrect length then invalid header
            //                throw new Exception("invalid IEND");
        }
    }// end of class cIEND
    // type of recognised chunks
    class CRC
    {
        static uint[] crc_table=new uint [256];           // CRC table
        static ulong CRC_POLY=0xedb88320;     // CRC polynomial
        static bool crc_table_computed=false; // flag to indicate that CRC table is not yet made
        private static void make_crc_table()  // make CRC table so that future calculations are done faster
        {

            ulong c;
            for (uint i = 0; i < 256; i++) {
                c=(ulong)i;
                for (int j = 0; j < 8; j++) {
                    if ((c & 1) != 0)
                        c=CRC_POLY^ (c>>1);
                    else
                        c=c>>1;
                }
                crc_table[i]=(uint)c;
            }
            crc_table_computed=true;
        }

        private static uint calculateCRC(uint crc,byte[] buf,uint start_index,uint end_index)  // calculate CRC
        {
            if (!crc_table_computed)
                make_crc_table();
            for (uint i = start_index; i < end_index; i++) {
                crc = crc_table[(crc^buf[i]) & 0xff]^(crc>>8);
            }
            return crc;
        }
        public static uint getCRC(byte[] buf,uint start_index,uint end_index)       // return CRC
        {
            return (calculateCRC(0xffffffff,buf,start_index, end_index)^0xffffffff);
        }
    }

    class CHUNK:CRC
    {
        protected uint length;
        protected uint type;
        protected Buffer data;
        protected uint crc;
        protected CHUNK_TYPE chunk_type;
        public CHUNK(){}
        public CHUNK(CHUNK obj)
        {
            length= obj.length;
            type = obj.type;
            data = obj.data;
            crc = obj.crc;
            chunk_type= obj.chunk_type;
        }
        public void init_CHUNK(byte[] buffer, ref uint ptr,out TYPE t)// throws an exception if chunk invalid
        {

            uint startindex=ptr;
            uint calculatedCRC;
            try {
                length = (256*256*256*(uint)buffer[ptr++]+256*256*(uint)buffer[ptr++]
                    +256*(uint)buffer[ptr++]+(uint)buffer[ptr++]);
                calculatedCRC = getCRC(buffer, ptr, ptr + 4 + length);
                type = (uint)buffer[ptr++]*256*256*256+(uint)buffer[ptr++]*256*256+((uint)buffer[ptr++]*256)+(uint)buffer[ptr++];
                t=(TYPE)type;
                chunk_type = ((type & 0x20000000)==0)?CHUNK_TYPE.CRITICAL:CHUNK_TYPE.ANCILLARY;
                // if bit 5 of first bye of type is 1 then its ancillary otherwise essential
                if (buffer.Length<(startindex+length))throw new Exception("invalid CHUNK");
                data = new Buffer(buffer,(int)ptr,(int)length);
                ptr += length;
                //data = new byte[length];
                //for (int i = 0; i < length; i++)
                //  data[i]=buffer[ptr++];  
                crc=(256*256*256*(uint)buffer[ptr++]+256*256*(uint)buffer[ptr++]
                    +256*(uint)buffer[ptr++]+(uint)buffer[ptr++]);
            }
            catch (Exception e) // any exception indicates that the chunk is invalid
            {
                DebugStub.Print("message from exception {0} {1}",
                                __arglist(e.Message, e));// to be removed later
                throw new Exception("invalid CHUNK");
            }
            if (crc != calculatedCRC) {
                DebugStub.Print("CRC chk failed in chunk {0}\ncalculated CRC: {1:x}\n actual CRC : {2:x}",__arglist(t,calculatedCRC,crc));
//                Console.Write("start:");
//                for (int i=0;i<data.length;i++)
//                    Console.Write("{0:x}",data.buf[data.startptr + i]);
//                Console.Write(":end");
                throw new Exception("CRC chk failed");
            }
        }
    }

    delegate  void defilter();

    class FILTER
    {
        static byte[] sbuf;
        static uint scanlength;
        //delegate void screenmode(out S3_VIDEO_MODES ModeEntry);
        static byte bpp;
        public static void encode(byte[] srcbuf,out byte[] dstbuf,uint slength)
        {
            // encode the inp buffer to form the output buffer
            dstbuf = new byte[10];
        }
        public static void decode(byte[] srcbuf,out byte[] dstbuf,uint slength,byte b)
        {
            sbuf = srcbuf;
            scanlength= slength;
            bpp =b;
            //  if (srcbuf.Length % scanlength !=0) throw new Exception("Invalid buffer after decoding");
            // the buffer length should be divisible by the scanlength
            uint ptr=0;
            byte t; // no filter to start with
            while (ptr < srcbuf.Length) {
                t=srcbuf[ptr++];
                switch (t) {
                    case 0:None(ref ptr);break;
                    case 1:sub(ref ptr);break;
                    case 2:up(ref ptr);break;
                    case 3:average(ref ptr);break;
                    case 4:paeth(ref ptr);break;
                    default:
                    {
                        DebugStub.Print("Invalid filter type at byte offset : {0} in decompressed buffer : byte read: {1}",
                                        __arglist(ptr,t));
                        throw new Exception("Invalid Filter Type");// invalid filter type
                    }
                }

            }
            dstbuf = srcbuf;

        }
        private static void None(ref uint ptr)
        {
            ptr += scanlength-1;  // scan length includes the byte for filter type as well
        }
        private static void sub(ref uint ptr)
        {
            for (int i = bpp; i <(scanlength - 1); i++)
                //note the loop starting from 1 and not 0: ptr will be 1 inside this loop
                sbuf[ptr + i]=(byte)(sbuf[ptr + i]+ sbuf[ptr + i -bpp]);
            ptr +=scanlength-1;
        }
        private static void up(ref uint ptr)
        {
            if (ptr >= scanlength)// thats is for 2nd onwards line
                for (int i = 0; i <(scanlength - 1); i++)
                    sbuf[ptr + i]=(byte)(sbuf[ptr + i]+sbuf[ptr + i -scanlength]);
            ptr +=scanlength-1;
        }
        private static void average(ref uint ptr)
        {
            if (ptr >= scanlength)// thats is for 2nd onwards line
            {
                for (int i = 0; i < bpp; i++)
                    sbuf[ptr]=(byte)(sbuf[ptr]+(sbuf[ptr-scanlength]/2)); // truncate the other part
                for (int i = bpp; i <(scanlength - 1); i++)
                    sbuf[ptr + i]=(byte)(sbuf[ptr + i]+(sbuf[ptr + i -scanlength]+sbuf[ptr+i-bpp])/2);
            }
            else  // first row
            {
                for (int i = bpp; i <(scanlength - 1); i++)
                    sbuf[ptr + i]=(byte)(sbuf[ptr + i]+(byte)(sbuf[ptr+i-bpp]/2));
            }
            ptr +=scanlength-1;
        }
        private static void paeth(ref uint ptr)
        {
            // two cases :
            // 1:first row
            if (ptr < scanlength)// first row
            {
                for (uint i = 0; i < bpp; i++)
                    sbuf[ptr+i]=(byte)(sbuf[ptr+i] + paethpredictor(0,0,0));
                for (uint i = bpp; i <(scanlength - 1); i++)
                    sbuf[ptr+i]=(byte)(sbuf[ptr+i] + paethpredictor(sbuf[ptr+i-bpp],0,0));
            }
            else    // not the first row
            {
                for (uint i = 0; i < bpp; i++)
                    sbuf[ptr+i]=(byte)(sbuf[ptr+i] + paethpredictor(0x00,sbuf[ptr+i-scanlength],0x00));
                for (uint i = bpp; i <(scanlength - 1); i++)
                    sbuf[ptr+i]=(byte)(sbuf[ptr+i] + paethpredictor(sbuf[ptr+i-bpp],sbuf[ptr+i-scanlength],sbuf[ptr+i-scanlength-bpp]));
            }
            ptr +=scanlength-1;
        }
        private static byte paethpredictor(byte a , byte b , byte c)
        {
            int p  =  a + b -c;
            int pa = ((p-a)>0)?p-a:a-p;
            int pb = ((p-b)>0)?p-b:b-p;
            int pc = ((p-c)>0)?p-c:c-p;
            if ((pa <= pb) && (pa <= pc))return a;
            else if (pb <= pc) return b;
            else return c;
        }
    } // end of Filter class
    class Display
    {
        private static byte[] buffer;
        private static ushort[] linebuffer; // reference to the linebuffer of Singularity namespace
        private static COLOR_TYPE ctype;
        private static IoMemory screenBuffer;
        private static  S3_VIDEO_MODE ActiveMode;
        private static cIHDR ihdr;
        public static void display(byte[] buf,Hashtable ht,ushort[] lineBuffer,IoMemory sbuf, S3_VIDEO_MODE actmode)
        {
            buffer=buf;
            screenBuffer = sbuf;
            ActiveMode = actmode;
            linebuffer = lineBuffer;
            ihdr = (cIHDR)ht["IHDR"];
            DebugStub.Print("ihdr :{0} ", __arglist(hdr.width));
            DebugStub.Print("ihdr :{0} ", __arglist(ihdr.color_type));
            DebugStub.Print("ihdr :{0} ", __arglist(COLOR_TYPE.RGBTRIPLATE));
            switch (ihdr.color_type) {
                case COLOR_TYPE.GRAY_SCALE:showGRAY_SCALE();break;
                case COLOR_TYPE.RGBTRIPLATE:showRGBTRIPLATE();break;
                case COLOR_TYPE.PALETTEINDEX:showPALETTEINDEX();break;
                case COLOR_TYPE.GRAYSCALE_ALPHA: showGRAYSCALE_ALPHA();break;
                case COLOR_TYPE.RGB_ALPHA:showRGBALPHA();break;
                default : throw new Exception("Invalid color type");
            }

        }
        private static void showGRAY_SCALE()
        {// fill in later
        showRGBALPHA();
        }

        static ushort abc(byte red,byte green,byte blue)
        {
            return (ushort)((((ushort)red >> 3) << 11) |
                (((ushort)green >> 2) << 5) |
                ((ushort)blue >> 3));
        }

        private static void showRGBTRIPLATE()
        {

            int pDst = (int)(/*(ihdr.y + ihdr.height - 1) * ActiveMode.ScreenStride + */ihdr.x * ActiveMode.BytesPerPixel);
            linebuffer = new ushort[ihdr.width];

            for (int j = 0; j < ihdr.height; j++) {
                for (int i = 0; i < ihdr.width; i++) {
                    linebuffer[i] = RGB.Compute16(buffer[i*ihdr.bpp+1+j*ihdr.scanlength],buffer[i*ihdr.bpp+2+j*ihdr.scanlength],buffer[i*ihdr.bpp+3+j*ihdr.scanlength]);
                   // DebugStub.Print("{0} ",
                    __arglist(abc(buffer[i*ihdr.bpp+1+j*ihdr.scanlength],buffer[i*ihdr.bpp+2+j*ihdr.scanlength],buffer[i*ihdr.bpp+3+j*ihdr.scanlength])));
                }
                //DebugStub.Print("\n");
                screenBuffer.Write16(pDst, linebuffer, 0,(int)ihdr.width);

                pDst += ActiveMode.ScreenStride;
            }
        }
        private static void showPALETTEINDEX()
        {

        }
        private static void showGRAYSCALE_ALPHA()
        {
        }
        private static void showRGBALPHA()
        {
            //PictureBox pictureBox = new PictureBox();
            int pDst = (int)(/*(ihdr.y + ihdr.height - 1) * ActiveMode.ScreenStride + */ihdr.x * ActiveMode.BytesPerPixel);

            for (int j = 0; j < ihdr.height; j++) {
                for (int i = 0; i < ihdr.width; i++) {
                    linebuffer[i] = RGB.Compute16(/*buffer[x*ihdr.bpp+4+y*ihdr.scanlength],*/buffer[i*ihdr.bpp+1+j*ihdr.scanlength],buffer[i*ihdr.bpp+2+j*ihdr.scanlength],buffer[i*ihdr.bpp+3+j*ihdr.scanlength]);
                }
                screenBuffer.Write16(pDst, linebuffer, 0,(int)ihdr.width);

                pDst += ActiveMode.ScreenStride;
            }


        }
    }

    class Png
    {
        public Hashtable chunk_list;
        public byte[] SIGNATURE=new byte[]{0x89,0x50,0x4e,0x47,0x0d,0x0a,0x1a,0x0a}; // signature of PNG files
        public byte[] buffer;
        public uint ptr;
        public TYPE tp;
        public byte bpp;
        public uint scanlength;
        public bool chksignature()
        {
            // chk if first 9 bytes of the file matches the signature
            // if yes then return true else return false
            for (int i = 0; i < 8; i++)
                if (buffer[ptr++]!=SIGNATURE[i]) return false;
            return true;

        }

    }

    public class Block
    {
        public enum BTYPE:byte{NO_COMPRESSION,FIXED_HUFFMAN,DYNAMIC_HUFFMAN};
        public bool last; // is this the last block: true: yes false : no
        public BTYPE Btype;
        public byte[] buf;
        public int count; // the length of uncompressed block
    }

    public class treenode
    {
        public uint len;
        public uint code;
        public uint val;
        public treenode(uint len)
        {
            this.len = len;
        }
    }
    public class searchtree
    {

        public Hashtable ht;
        public uint MAX_BITS = 0;
        public uint MIN_BITS = 0;

        public searchtree(treenode[] t,uint[] bt_count)
        {
            uint min = t[0].len;
            uint max = t[0].len;
            for (uint i = 0; i < t.Length; i++) {
                if (min == 0)
                    min = t[i].len;
                else if (t[i].len < min && t[i].len != 0)
                    min = t[i].len; // find minimum non zero
                if (t[i].len > max)
                    max = t[i].len; // find maximun
            }
            this.MAX_BITS = max;
            this.MIN_BITS = min;
            this.setsearchtree(t,0,(uint)t.Length,bt_count);
        }

        public searchtree  (treenode[] t,uint start_index,uint end_index)
        {
            uint min = t[start_index].len;
            uint max = t[start_index].len;

            for (uint i = start_index; i < end_index; i++) {
                if (min == 0)min = t[i].len;
                else if (t[i].len < min && t[i].len != 0)min = t[i].len; // find minimum non zero
                if (t[i].len > max)max = t[i].len; // find maximun
            }
            this.MAX_BITS = max;
            this.MIN_BITS = min;
            uint[] bt_count = new uint[MAX_BITS+1];

            for (uint n = start_index; n < end_index; n++) {
                bt_count[t[n].len]++; // no of codes having bit count as this

            }
            this.setsearchtree(t,start_index,end_index,bt_count);
        }

        void setsearchtree(treenode[] t,uint start_index,uint end_index,uint[] bt_count)
        {
            uint code=0;
            ht = new Hashtable();
            bt_count[0]=0;

            uint[] next_code= new uint[16];// no code larger than 16 bits
            for (uint bits = 1; bits <= MAX_BITS; bits++) {
                code =((code + bt_count[bits-1])<<1);
                next_code[bits]=code;
            }
            for (uint n = start_index; n < end_index; n++) {
                if (t[n].len != 0) {
                    t[n].code = next_code[t[n].len]++;
                    t[n].val=n-start_index;
                    ht.Add(t[n].code,t[n]);
                    //  DebugStub.Print("{0} {1} {2}", __arglist(t[n].code, t[n].len, n));
                }
            }
        }

        public uint decode(byte[] buf, ref ulong ptr)
        {
            // return the appropriate symbol after searching from the treenode
            // throw an exception if not found
            byte bits_read= (byte)MIN_BITS; // start from reading the min no of bits
            uint val = Compressed.readnbithuffmancode(buf, ref ptr, bits_read);

            while (bits_read < MAX_BITS) {
                if (ht.ContainsKey(val)) {
                    treenode obj = (treenode)ht[val];
                    if (obj.len == (uint)bits_read) {
                        return obj.val;
                    }
                }
                else {
                    val = (val << 1) | Compressed.readbit(buf, ref ptr);
                    bits_read++;
                }
            }
            DebugStub.Print("Huffman code at ptr :{0}", __arglist(ptr));
            throw new Exception("Invalid Huffman Code");
        }
    }

    public class Compressed
    {
        uint read_fixed_huffman_val(byte[] buf, ref ulong ptr)
        {
            uint val = readnbithuffmancode(buf, ref ptr, 7);
            if (val <= 0x17)  // chk for 7 bit code
                return (256+val);
            // else read one more bit
            val = (val << 1) | readbit(buf, ref ptr);  // valid for huffman code but not otherwise
            if (val >= 0x30 && val <= 0xBF) // chk for 8 bit code
                return (val-0x30);
            if (val >= 0xc0 && val <= 0xc7)
                return(280+val-0xc0);
            val = (val << 1) + readbit(buf, ref ptr);
            if (val >= 0x190 && val <= 0x1ff) // finally it must be a 9 bit code
                return (144+val-0x190);
            throw new Exception("invalid format of file");
        }

        uint read_length(byte[] buf, ref ulong ptr, uint val)
        {
            // since length is also written as huffman codes so same method will be valid but not valid generally
            if (val >= 257) {
                if (val <= 264) {   // chk for 0 extra bit length
                    return (3 + val-257);
                }
                else if (val <= 268) {  // chk for 1 extra bit length
                    return (11 + ((val-265)<<1) + readbit(buf, ref ptr));
                }
                else if (val <= 272) {  // chk for 2 extra bit length
                    return (19 + ((val-269)<<2) + readnbit(buf, ref ptr, 2));
                }
                else if (val <= 276) {  // chk for 3 extra bit length
                    return (35 + ((val-273)<<3) + readnbit(buf, ref ptr, 3));
                }
                else if (val <= 280) {  // chk for 4 extra bit length
                    return (67 + ((val-277)<<4) + readnbit(buf, ref ptr, 4));
                }
                else if (val <= 284) {  // chk for 5 extra bit length
                    return (131 + ((val-281)<<5) + readnbit(buf, ref ptr, 5));
                }
                else if (val == 285) {
                    return 258;
                }
            }
            throw new Exception("invalid format of file");
        }

        uint read_dist(byte[] buf, ref ulong ptr, uint val)
        {
            if (val <= 3) { // chk for 0 extra bit length
                return (val+1);
            }
            else if (val <= 5) { // chk for 1 extra bit length
                return (5 + ((val-4)<<1) + readbit(buf, ref ptr));
            }
            else if (val <= 7) { // chk for 2 extra bit length
                return (9 + ((val-6)<<2) +  readnbit(buf, ref ptr, 2));
            }
            else if (val <= 9) { // chk for 3 extra bit length
                return (17 + ((val-8)<<3) + readnbit(buf, ref ptr,3 ));
            }
            else if (val <= 11) {   // chk for 4 extra bit length
                return (33 + ((val-10)<<4) + readnbit(buf, ref ptr, 4));
            }
            else if (val <= 13) {   // chk for 5 extra bit length
                return (65 + ((val-12)<<5) + readnbit(buf, ref ptr, 5));
            }
            else if (val <= 15) {   // chk for 6 extra bit length
                return (129 + ((val-14)<<6) + readnbit(buf, ref ptr, 6));
            }
            else if (val <= 17) {   // chk for 7 extra bit length
                return (257 + ((val-16)<<7) + readnbit(buf, ref ptr, 7));
            }
            else if (val <= 19) {   // chk for 8 extra bit length
                return (513 + ((val-18)<<8) + readnbit(buf, ref ptr, 8));
            }
            else if (val <= 21) {   // chk for 9 extra bit length
                return (1025 + ((val-20)<<9) + readnbit(buf, ref ptr, 9));
            }
            else if (val <= 23) {   // chk for 10 extra bit length
                return (2049 + ((val-22)<<10) + readnbit(buf, ref ptr, 10));
            }
            else if (val <= 25) {   // chk for 11 extra bit length
                return (4097 + ((val-24)<<11) + readnbit(buf, ref ptr, 11));
            }
            else if (val <= 27) {   // chk for 12 extra bit length
                return (8193 + ((val-26)<<12) + readnbit(buf, ref ptr, 12));
            }
            else if (val <= 29) {   // chk for 13 extra bit length
                return (16385 + ((val-28)<<13) + readnbit(buf, ref ptr, 13));
            }
            else {
                DebugStub.Print(" ptr : {0} | val: {1}", __arglist(ptr, val));
                throw new Exception("invalid format of file");
            }
        }

        // function to read a bit from the buffer
        public static uint readbit(byte[] buf, ref ulong ptr)
        {
            uint b = ((((uint)buf[ptr/8]) >> (int)((ptr%8))) & (1));
            ptr++;
            return b;
        }

        // function to read n bits from the buffer
        public static uint readnbit(byte[] buf, ref ulong ptr, byte n)
        {
            uint val = 0;
            for (int i = 0; i < n; i++) {
                val |= readbit(buf, ref ptr) << i;
            }
            return val;
        }

        // function to read n bits from the buffer
        public static uint readnbithuffmancode(byte[] buf, ref ulong ptr, byte n)
        {
            uint val=0;
            for (; n > 0; n--) {
                val = ((val << 1) | readbit(buf, ref ptr));
            }
            return val;
        }

        public class circularbuffer
        {
            const uint SIZE = 32768;
            uint size;//current size
            byte[] buf ;
            uint ptr;// to start with
            public circularbuffer()
            {
                ptr=0;
                size=0;
                buf = new byte[SIZE];
                if (buf==null)throw new Exception("IoMemory can't be allocated");
            }
            public byte getindexeddata(uint relative_position)  // with respect to the current ptr:behind
            {
                if (relative_position>size) throw new Exception("out of bounds parameter");
                uint pos= (ptr + SIZE - relative_position)%SIZE;
                return buf[pos];
            }
            public void writeindexeddata(byte data)  // write can only take place at the current position
            {
                if (SIZE > size) size++;
                buf[ptr]= data;
                ptr = (ptr + 1)%SIZE;
            }

        }
        ArrayList BlkList;

        public Compressed(byte[] buf, ref ulong ptr)
        {
            // ptr th bit is being referred
            BlkList= new ArrayList();
            Block blk;
            circularbuffer circ_buf = new circularbuffer();
            uint length;
            MemoryStream ms;
            BinaryWriter bw;
            treenode[] code_length_length;
            treenode[] code_length;

            do
            {
                blk = new Block();
                ms = new MemoryStream();
                bw = new BinaryWriter(ms);
                blk.last = (readbit(buf, ref ptr) != 0);// for MSB ptr%8=0
                uint btype = readnbit(buf, ref ptr, 2);
                switch (btype)// note that readnbithuffmancode is not used
                {
                    case 0:blk.Btype = Block.BTYPE.NO_COMPRESSION;break;
                    case 1:blk.Btype = Block.BTYPE.FIXED_HUFFMAN;break;
                    case 2:blk.Btype = Block.BTYPE.DYNAMIC_HUFFMAN;break;
                    default:throw new Exception("Error in block type");
                }

                switch (blk.Btype) {
                    case Block.BTYPE.NO_COMPRESSION:
                        // skip remaining bits in currently partially read byte
                        ptr -= ptr%8;
                        ptr +=8;

                        uint LEN = readnbit(buf, ref ptr, 16);
                        // uint NLEN = readnbit(buf, ref ptr, 16);
                        // to be removed later
                        //                      if ((LEN + NLEN )!=0xffff) // chk if one's complement
                        //                          throw new Exception("Error in Block");
                        length = 0;
                        blk.buf = new byte[LEN];

                        for (length = 0; length < LEN; length++) // copy length character from the buffer
                        {
                            byte readbyte = buf[ptr/8];
                            ptr += 8;
                            bw.Write(readbyte);
                            circ_buf.writeindexeddata(readbyte);
                        }
                        blk.buf=ms.GetBuffer();
                        break;
                    case Block.BTYPE.FIXED_HUFFMAN:
                        uint val;

                        //                      BinaryReader br = new BinaryReader(ms);
                        while ((val = read_fixed_huffman_val(buf, ref ptr)) != 256) {
                            // loop till end character found
                            if (val < 256) // literal
                            {

                                bw.Write((byte)val);
                            }
                            else // length - distance pair
                            {
                                length = read_length(buf, ref ptr,val);
                                val = readnbit(buf, ref ptr, 5);
                                uint distance = read_dist(buf, ref ptr,val);
                                for (uint i = 0; i < length; i++) // copy length character from the buffer
                                {

                                    byte readbyte = circ_buf.getindexeddata(distance);
                                    bw.Write(readbyte);
                                    circ_buf.writeindexeddata(readbyte);

                                }
                            }
                        }
                        // transfer buffer to blk.buf
                        blk.buf=ms.GetBuffer();
                        break;

                    case Block.BTYPE.DYNAMIC_HUFFMAN:
                        uint chkcount =0;
                        int[] border = new int[] {    // Order of the bit length code lengths   
                                                     16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15};

                    {
                        uint HLIT = (257 + readnbit(buf, ref ptr, 5));
                        uint HDIST = (1 + readnbit(buf, ref ptr, 5));
                        uint HCLEN = (4 + readnbit(buf, ref ptr, 4));

                        searchtree code_length_length_tree;
                        // block 1 :to allocate code_length_length
                        int i;
                        code_length_length = new treenode[19];

                        uint[] bt_count = new uint[8]{0,0,0,0,0,0,0,0};
                        for (i = 0; i < HCLEN; i++) {

                            code_length_length[border[i]] = new treenode(readnbit(buf, ref ptr, 3));
                            bt_count[code_length_length[border[i]].len ]++;

                        }
                        for (; i < 19; i++) {
                            code_length_length[border[i]] =  new treenode(0);
                            bt_count[code_length_length[border[i]].len ]++;
                        }
                       code_length_length_tree = new searchtree(code_length_length,bt_count);
                        // block 1 ends

                        code_length = new treenode[HLIT + HDIST];// storing the huffman code length for the literals and code length

                        searchtree literal_length_tree;
                        searchtree distance_tree;
                        for (i = 0; i <(HLIT + HDIST);) {

                            uint bt = code_length_length_tree.decode(buf, ref ptr);
                            if (bt < 16) {
                                code_length[i] = new treenode(bt);i++;
                            }
                            else if (bt == 16) {
                                ushort cnt = (ushort)(readnbit(buf, ref ptr, 2) + 3);
                                for (; cnt > 0; cnt--,i++)
                                    code_length[i] = new treenode(code_length[i-1].len);
                            }
                            else if (bt == 17) {
                                ushort cnt =(ushort)(readnbit(buf, ref ptr, 3) + 3);
                                for (; cnt > 0; cnt--,i++)
                                    code_length[i] = new treenode(0);
                            }
                            else if (bt == 18) {
                                ushort cnt = (ushort)(readnbit(buf, ref ptr, 7) + 11);
                                for (; cnt > 0; cnt--,i++)
                                    code_length[i] =new treenode(0);
                            }
                        }
                        //call function and make two huffman treenode
                        //then use these trees to form original huffman codes and hence decompress
                        //  DebugStub.Print("length literal treenode");
                        literal_length_tree = new searchtree(code_length,0,HLIT);
                        //  DebugStub.Print("distance treenode");
                        distance_tree = new searchtree(code_length,HLIT,(uint)HLIT + HDIST);
                        // both trees made

                        //we don't need code_length array now
                        code_length = null;
                        // make dynamic treenode now

                    {   // block 2



                        val = literal_length_tree.decode(buf, ref ptr);

//                        DebugStub.Break();
                        while (val != 256) {
                            // loop till end character found
                            if (val < 256) // literal
                            {
                                bw.Write((byte)val);
                               // DebugStub.Print("{0:x},{1:x} ", __arglist(val,br.Read()));
                                circ_buf.writeindexeddata((byte)val);
                                chkcount++;
                            }
                            else // length - distance pair
                            {

                                length = read_length(buf, ref ptr,val);
                                val = distance_tree.decode(buf, ref ptr);
                                uint distance = read_dist(buf, ref ptr,val);
                                for (i = 0; i < length; i++) // copy length character from the buffer
                                {
                                    byte readbyte = circ_buf.getindexeddata(distance);
                                    bw.Write(readbyte);
                                   // DebugStub.Print("{0:x},{1:x} ",__arglist(val,br.Read());
                                    circ_buf.writeindexeddata(readbyte);
                                    chkcount++;

                                }
                            }
                           // DebugStub.Print("The value of chkcount : {0}",__arglist(chkcount));
                            val = literal_length_tree.decode(buf, ref ptr);
                        }  // while ends
                        // transfer buffer to blk.buf
                        blk.buf=ms.GetBuffer();
//                        for (uint ij = 0;ij<chkcount;ij++)
//                             DebugStub.Print("{0:x} ",__arglist(blk.buf[ij]));
                        blk.count = (int)chkcount;

                        //                  Debug.Assert((chkcount==blk.buf.Length),"count does not match "+chkcount +" " +blk.buf.Length );
                    } // block 2 ends


                    } // main block ends
                        break; // case ends

                    default:throw new Exception("Invalid Block Type");
                }  // switch ends
                //DebugStub.Break();
                BlkList.Add(blk);
            } while (!blk.last); // do while ends
           // DebugStub.Break();
          //  DebugStub.Print("Exitting decompression block");
//            if (buf.Length!=(uint)((ptr+39)/8))  // 7 to round off to next byte and 32 for the ADLER 32 chksum!
//            {
//                //DebugStub.Print("buf length {0} \t ptr {1}",__arglist(buf.Length, ptr));
//                DebugStub.Print("buf length {0} \t ptr {1}",__arglist(buf.Length, ptr));
//                 DebugStub.Break();
//                //    throw new Exception("unprocessed bits at the end of the buffer");
//            }
        } // constructor ends

        public MemoryStream getdecompressedtext()
        {
            MemoryStream decompressedText = new MemoryStream();
            foreach (Block blk in BlkList) {
                decompressedText.Write(blk.buf,0,blk.count);
                DebugStub.Print("Checkpoint 1");
            }
            DebugStub.Print("Checkpoint 2");
            return decompressedText;

        }
    } // class ends

    public class zlib
    {

        public static MemoryStream UnCompress(byte[] buf)
        {
            // extract information from the buf: for zlib header format
            ulong ptr = 0;
            // CINFO
            Compressed.readnbit(buf, ref ptr, 4);// TODO: correct the order; CINFO is not the first one
            // CM
            Compressed.readnbit(buf, ref ptr, 4);
            // FLEVEL
            Compressed.readnbit(buf, ref ptr, 2);
            // FDICT
            Compressed.readbit(buf, ref ptr);
            // FCHECK
            Compressed.readnbit(buf, ref ptr, 5);
            Compressed obj = new Compressed(buf, ref ptr);

            // ADLER32
            Compressed.readnbit(buf, ref ptr, 32);
            return obj.getdecompressedtext();
            //if (ADLER32!=getADLER32(buf))throw new Exception("Buffer Corrupted");
        }
        public static uint getADLER32(byte[] buf)
        {
            return 0;
        }

    }

    class Decompress
    {
        public static void decompress(MemoryStream compressedText,out byte[] dstbuf)
        {
            MemoryStream deCompressedText = new MemoryStream();
            compressedText.Seek( 0, SeekOrigin.Begin);
            deCompressedText = zlib.UnCompress( compressedText.GetBuffer() );
            DebugStub.Print("Checkpoint 3");
            deCompressedText.Seek( 0, SeekOrigin.Begin );
            dstbuf=deCompressedText.GetBuffer();
            compressedText = null;
            DebugStub.Print("Checkpoint 2");
        }
    }
