#include "mycvdigitprocfeatures.h"
#ifdef DEBUG_DIGIT
    #include <QDebug>
    #include <QString>
#endif

mycvDigitProcFeatures::mycvDigitProcFeatures()
{

}

mycvDigitProcFeatures::~mycvDigitProcFeatures()
{

}

void mycvDigitProcFeatures::loadPattern(Mat &aSrc)
{
    Mat imgGray, imgToCut;
    cv::cvtColor(aSrc, imgGray, CV_BGR2GRAY);
    aSrc.convertTo(imgToCut, CV_8UC1);
    /// Character segmentation
    threshold(imgGray, imgGray, 127, 255, CV_THRESH_BINARY | CV_THRESH_OTSU);

    // Segment pattern characters
    vector<vector<Point>> contour;
    findContours(imgGray.clone(), contour, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);

    // Get indexes
    vector<int> indxs;
    for( int i = 0; i < contour.size(); i++ )
    {
        Rect rect = boundingRect(contour[i]);
        indxs.push_back(rect.x);
    }
    sort(indxs.begin(), indxs.end());

    mImgsPatterns.clear();
    mNumPatterns.clear();
    for( int i = 0; i < contour.size(); i++ )
    {
        Rect rect;
        rect = boundingRect(contour[i]);
        Mat imgCut;
        imgCut = imgGray(rect).clone();
        // add borders
        copyMakeBorder(imgCut, imgCut, 5, 5, 5, 5, BORDER_CONSTANT, Scalar(0));
        // push pattern to list
        mImgsPatterns.push_back(imgCut);

        int j = 0;
        while(rect.x > indxs[j])
        {
            j++;
        }
        // Make horizontal indexes
        mNumPatterns.push_back(j);
        copyMakeBorder(imgCut, imgCut, 5, 5, 50, 50, BORDER_CONSTANT, Scalar(0));
        ostringstream stream;
        stream << i << " indx:" << j;
        imshow(stream.str(), imgCut);
    }
    int i = 0;
}

float mycvDigitProcFeatures::match(const Mat &aSrc, int aMethod)
{
    Mat imgGray;
//    cv::cvtColor(aSrc, imgGray, CV_BGR2GRAY);
    // To grayscale
    aSrc.convertTo(imgGray, CV_8UC1);

    // Find features in digit
//    GoodFeaturesToTrackDetector *detector = new GoodFeaturesToTrackDetector(2000, 0.01, 10, 3, true);
//    FastFeatureDetector detector(10, false);
    FastFeatureDetector *detectorType = new FastFeatureDetector(100, false);
    Ptr<FeatureDetector> detector = new PyramidAdaptedFeatureDetector(detectorType, 2);
//    Ptr<FeatureDetector> detector = new GridAdaptedFeatureDetector(detectorType);
//    Ptr<FeatureDetector> detector = new DynamicAdaptedFeatureDetector(new FastAdjuster(5,false));

    vector<KeyPoint> keypoints1;
    detector->detect(imgGray, keypoints1);
    // Extract digit features
    SiftDescriptorExtractor extractor;
//    FREAK extractor;
//    BriefDescriptorExtractor extractor;
//    ORB extractor;
//    OrbDescriptorExtractor extractor;

    Mat descDigit;
    extractor.compute(imgGray, keypoints1, descDigit);
//    if(descDigit.type()!=CV_32F) {
//        descDigit.convertTo(descDigit, CV_32F);
//    }

    // Show digit features
    Mat imgBGR;
    aSrc.convertTo(imgBGR, CV_8UC3);
    imgGray.convertTo(imgGray, CV_8UC3);
    drawKeypoints(imgGray, keypoints1, imgBGR);
    imshow("test", imgBGR);

    // Create matcher
    FlannBasedMatcher matcher;
//    FlannBasedMatcher matcher(new cv::flann::LshIndexParams(5, 24, 2));
//    BFMatcher matcher(NORM_HAMMING);

    vector<int> matchesBuf;
    for(int i = 0 ; i < (int)mImgsPatterns.size() ; i++)
    {
        // find features in patterns
        vector<KeyPoint> keypoints2;
        Mat imgAct = mImgsPatterns[i].clone();
        detector->detect(imgAct, keypoints2);
        if(keypoints2.empty())continue;
//        Mat imgPattern;
//        cvtColor(mImgsPatterns[i], imgPattern, CV_GRAY2BGR);
//        drawKeypoints(imgPattern, keypoints2, imgPattern);

        // Extract pattern features
        Mat descPattern;
        extractor.compute(imgAct, keypoints2, descPattern);
//        if(descPattern.type()!=CV_32F) {
//            descPattern.convertTo(descPattern, CV_32F);
//        }

#ifdef DEBUG_DIGIT
    ostringstream stream;
    stream << mNumPatterns[i];

    imgAct.convertTo(imgBGR, CV_8UC3);
    drawKeypoints(imgAct, keypoints2, imgBGR);
    imshow(stream.str(), imgBGR);
#endif
        if(descPattern.rows*descPattern.cols == 0)continue;
        // Train matcher
        std::vector< DMatch > matches;
        matcher.match( descDigit, descPattern, matches);

        // Find only good matches
        double max_dist = 0; double min_dist = 100;
        for( int i = 0; i < descDigit.rows; i++ )
        {
            double dist = matches[i].distance;
            if( dist < min_dist ) min_dist = dist;
            if( dist > max_dist ) max_dist = dist;
        }
        std::vector< DMatch > good_matches;
        for( int i = 0; i < descDigit.rows; i++ )
        {
            if(matches[i].distance <= min_dist*2.8)
            good_matches.push_back( matches[i]);
        }

        // Draw matcher
        Mat imgMatches;
        if(good_matches.size() > 0)
        {
            drawMatches(imgGray, keypoints1, mImgsPatterns[i], keypoints2, good_matches, imgMatches);
            // Show matches
        #ifdef DEBUG_DIGIT
            ostringstream stream;
            stream << mNumPatterns[i];
            imshow(stream.str(), imgMatches);
            moveWindow(stream.str(), 5, 5/*+i*imgMatches.rows*1.5*/);

            qDebug() << mNumPatterns[i] << " number of matches:" << good_matches.size() << endl;
        #endif
        }
        matchesBuf.push_back(good_matches.size());
    }

    if(matchesBuf.empty())return -1;

    int maxMatch = 0, maxIndx = -1;
    for(int i = 0 ; i < (int)mImgsPatterns.size() ; i++)
    {
        if(maxMatch < matchesBuf[i])
        {
            maxMatch = matchesBuf[i];
            maxIndx = mNumPatterns[i];
        }
    }

    // Check minimum matches
    if(maxMatch < MIN_MATCHES)maxIndx = -1;

#ifdef DEBUG_DIGIT
    ostringstream stream;
    stream << maxIndx;
    moveWindow(stream.str(), 5, 5+imgGray.rows*1.5);

    if(maxIndx < 10)
        qDebug() << "Result: " << maxIndx << endl;
    else
        qDebug() << "Result: " << (maxIndx-10)+0.5 << endl;

    qDebug() << "-----------------------------------------------" << endl;
#endif
    if(maxIndx < 10)
        return (float) maxIndx;
    else
        return (float)(maxIndx-10)+0.5;
}
