#include "project.h"

Project::Project(Settings* _settings){
    settings=_settings;
    filename="";
    retrmode=CV_RETR_LIST;
    cannythresh=100;
    gcode="";
    ivcombined=ImageViewer(&combined, "Images");
    ivprocessed=ImageViewer(&processed, "Processing");
}

Project::Project(Settings* _settings, std::string _filename){
    settings=_settings;
    filename=_filename;
    retrmode=CV_RETR_LIST;
    cannythresh=100;
    gcode="";
    ivcombined=ImageViewer(&combined, "Images");
    ivprocessed=ImageViewer(&processed, "Processing");
}

std::vector<Image>* Project::getImages(){
    return &images;
}

int Project::getRetrMode(){
    return retrmode;
}

int Project::getCannyThresh(){
    return cannythresh;
}

std::map<int, int>* Project::getColoring(){
    return &coloring;
}

std::string* Project::getGcode(){
    return &gcode;
}

void Project::setFileName(std::string _filename){
    filename=_filename;
}

void Project::setRetrMode(int _retrmode){
    retrmode=_retrmode;
}

void Project::setCannyThresh(int _cannythresh){
    cannythresh=_cannythresh;
}

bool Project::newProj(){
    //create files, save def, etc
    FILE* f=fopen(filename.c_str(), "w");
    if(!f){
        fprintf(stderr, "Error> Creating file for project. At: Project::newProj() (file: %s, line: %i)\n", __FILE__, __LINE__);
        return false;
    }
    fprintf(f,"");
    fclose(f);
    return true;
}

void Project::loadProj(){
    //load stuff, deserialize
}

void Project::save(){
    //serialize, save to file
}

void Project::close(){
    //close project, free up resources;
    cv::destroyAllWindows();
}

bool Project::combine(){
    //get col and row count
    int colmax=0, rowmax=0;
    for(std::vector<Image>::iterator it=images.begin(); it<images.end(); it++){
        //get current mat
        cv::Mat cur=*it->getMat();

        //get resized mat
        cv::Mat resz;
        cv::resize(cur, resz, cv::Size(cur.cols*it->getScale(), cur.rows*it->getScale()));

        //get cropped mat
        cv::Mat crop=resz(cv::Rect(it->getCrop1().getX(), it->getCrop1().getY(), (it->getCrop2().getX()-it->getCrop1().getX())*it->getScale(), (it->getCrop2().getY()-it->getCrop1().getY())*it->getScale()));

        //crop debug
        if(settings->isDebug()){
            ImageViewer* iv=new ImageViewer(crop, "DEBUG:combine> "+it->getFilename());
            iv->show();
        }

        if(it->getPosition().getX()+crop.cols > colmax){
            colmax=it->getPosition().getX()+crop.cols;
        }
        if(it->getPosition().getY()+crop.rows > rowmax){
            rowmax=it->getPosition().getY()+crop.rows;
        }

        //cleanup
        resz.release();
        crop.release();
    }

    if(settings->isDebug()){
        printf("DEBUG:combine> colmax=%i, rowmax=%i\n", colmax, rowmax);
    }

    //create new mat
    combined=cv::Mat::zeros(rowmax, colmax, CV_8UC3);

    //add layers
    for(std::vector<Image>::iterator it=images.begin(); it<images.end(); it++){
        //get current mat
        cv::Mat cur=*it->getMat();

        //get resized mat
        cv::Mat resz;
        cv::resize(cur, resz, cv::Size(cur.cols*it->getScale(), cur.rows*it->getScale()));

        //get cropped mat
        cv::Mat crop=resz(cv::Rect(it->getCrop1().getX()*it->getScale(), it->getCrop1().getY()*it->getScale(), (it->getCrop2().getX()-it->getCrop1().getX())*it->getScale(), (it->getCrop2().getY()-it->getCrop1().getY())*it->getScale()));

        //add layer
        crop.copyTo(combined(cv::Rect(it->getPosition().getX(), it->getPosition().getY(), crop.cols, crop.rows)));

        //cleanup
        resz.release();
        crop.release();
    }

    //show results
    ivcombined.refresh();

    //everything alright
    return true;
}

bool Project::process(){
    if(combined.empty()){
        return false;
    }

    //convert mat to gray
    cv::Mat graymat;
    cv::cvtColor(combined, graymat, CV_BGR2GRAY);
    if(settings->isDebug()){
        ImageViewer* iv=new ImageViewer(graymat, "DEBUG:process> cvtColor");
        iv->show();
    }

    //blur edges
    cv::blur(graymat, graymat, cv::Size(3, 3));
    if(settings->isDebug()){
        ImageViewer* iv=new ImageViewer(graymat, "DEBUG:process> blur");
        iv->show();
    }

    //canny edge detection
    cv::Mat canny;
    cv::Canny(graymat, canny, cannythresh, cannythresh*2, 3);
    if(settings->isDebug()){
        ImageViewer* iv=new ImageViewer(canny, "DEBUG:process> Canny");
        iv->show();
    }

    //find contours
    cv::findContours(canny, contours, hierarchy, retrmode, CV_CHAIN_APPROX_SIMPLE, cv::Point(0, 0));

    //build color map if not ready
    if(coloring.size()!=contours.size()){
        coloring.clear();
        for(int i=0; i<contours.size(); i++){
            coloring[i]=-1; //set to default head to use
        }
    }

    //draw output
    processed=cv::Mat::zeros(canny.size(), CV_8UC3);
    for(int i=0; i<contours.size(); i++){
        cv::Scalar color=cv::Scalar(30, 200, 30);
        cv::drawContours(processed, contours, i, color, 2, 8, hierarchy, 0, cv::Point());
    }

    //show results
    ivprocessed.refresh();

    //cleanup
    graymat.release();
    canny.release();

    //it's k
    return true;
}

bool Project::printEdge(int n){
    //the same story
    if(combined.empty()){
        return false;
    }

    //draw contours
    processed=cv::Mat::zeros(processed.size(), CV_8UC3);
    for(int i=0; i<contours.size(); i++){
        cv::Scalar color=cv::Scalar(30, 200, 30);
        cv::drawContours(processed, contours, i, color, 2, 8, hierarchy, 0, cv::Point());
    }

    //highlight "The One"
    cv::Scalar color=cv::Scalar(30, 30, 200);
    cv::drawContours(processed, contours, n, color, 2, 8, hierarchy, 0, cv::Point());

    //show results
    ivprocessed.refresh();

    //kkk
    return true;
}

bool Project::generateGcode(){
    //check if the stuff is set
    if(contours.empty()){
        return false;
    }

    //clear previous gcode
    gcode="";

    //statistics
    unsigned int lines=0;
    double maxX=0, maxY=0;

    //add info
    gcode+=";Generated by: PlottWareControl\n;Project: "+filename+"\n;Program created by: Fandly Gergo (fandlygergo@gmail.hu, systemtest.tk)\n\n\n\n";

    //startup code
    gcode+="G90 ;absolute positioning\n";
    gcode+="M17 ;enable steppers\n";
    gcode+="T1000 ;deselect all tools\n";
    gcode+="G28 ;home axises\n";
    lines+=10;

    //iterate through contours
    for(int i=0; i<contours.size(); i++){
        //if we need to eliminate the contour...
        if(coloring[i]==0){
            continue; //we do so
        }

        if(coloring[i]==-1){
            coloring[i]=1; //set to the default if not set...
        }

        //start contour drawing
        gcode+=";printing contour #"+std::to_string(i)+"\n";
        //select tool
        gcode+="T"+std::to_string(coloring[i])+"\n";

        //move to first point
        gcode+="G0 X"+std::to_string(contours[i][0].x/10.0)+" Y"+std::to_string(contours[i][0].y/10.0)+"\n";
        if(contours[i][0].x/10.0 > maxX){
            maxX=contours[i][0].x/10.0;
        }
        if(contours[i][0].y/10.0 > maxY){
            maxY=contours[i][0].y/10.0;
        }

        //move pen down
        gcode+="M300 S10\n";
        lines+=4;

        //iterate through points
        for(int o=1; o<contours[i].size(); o++){
            gcode+="G1 X"+std::to_string(contours[i][o].x/10.0)+" Y"+std::to_string(contours[i][o].y/10.0)+"\n";
            if(contours[i][o].x/10.0 > maxX){
                maxX=contours[i][o].x/10.0;
            }
            if(contours[i][o].y/10.0 > maxY){
                maxY=contours[i][o].y/10.0;
            }
            lines++;
        }

        //lift up pen
        gcode+="M300 S50\n";
        lines+=1;
    }

    //end
    gcode+="T1000 ;deselect tool\n";
    gcode+="M18 ;disable steppers\n";
    gcode+="M2 ;program end\n";
    lines+=3;

    //print "final thoughts"
    lines+=7;
    gcode+="\n\n\n;Line count: "+std::to_string(lines)+"\n;Max X: "+std::to_string(maxX)+"\n;Max Y: "+std::to_string(maxY)+"\n;ENJOY!";
}

bool Project::exportGcode(std::string file){
    FILE* f=fopen(file.c_str(), "w");
    if(!f){
        fprintf(stderr, "Error> Can't create file for gcode export. At: Project::exportGcode(std::string file) (file: %s, line: %i)\n", __FILE__, __LINE__);
        return false;
    }

    fprintf(f, "%s", gcode.c_str());
    fclose(f);
    return true;
}
