#include "imgmanipbs.h"
#include <list>

ImgManipBS::ImgManipBS() {
    width = height = 0;
}

ImgManipBS::ImgManipBS(const char* fileName)
{

    mainImage = CImg<>(fileName);

    width = mainImage.width();
    height = mainImage.height();

    noOfWSquares = width / squareSize;
    noOfHSquares = height / squareSize;

    whiteMeanValues = new int*[noOfWSquares];
    blackMeanValues = new int*[noOfWSquares];

    for (int i = 0; i < noOfWSquares; ++i) {
      whiteMeanValues[i] = new int[noOfHSquares];
      blackMeanValues[i] = new int[noOfHSquares];
    }

    // Output image
    outImages[0] = CImg<>(width,height); // B&W Image with same size: White
    outImages[1] = CImg<>(width,height); // B&W Image with same size: Black

    printf("Original image size %dx%d\n",width,height);

    EvaluateImage();

    CreateGradiantImage();

    outImages[0].save_jpeg("ref_white.jpg");
    outImages[1].save_jpeg("ref_black.jpg");

    printf("Done.\n");
}

void ImgManipBS::CreateGradiantImage() {

    for(auto yy = 0; yy < noOfHSquares-1; yy++)
    {
        for (auto xx = 0; xx < noOfWSquares-1; xx++ )
        {
            int x = (xx*squareSize) + squareMargin;
            int y = (yy*squareSize) + squareMargin;

            DrawGradRect(0, x,y,whiteMeanValues[xx][yy],whiteMeanValues[xx+1][yy],whiteMeanValues[xx][yy+1],whiteMeanValues[xx+1][yy+1]);
            DrawGradRect(1, x,y,blackMeanValues[xx][yy],blackMeanValues[xx+1][yy],blackMeanValues[xx][yy+1],blackMeanValues[xx+1][yy+1]);

            if( yy == 0 )   // Only top row
            {
                DrawTopGradRect(0,x,0,whiteMeanValues[xx][yy],whiteMeanValues[xx+1][yy]);
                DrawTopGradRect(1,x,0,blackMeanValues[xx][yy],blackMeanValues[xx+1][yy]);
            }
        }
    }
}

void ImgManipBS::DrawGradRect(int imageNo,int x,int y, unsigned char p1, unsigned char p2, unsigned char p3, unsigned char p4)
{
    CImg<unsigned char> tempImage = CImg<>(squareSize,squareSize);  // Black and white image
    const unsigned char white[] = { 255,255,255 };
    const float opacity=1.0;

    float fp1 = (1.0 / 255) * p1;
    float fp2 = (1.0 / 255) * p2;
    float fp3 = (1.0 / 255) * p3;
    float fp4 = (1.0 / 255) * p4;

    tempImage.draw_triangle(0,0,0,squareSize,squareSize,squareSize,white,fp1,fp3,fp4,opacity);
    tempImage.draw_triangle(0,0,squareSize,0,squareSize,squareSize,white,fp1,fp2,fp4,opacity);

    outImages[imageNo].draw_image(x,y,tempImage);
}

void ImgManipBS::DrawTopGradRect(int imageNo,int x,int y, unsigned char p1, unsigned char p2)
{
    CImg<unsigned char> tempImage = CImg<>(squareSize,squareMargin);  // Black and white image
    const unsigned char white[] = { 255,255,255 };
    const float opacity=1.0;

    float fp1 = (1.0 / 255) * p1;
    float fp2 = (1.0 / 255) * p2;

    tempImage.draw_triangle(0,0,0,squareSize,squareSize,squareSize,white,fp1,fp1,fp2,opacity);
    tempImage.draw_triangle(0,0,squareSize,0,squareSize,squareSize,white,fp1,fp2,fp2,opacity);

    outImages[imageNo].draw_image(x,y,tempImage);
}

void ImgManipBS::EvaluateImage() {



    printf("Starting to evaluate image....\n\n");

    for(auto yy = 0; yy < noOfHSquares; yy++)
    {
        for (auto xx = 0; xx < noOfWSquares; xx++ )
        {
            int x = (xx*squareSize) + squareMargin;
            int y = (yy*squareSize) + squareMargin;

            double mW,mB;
            findNearValues(x,y,mW,mB);

            whiteMeanValues[xx][yy] = mW;
            blackMeanValues[xx][yy] = mB;

            //printf("%d,%d #%x%x%x ",x,y,(int)mW,(int)mW,(int)mW);*/

            //MarkPixelInOutImage(0, x,y);
        }
    }
    printf("\n");
}

void ImgManipBS::findNearValues(int x,int y, double &meanWhite, double &meanBlack) {

    std::list <double> brList;

    for(auto yy=y-(measureSquareSize/2);yy<=y+(measureSquareSize/2);yy++) {
        for(auto xx=x-(measureSquareSize/2);xx<=x+(measureSquareSize/2);xx++) {
            double br = GetBrightness(xx,yy);
            brList.push_back( br );
            //MarkPixelInOutImage(0, xx,yy);
        }
    }
    brList.sort();

    // Iterate through the list from the front (most black)
    std::list<double>::iterator it=brList.begin();
    double brSum=0;
    int cnt=0;
    for( auto i=0; i < noOfMeanValues && it != brList.end(); i++ ) {
        brSum += *it;
        cnt++;
        it++;
    }
    meanBlack = brSum / cnt;

    // Iterate through the list from the end (most white)
    it=brList.end();
    brSum=0;
    cnt=0;
    for( auto i=0; i < noOfMeanValues && it != brList.begin(); i++ ) {
        brSum += *it;
        cnt++;
        it--;
    }
    meanWhite = brSum / cnt;
}

void ImgManipBS::MarkPixelInOutImage(int imageNo,int x,int y) {
    outImages[imageNo](x,y,0,0) = 255;
    outImages[imageNo](x,y,0,1) = 255;
    //outImages[imageNo](x,y,0,2) = 255;
}

double ImgManipBS::GetBrightness(unsigned int x,unsigned int y) {
    return( (mainImage(x,y,0,0)+mainImage(x,y,0,1)+mainImage(x,y,0,2))/3 );
}
