#include "mycvdigitprocgeometric.h"
#include "mycvfloodfill.h"
#include <math.h>

#ifdef DEBUG_DIGIT
    #include <QDebug>
    #include <QString>
#endif

mycvDigitProcGeometric::mycvDigitProcGeometric()
{

}

mycvDigitProcGeometric::~mycvDigitProcGeometric()
{
    mPatternHoles.clear();
    mPatternsContours.clear();
}

void mycvDigitProcGeometric::loadPattern(Mat &aPattern)
{
    Mat imgGray;
    cv::cvtColor(aPattern, imgGray, CV_BGR2GRAY);
    /// Character segmentation
    threshold(imgGray, imgGray, 127, 255, CV_THRESH_BINARY | CV_THRESH_OTSU);

    vector<vector<Point>> contour;
    findContours(imgGray.clone(), contour, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);

    // Sort frames according to horizontal position
    while(contour.size() > 0)
    {
        // find minimum
        int min = numeric_limits<int>::max();
        int minIndx = 0;
        for(int i = 0 ; i < contour.size() ; i ++)
        {
            Rect boundR = boundingRect(contour[i]);
            if(min > boundR.x)
            {
                minIndx = i;
                min = boundR.x;
            }
        }
        // push min to out
        mPatternsContours.push_back(contour[minIndx]);
        // delete min from list
        contour.erase(contour.begin()+minIndx);
    }

    imshow("Patterns", imgGray);
    Mat imgOut = Mat::zeros(imgGray.size(), CV_8SC3);
    mPatternHoles.clear();
    mPatternConvexity.clear();
    for( int i = 0; i < mPatternsContours.size(); i++ )
    {
        std::ostringstream ostream;
        ostream << i;
        vector<Point> approx;
        approxPolyDP(
            cv::Mat(mPatternsContours[i]),
            approx,
            cv::arcLength(cv::Mat(mPatternsContours[i]), true) * 0.03,
            true
        );
        Rect rect;
        rect = boundingRect(approx);
        Point p(rect.x, rect.y);
        putText(imgOut, ostream.str(), p, FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0,0,255));
        drawContours(imgOut, mPatternsContours, i, Scalar(0,0,255));

        Mat imgCut;
        imgCut = imgGray(rect).clone();
//        ostringstream stream;
//        stream << i;
//        imshow(stream.str(), imgCut);
        int hole = holesNumber(imgCut);
        mPatternHoles.push_back(hole);
        mPatternConvexity.push_back(convexity(imgCut));
    }

    imshow("Pattern contours", imgOut);
}

float mycvDigitProcGeometric::match(Mat &aDigit)
{
    Mat imgGray = aDigit;
//    cv::cvtColor(aDigit, imgGray, CV_BGR2GRAY);
//    /// Character segmentation
//    threshold(imgGray, imgGray, 127, 255, CV_THRESH_BINARY | CV_THRESH_OTSU);
//    imshow("Tests", imgGray);
    vector<vector<Point>> contour;
    findContours(imgGray.clone(), contour, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);

    // biggist contour
    if(contour.size() > 1)
    {
        int volMax = 0;
        for(int i = 0 ; i < contour.size() ; i++)
        {
            int vol = contourArea(contour[i]);
            if(volMax < vol)volMax = vol;
        }
        float tolerance = 0.3;

        // Apply tolerance
        vector<vector<Point>> contour2;
        for(int i = 0 ; i < contour.size() ; i++)
        {
            int vol = contourArea(contour[i]);
            if(vol > tolerance*volMax)contour2.push_back(contour[i]);
        }

        if(contour2.size() > 1)
        {
        // Leave only 2 biggist
            contour.clear();

            while(contour.size() < 2)
            {
                int volMax2 = 0;
                int indxMax = 0;
                for(int i = 0 ; i < contour2.size() ; i++)
                {
                    int vol = contourArea(contour2[i]);
                    if(volMax2 < vol)
                    {
                       volMax2 = vol;
                       indxMax = i;
                    }
                }
                contour.push_back(contour2[indxMax]);
            }
        }
    }

    // number of holes
    Mat imgDigit;
    for(int i = 0 ; i < contour.size() ; i++)
        drawContours(imgDigit, contour, i, Scalar(255), -1);
    // Mask digit
    bitwise_and(imgDigit, imgGray, imgDigit);

    int numOfHoles = holesNumber(imgDigit);

    if(contour.size() == 1)
    {
        if(numOfHoles == 0)
        {


        }
        else if(numOfHoles == 1)
        {

        }
        else if(numOfHoles == 2)
        {

        }
    }
    else if(contour.size() == 2)
    {
        if(numOfHoles == 0)
        {

        }
        else if(numOfHoles == 1)
        {

        }
        else if(numOfHoles == 2)
        {

        }
    }

//    Mat imgOut = Mat::zeros(imgGray.size(), CV_8SC3);
////    cvtColor(imgGray, imgOut, CV_GRAY2BGR);
//    vector<Moments> mu(contour.size());
//    vector<double[7]> hu(contour.size());
//    double res[10];
//    for( int i = 0; i < contour.size(); i++ )
//    {
//        mu[i] = moments( contour[i], false );
//        HuMoments(mu[i], hu[i]);
//    #ifdef DEBUG_DIGIT
//        qDebug() << "Match " << i << ":";
//    #endif
//        for(int j = 0 ; j < mPatternsContours.size() ; j++)
//        {
////            res[j] = matchShapes(contour[i], mPatternsContours[j], CV_CONTOURS_MATCH_I3, 0);
//            res[j] = mPatternConvexity[j]-convexity(imgGray);
//            if(res[j] < 0)res[j] -= res[j]*2;
//        #ifdef DEBUG_DIGIT
//            qDebug() << j << ": " << QString::number(res[j]) << ", holes pattern = " << mPatternHoles[j] ;
//        #endif
//        }
//    #ifdef DEBUG_DIGIT
//        qDebug() << endl;
//    #endif
//        drawContours(imgOut, contour, i, Scalar(0,0,255));
//    }

//    double minComp = 1000;
//    int minIndx = 0;
//    int actHoles = holesNumber(imgGray);
//    for(int i = 0 ; i < mPatternsContours.size() ; i++)
//    {
//        if(minComp > res[i] && mPatternHoles[i] == actHoles)
//        {
//            minIndx = i;
//            minComp = res[i];
//        }
//    }


//#ifdef DEBUG_DIGIT
//    qDebug() << "Res = " << minIndx << " Holes num = " << actHoles << "Convexity = " << QString::number(convexity(imgGray)) << endl;
//#endif
////    imshow("Test contours", imgOut);

    return -1;
}

double mycvDigitProcGeometric::convexity(const Mat &aSrc)
{
    Mat imgGray;
    threshold(aSrc, imgGray, 127, 255, CV_THRESH_BINARY | CV_THRESH_OTSU);
    // Remove small objects
    mycvClassFloodFill cFF;
    cFF.mycvRemoveHigherObjects(imgGray, imgGray, 0, 0, 0, imgGray.rows/8, imgGray.rows/8, 5);

    // find contour
    vector<vector<Point>> contour;
    findContours(imgGray.clone(), contour, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
    // find convex hull
    vector<Point> hull;
    convexHull(contour[0], hull);

    // Convexity defects
    vector<Vec4i> defects;
    vector<int> hullI;
    convexHull(contour[0], hullI);
    if(contour[0].size() > 0 && hullI.size() > 0)
        convexityDefects(contour[0], hullI, defects);

    Mat imgDefects;
    cvtColor(imgGray, imgDefects, CV_GRAY2BGR);
    for(int i = 0 ; i < defects.size() ; i++)
    {
        circle(imgDefects, contour[0][defects[i][0]], 3, Scalar(255, 0, 0));
        circle(imgDefects, contour[0][defects[i][1]], 3, Scalar(255, 0, 0));
        circle(imgDefects, contour[0][defects[i][2]], 3, Scalar(255, 0, 0));
        line(imgDefects, contour[0][defects[i][0]], contour[0][defects[i][2]], Scalar(0, 0, 255));
        line(imgDefects, contour[0][defects[i][1]], contour[0][defects[i][2]], Scalar(0, 0, 255));
    }
    resize(imgDefects, imgDefects, Size(), 5, 5);
    imshow("imgDefects", imgDefects);


    // evaluate areas
    double origArea = contourArea(contour[0]);
    double hullArea = contourArea(hull);

    return ((origArea/hullArea));
}

int mycvDigitProcGeometric::holesNumber(const Mat &aSrc)
{
    Mat imgGray;
    threshold(aSrc, imgGray, 127, 255, CV_THRESH_BINARY | CV_THRESH_OTSU);
    // Remove small objects
    mycvClassFloodFill cFF;
    cFF.mycvRemoveHigherObjects(imgGray, imgGray, 0, 0, 0, imgGray.rows/8, imgGray.rows/8, 5);
//    int blockSz = imgGray.cols/10;
//    blockSz += (blockSz + 1) % 2;
//    Mat element = getStructuringElement(MORPH_ELLIPSE, Size(blockSz, blockSz));
//    morphologyEx(imgGray, imgGray, CV_MOP_OPEN, element);
    imshow("Digit no smalls", imgGray);

    vector<vector<Point>> contour;
    findContours(imgGray.clone(), contour, CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);

    return contour.size()-1;
}
