/*
        Find a vector from a block to the most similar portion of an image
            */
        public Point getVector(double[,] image, Block comparison, int xPos, int yPos, int range) {
            xPos = xPos + 4;
            yPos = yPos + 4;
            double minError = 99999;
            int minXPos=0;
            int minYPos=0;
            double currentError;

            for (int y= yPos - range; y<yPos+ range; y++) {
                for (int x = xPos - range; x < xPos + range; x++) {  
                    currentError = getErrorForPosition(image, comparison, x, y, 1);
                    if (currentError < minError) {
                        minError = currentError;
                        minXPos = x;
                        minYPos = y;
                    }
                }
            }
            //Debug.WriteLine(minError);

            Point vector = new Point(minXPos-xPos, minYPos-yPos);            

            return vector;
        }
        /*
        Returns the amount of error compared to a given block at the current position
            */
        public double getErrorForPosition(double[,] image, Block comparison, int xPos, int yPos, int channel) {
            xPos -= 4;
            yPos -= 4;

            double error = 0;
            double pixelValue;
            double currentError;
            Color color = new Color();
            switch (channel)
            {
                case 1:
                    YerrorBlock = new Block();
                    break;
                case 2:
                    CberrorBlock = new Block();
                    break;
                case 3:
                    CrerrorBlock = new Block();
                    break;
            }
            
            for (int y=yPos; y<yPos+8; y++) {
                for (int x=xPos; x<xPos+8; x++) {
                    //if out of bounds, treat as a value of zero
                    if (y >= image.GetLength(1) || x >= image.GetLength(0)||x<0||y<0) {
                        switch (channel)
                        {
                            case 1:
                                YerrorBlock.set(x - xPos, y - yPos, Math.Round(0 - comparison.get(x - xPos, y - yPos)));
                                break;
                            case 2:
                                CberrorBlock.set(x - xPos, y - yPos, Math.Round((0 - comparison.get(x - xPos, y - yPos))));
                                break;
                            case 3:
                                CrerrorBlock.set(x - xPos, y - yPos, Math.Round((0 - comparison.get(x - xPos, y - yPos))));
                                break;
                        }
                        
                        error += Math.Abs(0 - comparison.get(x - xPos, y - yPos));
                        continue;
                    }
                    currentError = image[x,y]-comparison.get(x-xPos, y-yPos);
                    error += Math.Abs(currentError);
                    switch (channel)
                    {
                        case 1:
                            YerrorBlock.set(x - xPos, y - yPos, Math.Round(currentError));
                            break;
                        case 2:
                            CberrorBlock.set(x - xPos, y - yPos, Math.Round(currentError));
                            break;
                        case 3:
                            CrerrorBlock.set(x - xPos, y - yPos, Math.Round(currentError));
                            break;
                    }                    
                }
            }
                       
            if (xPos == 3 && yPos == 0)
            {
                //Debug.WriteLine("----------------------------ErrorBlock-----------------------------------");
                for (int q = 0; q < 8; q++)
                {
                    for (int w = 0; w < 8; w++)
                    {
                        //Debug.Write(errorBlock.get(q, w) + ",");
                        //Debug.Write(comparison.get(q, w) + ",");
                    }
                   // Debug.WriteLine("");
                }
               // Debug.WriteLine("----------------------------------------------------------------------------------------------");
            }

            error /= 64;

           // Debug.WriteLine("Total error = "+error);

            return error;
        }
        /*
        Returns the amount of error compared to a given block at the current position
            */
        public void getOriginalFromError(double[,] image, Block errorBlock, int xPos, int yPos, int channel)
        {
            xPos -= 4;
            yPos -= 4;

            double error = 0;
            double pixelValue;
            double currentError;
            Color color = new Color();
            switch (channel)
            {
                case 1:
                    YpostBlock = new Block();
                    break;
                case 2:
                    CbpostBlock = new Block();
                    break;
                case 3:
                    CrpostBlock = new Block();
                    break;
            }

            for (int y = yPos; y < yPos + 8; y++)
            {
                for (int x = xPos; x < xPos + 8; x++)
                {
                    //if out of bounds, treat as a value of zero
                    if (y >= image.GetLength(1) || x >= image.GetLength(0) || x < 0 || y < 0)
                    {
                        switch (channel)
                        {
                            case 1:
                                YpostBlock.set(x - xPos, y - yPos, Math.Round(Math.Abs(0 + errorBlock.get(x - xPos, y - yPos))));
                                break;
                            case 2:
                                CbpostBlock.set(x - xPos, y - yPos, Math.Round(Math.Abs(0 + errorBlock.get(x - xPos, y - yPos))));
                                break;
                            case 3:
                                CrpostBlock.set(x - xPos, y - yPos, Math.Round(Math.Abs(0 + errorBlock.get(x - xPos, y - yPos))));
                                break;
                        }                        
                        continue;
                    }
                    currentError = Math.Abs(image[x, y] + errorBlock.get(x - xPos, y - yPos));
                    switch (channel)
                    {
                        case 1:
                            YerrorBlock.set(x - xPos, y - yPos, Math.Round(currentError));
                            break;
                        case 2:
                            CbpostBlock.set(x - xPos, y - yPos, Math.Round(currentError));
                            break;
                        case 3:
                            CrpostBlock.set(x - xPos, y - yPos, Math.Round(currentError));
                            break;
                    }
                }
            }
        }
        /*
        Undo each of the compression steps
            */
        public void decompress(List<List<int>> YencodedList, List<List<int>> CbencodedList, List<List<int>> CrencodedList, int width, int height) {
            int horizontalBlocks = (int)Math.Ceiling((double)width / 8);//amount of full 8x8 blocks will fit horizontally
            int verticalBlocks = (int)Math.Ceiling((double)height / 8);//amount of full 8x8 blocks will fit vertically

            Block[,] YpostBlocks = new Block[horizontalBlocks, verticalBlocks];
            Block[,] CbpostBlocks = new Block[horizontalBlocks, verticalBlocks];
            Block[,] CrpostBlocks = new Block[horizontalBlocks, verticalBlocks];
            
            Block[,] YdctBlocks = new Block[horizontalBlocks, verticalBlocks];
            Block[,] CbdctBlocks = new Block[horizontalBlocks, verticalBlocks];
            Block[,] CrdctBlocks = new Block[horizontalBlocks, verticalBlocks];

            int[] Yencoded,Cbencoded,Crencoded;

            for (int y = 0; y < verticalBlocks; y++)
            {
                for (int x = 0; x < horizontalBlocks; x++)
                {
                    YpostBlocks[x, y] = new Block();
                    CbpostBlocks[x, y] = new Block();
                    CrpostBlocks[x, y] = new Block();
                }
            }

            int[] Yzig,Cbzig,Crzig;

            for (int y = 0; y < verticalBlocks; y++)
            {
                for (int x = 0; x < horizontalBlocks; x++)
                {
                    Yencoded = convert2dListToArray(YencodedList, verticalBlocks * y + x);
                    Cbencoded = convert2dListToArray(CbencodedList, verticalBlocks * y + x);
                    Crencoded = convert2dListToArray(CrencodedList, verticalBlocks * y + x);

                    Yzig = undoRunlengthEncoding(Yencoded);
                    Cbzig = undoRunlengthEncoding(Cbencoded);
                    Crzig = undoRunlengthEncoding(Crencoded);


                    if (x == 0 && y == 0)
                    {
                        for (int i = 0; i < Yzig.GetLength(0); i++)
                        {
                            Debug.Write((Yzig[i]) + ",");
                            //Debug.Write((Cbzig[i]) + ",");
                        }

                        Debug.WriteLine("");
                        Debug.WriteLine("-------------------------------------");
                    }

                    YdctBlocks[x, y] = undoZigZag(Yzig);
                    CbdctBlocks[x, y] = undoZigZag(Cbzig);
                    CrdctBlocks[x, y] = undoZigZag(Crzig);


                    if (x == 0 && y == 0)
                    {
                        for (int q = 0; q < 8; q++)
                        {
                            for (int w = 0; w < 8; w++)
                            {
                                Debug.Write(YdctBlocks[x, y].get(q, w) + ",");
                                //Debug.Write(CbdctBlocks[x, y].get(q, w) + ",");
                            }
                            Debug.WriteLine("");
                        }
                    }

                }
            }



            for (int y = 0; y < verticalBlocks; y++)
            {
                for (int x = 0; x < horizontalBlocks; x++)
                {
                    YdctBlocks[x, y] = RemoveQuantization(YdctBlocks[x, y], luminance);
                    CbdctBlocks[x, y] = RemoveQuantization(CbdctBlocks[x, y], chrominance);
                    CrdctBlocks[x, y] = RemoveQuantization(CrdctBlocks[x, y], chrominance);

                    for (int v = 0; v < 8; v++)
                    {
                        for (int u = 0; u < 8; u++)
                        {
                            YpostBlocks[x, y].set(u, v, applyIDCTFormula(YdctBlocks[x, y], u, v));

                            //if (x % 2 == 0 && y % 2 == 0)
                            //{
                                CbpostBlocks[x , y ].set(u, v, applyIDCTFormula(CbdctBlocks[x, y], u, v));
                                CrpostBlocks[x , y ].set(u, v, applyIDCTFormula(CrdctBlocks[x , y], u, v));
                            //}
                        }
                    }
                }
            }

            this.Yblocks = YpostBlocks;
            this.Cbblocks = CbpostBlocks;
            this.Crblocks = CrpostBlocks;

            YImage = makeDoubleArrayFromBlocks(YpostBlocks, width, height);
            CbImage = makeDoubleArrayFromBlocks(CbpostBlocks, width, height);
            CrImage = makeDoubleArrayFromBlocks(CrpostBlocks, width, height);
        }
        /*
        compresses a pframe
            */
        public void compressPframe(Block[,] Yblocks, Block[,] Cbblocks, Block[,] Crblocks, Point[,] vectors, int width, int height) {
            int horizontalBlocks = Yblocks.GetLength(0);
            int verticalBlocks = Yblocks.GetLength(1);

            Block[,] YdctBlocks = new Block[horizontalBlocks,verticalBlocks];
            Block[,] CbdctBlocks = new Block[horizontalBlocks, verticalBlocks];
            Block[,] CrdctBlocks = new Block[horizontalBlocks, verticalBlocks];

            List<int> YSaveBuffer = new List<int>();
            List<int> CbSaveBuffer = new List<int>();
            List<int> CrSaveBuffer = new List<int>();

            for (int y = 0; y < verticalBlocks; y++)
            {
                for (int x = 0; x < horizontalBlocks; x++)
                {
                    //Debug.WriteLine("-----------Starting------------");
                    YdctBlocks[x, y] = new Block();
                    CbdctBlocks[x, y] = new Block();
                    CrdctBlocks[x, y] = new Block();

                    for (int v = 0; v < 8; v++)
                    {
                        for (int u = 0; u < 8; u++)
                        {

                            YdctBlocks[x, y].set(u, v, applyDCTFormula(Yblocks[x, y], u, v));
                            CbdctBlocks[x, y].set(u, v, applyDCTFormula(Cbblocks[x, y], u, v));
                            CrdctBlocks[x, y].set(u, v, applyDCTFormula(Crblocks[x, y], u, v));

                        }
                        //Debug.WriteLine("");
                    }

                    YdctBlocks[x, y] = applyQuantization(YdctBlocks[x, y], luminance);
                    CbdctBlocks[x, y] = applyQuantization(CbdctBlocks[x, y], chrominance);
                    CrdctBlocks[x, y] = applyQuantization(CrdctBlocks[x, y], chrominance);


                    if (x == 0 && y == 0)
                    {
                        for (int q = 0; q < 8; q++)
                        {
                            for (int w = 0; w < 8; w++)
                            {
                                Debug.Write(YdctBlocks[x, y].get(q, w) + ",");
                                //Debug.Write(CbdctBlocks[x, y].get(q, w) + ",");
                            }
                            Debug.WriteLine("");
                        }

                        Debug.WriteLine("-------------------------------------");
                    }
                    int[] Yzig = applyZigZag(YdctBlocks[x, y]);


                    int[] Yencoded = runLengthEncode(Yzig);
                    YSaveBuffer.Add(Yencoded.Length);
                    for (int i = 0; i < Yencoded.Length; i++)
                    {
                        YSaveBuffer.Add(Yencoded[i]);
                    }

                    int[] Cbzig = new int[128];
                    int[] Crzig = new int[128];

                    int[] Cbencoded = new int[128];
                    int[] Crencoded = new int[128];


                    Cbzig = applyZigZag(CbdctBlocks[x, y]);
                    Crzig = applyZigZag(CrdctBlocks[x, y]);

                    Cbencoded = runLengthEncode(Cbzig);
                    Crencoded = runLengthEncode(Crzig);

                    //first save the length of the run length, so we know how far to read later
                    CbSaveBuffer.Add(Cbencoded.Length);
                    for (int i = 0; i < Cbencoded.Length; i++)
                    {
                        CbSaveBuffer.Add(Cbencoded[i]);
                    }
                    CrSaveBuffer.Add(Crencoded.Length);
                    for (int i = 0; i < Crencoded.Length; i++)
                    {
                        CrSaveBuffer.Add(Crencoded[i]);
                    }


                    if (x == 0 && y == 0)
                    {
                        for (int i = 0; i < Yzig.GetLength(0); i++)
                        //for (int i = 0; i < Cbzig.GetLength(0); i++)
                        {
                            Debug.Write((Yzig[i]) + ",");
                            //Debug.Write((Cbzig[i]) + ",");
                        }

                        Debug.WriteLine("");
                        Debug.WriteLine("-------------------------------------");



                        for (int i = 0; i < Yencoded.GetLength(0); i++)
                        //for (int i = 0; i < Cbencoded.GetLength(0); i++)
                        {
                            Debug.Write((Yencoded[i]) + ",");
                            //Debug.Write((Cbencoded[i]) + ",");
                        }

                        Debug.WriteLine("");
                        Debug.WriteLine("-------------------------------------");
                    }

                    //saveFile();
                    //--------------------------------------UNDO COMPRESSION---------------------------------------------------------------------




                }//x
            }//y            

            int position = 0;
            int totalCount = YSaveBuffer.Count + CbSaveBuffer.Count + CrSaveBuffer.Count;
            int[] toSave = new int[totalCount + 2];
            for (int i = 0; i < YSaveBuffer.Count; i++)
            {
                toSave[position++] = YSaveBuffer[i];
            }
            toSave[position++] = 127;
            for (int i = 0; i < CbSaveBuffer.Count; i++)
            {
                toSave[position++] = CbSaveBuffer[i];
            }
            toSave[position++] = 127;
            for (int i = 0; i < CrSaveBuffer.Count; i++)
            {
                toSave[position++] = CrSaveBuffer[i];
            }

            FileFunctions.saveCompressed(toSave, width, height);
            decodeSavePFrameArray(toSave, width, height);
            //decompress(Yencoded, Cbencoded, Crencoded, width, height);
        }
        public void runDCT() {
            int horizontalBlocks = (int)Math.Ceiling((double)Y.GetLength(0) / 8);//amount of full 8x8 blocks will fit horizontally
            int verticalBlocks = (int)Math.Ceiling((double)Y.GetLength(1) / 8);//amount of full 8x8 blocks will fit vertically

            Block[,] Yblocks = new Block[horizontalBlocks, verticalBlocks];
            Block[,] YdctBlocks = new Block[horizontalBlocks, verticalBlocks];
            

            Block[,] Cbblocks = new Block[horizontalBlocks, verticalBlocks];
            Block[,] CbdctBlocks = new Block[horizontalBlocks, verticalBlocks];
            

            Block[,] Crblocks = new Block[horizontalBlocks, verticalBlocks];
            Block[,] CrdctBlocks = new Block[horizontalBlocks, verticalBlocks];
            

            List<int> YSaveBuffer = new List<int>();
            List<int> CbSaveBuffer = new List<int>();
            List<int> CrSaveBuffer = new List<int>();
            int toSaveBufferPos = 0;

            for (int y=0; y<verticalBlocks; y++) {
                for (int x = 0; x < horizontalBlocks; x++)
                {
                    Yblocks[x, y] = generateBlock(Y, x * 8, y * 8);//which block, multiplied by block offset (8) 
                    YdctBlocks[x, y] = new Block();

                    Cbblocks[x, y] = generateBlock(Cb, x * 8, y * 8);//which block, multiplied by block offset (8) 
                    CbdctBlocks[x,y] = new Block();

                    Crblocks[x, y] = generateBlock(Cr, x * 8, y * 8);//which block, multiplied by block offset (8) 
                    CrdctBlocks[x, y] = new Block();

                }
            }
            
            for (int y = 0; y < verticalBlocks; y++)
            {
                for (int x = 0; x < horizontalBlocks; x++)
                {
                    //Debug.WriteLine("-----------Starting------------");
                    for (int v = 0; v < 8; v++)
                    {
                        for (int u = 0; u < 8; u++)
                        {

                            YdctBlocks[x, y].set(u, v, applyDCTFormula(Yblocks[x, y], u, v));
                            CbdctBlocks[x, y].set(u, v, applyDCTFormula(Cbblocks[x, y], u, v));
                            CrdctBlocks[x, y].set(u, v, applyDCTFormula(Crblocks[x, y], u, v));
                            
                        }
                        //Debug.WriteLine("");
                    }

                    YdctBlocks[x, y] = applyQuantization(YdctBlocks[x, y], luminance);                    
                    CbdctBlocks[x, y] = applyQuantization(CbdctBlocks[x, y], chrominance);
                    CrdctBlocks[x, y] = applyQuantization(CrdctBlocks[x, y], chrominance);
                    

                    if (x == 0 && y == 0)
                    {
                        for (int q = 0; q < 8; q++)
                        {
                            for (int w = 0; w < 8; w++)
                            {
                                Debug.Write(YdctBlocks[x, y].get(q, w) + ",");
                                //Debug.Write(CbdctBlocks[x, y].get(q, w) + ",");
                            }
                            Debug.WriteLine("");
                        }

                        Debug.WriteLine("-------------------------------------");
                    }
                    int[] Yzig = applyZigZag(YdctBlocks[x, y]);


                    int[] Yencoded = runLengthEncode(Yzig);
                    YSaveBuffer.Add(Yencoded.Length);
                    for (int i = 0; i < Yencoded.Length; i++)
                    {
                        YSaveBuffer.Add(Yencoded[i]);
                    }

                    int[] Cbzig = new int[128];
                    int[] Crzig = new int[128];

                    int[] Cbencoded = new int[128];
                    int[] Crencoded = new int[128];

                    
                        Cbzig = applyZigZag(CbdctBlocks[x, y]);
                        Crzig = applyZigZag(CrdctBlocks[x, y]);

                        Cbencoded = runLengthEncode(Cbzig);
                        Crencoded = runLengthEncode(Crzig);

                        //first save the length of the run length, so we know how far to read later
                        CbSaveBuffer.Add(Cbencoded.Length);
                        for (int i = 0; i < Cbencoded.Length; i++)
                        {
                            CbSaveBuffer.Add(Cbencoded[i]);
                        }
                    CrSaveBuffer.Add(Crencoded.Length);
                        for (int i = 0; i < Crencoded.Length; i++)
                        {
                            CrSaveBuffer.Add(Crencoded[i]);
                        }


                    if (x == 0 && y == 0)
                    {
                        for (int i = 0; i < Yzig.GetLength(0); i++)
                        //for (int i = 0; i < Cbzig.GetLength(0); i++)
                        {
                            Debug.Write((Yzig[i]) + ",");
                            //Debug.Write((Cbzig[i]) + ",");
                        }

                        Debug.WriteLine("");
                        Debug.WriteLine("-------------------------------------");
                    
                                        
                    
                        for (int i = 0; i < Yencoded.GetLength(0); i++)
                        //for (int i = 0; i < Cbencoded.GetLength(0); i++)
                        {
                            Debug.Write((Yencoded[i]) + ",");
                            //Debug.Write((Cbencoded[i]) + ",");
                        }

                        Debug.WriteLine("");
                        Debug.WriteLine("-------------------------------------");
                    }                    

                    //saveFile();
                    //--------------------------------------UNDO COMPRESSION---------------------------------------------------------------------

                    
                    
                
                }//x
            }//y

            Debug.WriteLine("File length = "+toSaveBufferPos);

            int position = 0;
            int totalCount = YSaveBuffer.Count + CbSaveBuffer.Count + CrSaveBuffer.Count;
            int[] toSave = new int[totalCount+2];
            for(int i=0; i< YSaveBuffer.Count; i++){
                toSave[position++] = YSaveBuffer[i];
            }
            toSave[position++] = 127;
            for (int i = 0; i < CbSaveBuffer.Count; i++)
            {
                toSave[position++] = CbSaveBuffer[i];
            }
            toSave[position++] = 127;
            for (int i = 0; i < CrSaveBuffer.Count; i++)
            {
                toSave[position++] = CrSaveBuffer[i];
            }

            FileFunctions.saveCompressed(toSave, Y.GetLength(0), Y.GetLength(1));
            byte[] savedData = FileFunctions.openCompressed("TestFile.cmpr");
            decodeSaveArray(savedData);            
        }
        /*
        undoes zig zag on an int array, and outputs the original 8x8 block
            */
        public Block undoZigZag(int[] array) {
            Block block = new Block();

            int arrayPos = 0;
            int yMove = -1;
            int xMove = 1;
            int x = 0;
            int y = 0;

            block.set(x, y, array[arrayPos++]);

            for (int level = 0; level < 8; level++)
            {

                for (int depth = 0; depth < level; depth++)
                {
                    x += xMove;
                    y += yMove;

                    block.set(x, y, array[arrayPos++]);
                }

                if (level == 7) break;

                if (xMove < 0) y++; else x++;

                block.set(x, y, array[arrayPos++]);

                yMove *= -1;
                xMove *= -1;
            }

            yMove *= -1;
            xMove *= -1;

            if (xMove < 0) y++; else x++;

            block.set(x, y, array[arrayPos++]);

            for (int level = 6; level > 0; level--)
            {

                for (int depth = level; depth > 0; depth--)
                {
                    x += xMove;
                    y += yMove;

                    block.set(x, y, array[arrayPos++]);
                }

                if (xMove < 0) x++; else y++;

                block.set(x, y, array[arrayPos++]);

                yMove *= -1;
                xMove *= -1;
            }
            

            return block;
        }
        /*
        Multiplies the values of a quantized block by a quantization table to undo quantization. returns the outcome
            */
        public Block RemoveQuantization(Block block, int[,] table)
        {
            Block quantizedBlock = new Block();
            for (int y = 0; y < 8; y++)
            {
                for (int x = 0; x < 8; x++)
                {
                    quantizedBlock.set(x, y, block.get(x, y) * table[x, y]);
                }
            }

            return quantizedBlock;
        }
        /*
        converts a block into a 1 dimensional array
            */
        public int[] applyZigZag(Block block) {
            int[] zigzag = new int[64];
            int arrayPos =0;
            int yMove = -1;
            int xMove = 1;
            int x = 0;
            int y = 0;

            zigzag[arrayPos++] = (int)block.get(x, y);

            for (int level = 0; level < 8; level++)
            {

                for (int depth = 0; depth < level; depth++)
                {
                    x += xMove;
                    y += yMove;
                    
                    zigzag[arrayPos++] = (int)block.get(x,y);
                }

                if (level == 7) break;

                if (xMove < 0) y++; else x++;

                zigzag[arrayPos++] = (int)block.get(x, y);

                yMove *= -1;
                xMove *= -1;
            }

            yMove *= -1;
            xMove *= -1;

            if (xMove < 0) y++; else x++;

            zigzag[arrayPos++] = (int)block.get(x, y);

            for (int level = 6; level > 0; level--)
            {

                for (int depth = level; depth > 0; depth--)
                {
                    x += xMove;
                    y += yMove;

                    zigzag[arrayPos++] = (int)block.get(x, y);
                }

                if (xMove < 0) x++; else y++;

                zigzag[arrayPos++] = (int)block.get(x, y);

                yMove *= -1;
                xMove *= -1;
            }

            return zigzag;
        }
        /*
        Creates a bitmap from an array of 8*8 blocks.
            */
        public Bitmap createBitmapFromBlocks(Block[,] blocks, int imageWidth, int imageHeight) {
            Bitmap image = new Bitmap(imageWidth, imageHeight);
            int blocksVertical = blocks.GetLength(0);
            int blocksHorizontal  = blocks.GetLength(1);

            for (int y=0; y<blocksHorizontal; y++) {
                for (int x=0; x<blocksVertical; x++) {

                    for (int blockY=0; blockY<8; blockY++) {
                        for (int blockX=0; blockX<8; blockX++) {
                            if ( y * 8 + blockY >= imageHeight) continue;
                            if (x * 8 + blockX >= imageWidth) continue;
                            image.SetPixel(x*8+blockX, y*8+blockY, Color.FromArgb((int)blocks[x,y].get(blockX,blockY), (int)blocks[x, y].get(blockX, blockY), (int)blocks[x, y].get(blockX, blockY)));
                        }
                    }

                }
            }

            return image;
        }
        /*
        Divides the values of the block by a passed quantization table, and returns the outcome
            */
        public Block applyQuantization(Block block, int[,] table) {
            Block quantizedBlock = new Block();
            for (int y=0; y<8; y++) {
                for (int x=0; x<8; x++) {
                    quantizedBlock.set(x,y,Math.Round((double)block.get(x, y) / table[x, y]));
                }
            }

            return quantizedBlock;
        }
        /*
        Applies inverse DCT formula and returns the image pixel
            */
        public double applyIDCTFormula(Block input, double i, double j) {
            double sum = 0, firstCos, secondCos;

            for (double u=0; u<8; u++) {
                for (double v=0; v<8; v++) {
                    firstCos = Math.Cos((2*i+1)*u*Math.PI/16);
                    secondCos = Math.Cos((2*j+1)*v*Math.PI/16);
                    sum += c(u) * c(v) /4 * firstCos * secondCos*input.get((int)u,(int)v);
                }
            }

            return sum;
        }
        /*
        Applies DCT formula to a block, and returns the post DCT pixel
            */
        public double applyDCTFormula(Block input, double u, double v) {
            double sum=0, firstCos, secondCos;
            
            for (double i=0; i<8; i++) {
                for (double j=0; j<8; j++) {
                    firstCos = Math.Cos((2*i+1)*u*Math.PI/16);
                    secondCos = Math.Cos((2 * j + 1) * v * Math.PI / 16);
                    sum += (firstCos * secondCos * input.get((int)i, (int)j));
                    //if (i < 4 && j < 4) sum += firstCos * secondCos * input.get((int)i, (int)j);
                }
            }

            sum *= c(u) * c(v) / 4;

            return sum;
        }
        /*
        Puts an array of blocks back together as a single double array, ready for conversion to image
            */
        public double[,] makeDoubleArrayFromBlocks(Block[,] blocks, int width, int height) {
            double[,] image = new double[width, height];

            for (int y=0; y< height; y++) {
                for (int x=0; x< width; x++) {
                    int XblockPosition = x / 8;
                    int YblockPosition = y / 8;
                    int XinsidePosition = x-(XblockPosition * 8);
                    int YinsidePosition = y - (YblockPosition * 8);

                    image[x, y] = blocks[XblockPosition, YblockPosition].get(XinsidePosition, YinsidePosition);
                }
            }

            return image;
        }
 /*
 Generates an 8x8 block from a position 
     */
 public Block generateBlock(double[,] fullSize, int xPosition, int yPosition) {
     Block block = new Block();
     
     for (int y=yPosition; y<yPosition+8; y++) {
         for (int x=xPosition; x<xPosition+8; x++) {
             if (x < fullSize.GetLength(0) && y < fullSize.GetLength(1))
             {
                 block.set(x-xPosition,y-yPosition,fullSize[x,y]);
             }
             else {
                 block.set(x - xPosition, y - yPosition, 0.0);
             }
         }
     }
     return block;
 }
        /*Generate motion vectors between the two frames*/
        private void generateMotionVectorsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            int width = uncompressedSecondFrame.Width;
            int height = uncompressedSecondFrame.Height;

            double[,] Y = new double[width, height];
            double[,] Cb = new double[width / 2, height / 2];
            double[,] Cr = new double[width / 2, height / 2];

            //generate the Y, Cb, Cr values from the second frame
            generateYcbcrBitmap(uncompressedSecondFrame, ref Y, ref Cb, ref Cr);

            double[,] Y2 = new double[width, height];
            double[,] Cb2 = new double[width / 2, height / 2];
            double[,] Cr2 = new double[width / 2, height / 2];

            generateYcbcrBitmap(uncompressedBitmap, ref Y2, ref Cb2, ref Cr2);
            
            DCT dct = new DCT();
            Point[,] vectors = new Point[(int)Math.Ceiling((double)width/8), (int)Math.Ceiling((double)height / 8)];
            Block[,] YerrorBlocks = new Block[(int)Math.Ceiling((double)width / 8), (int)Math.Ceiling((double)height / 8)];
            Block[,] CberrorBlocks = new Block[(int)Math.Ceiling((double)width / 8), (int)Math.Ceiling((double)height / 8)];
            Block[,] CrerrorBlocks = new Block[(int)Math.Ceiling((double)width / 8), (int)Math.Ceiling((double)height / 8)];
            VideoCompression vidcom = new VideoCompression();

            for (int y=0; y<height; y+=8) {
                for (int x = 0; x < width; x += 8) {
                    Block Yblock = dct.generateBlock(Y, x, y);
                    Block Cbblock = dct.generateBlock(Cb, x, y);
                    Block Crblock = dct.generateBlock(Cr, x, y);

                    vectors[x/8,y/8] = vidcom.getVector(Y2, Yblock, x, y, 15);
                    vidcom.getErrorForPosition(Cb2, Cbblock, vectors[x / 8, y / 8].X, vectors[x / 8, y / 8].Y, 2);
                    vidcom.getErrorForPosition(Cr2, Crblock, vectors[x / 8, y / 8].X, vectors[x / 8, y / 8].Y, 3);
                    YerrorBlocks[x/8,y/8] = vidcom.getCurrentYErrorBlock();
                    CberrorBlocks[x / 8, y / 8] = vidcom.getCurrentCbErrorBlock();
                    CrerrorBlocks[x / 8, y / 8] = vidcom.getCurrentCrErrorBlock();

                    Debug.Write("(" + vectors[x/8,y/8].X + "," + vectors[x/8,y/8].Y+"),");
                }
                Debug.WriteLine("");
            }
            

            dct.compressPframe(YerrorBlocks, CberrorBlocks, CrerrorBlocks, vectors, width, height);


            Block[,] finalY = new Block[dct.Yblocks.GetLength(0), dct.Yblocks.GetLength(1)];
            Block[,] finalCb = new Block[dct.Yblocks.GetLength(0), dct.Yblocks.GetLength(1)];
            Block[,] finalCr = new Block[dct.Yblocks.GetLength(0), dct.Yblocks.GetLength(1)];

            for (int y=0; y<dct.Yblocks.GetLength(0); y++) {
                for (int x=0; x<dct.Yblocks.GetLength(1); x++) {
                    vidcom.getOriginalFromError(Y2, dct.Yblocks[x,y], x*8+vectors[x,y].X, y * 8 + vectors[x,y].Y, 1);
                    finalY[x, y] = vidcom.YpostBlock;
                    vidcom.getOriginalFromError(Cb2, dct.Cbblocks[x, y], x * 8 + vectors[x, y].X, y * 8 + vectors[x, y].Y, 2);
                    finalCb[x, y] = vidcom.CbpostBlock;
                    vidcom.getOriginalFromError(Cr2, dct.Crblocks[x, y], x * 8 + vectors[x, y].X, y * 8 + vectors[x, y].Y, 3);
                    finalCr[x, y] = vidcom.CrpostBlock;
                }
            }

            double[,] finalYimage = dct.makeDoubleArrayFromBlocks(finalY, width, height);
            double[,] finalCbimage = dct.makeDoubleArrayFromBlocks(finalCb, width, height);
            double[,] finalCrimage = dct.makeDoubleArrayFromBlocks(finalCr, width, height);

            Bitmap pframe = generateRgbBitmapFromYCbCr(finalYimage, finalCbimage, finalCrimage);
            pictureBox2.Image = pframe;
            pictureBox2.SizeMode = PictureBoxSizeMode.Zoom;
        }