#include <QtGui>

 #include "mainwindow.h"
#include <fstream>
#include <iostream>
#include <vector>
#include <string>
#include <cassert>
#include "liblinear-1.8\linear.h"

#define Malloc(type,n) (type *)malloc((n)*sizeof(type))
  const int NUM_FEATURES = 2000;

MainWindow::MainWindow()
{
    window = new QWidget;
    mainLayout= new QVBoxLayout;
    labelIm = new QLabel;
    layout1 = new QHBoxLayout;
    layout2 = new QHBoxLayout;
    button0 = new QPushButton("&Use Previous Model");
    button1 = new QPushButton("&Train");
    button2 = new QPushButton("&Searching in the directory");
    button3 = new QPushButton("&Searching in an image");
    button4 = new QPushButton("&Exit");
    button5 = new QPushButton("&Retrain");
    button6 = new QPushButton("&Precision and Recall");


    layout2->addWidget(button0, 8);
    layout2->addWidget(button1, 8);
    layout2->addWidget(button6, 8);
    layout2->addWidget(button4, 8);
    //layout1->addWidget(labelIm);
    mainLayout->addLayout(layout2);
    mainLayout->addLayout(layout1);
    //curFile = QFileDialog::getOpenFileName(this);
    window->setLayout(mainLayout);
    setCentralWidget(window);

    createActions();
    createStatusBar();
}

MainWindow::~MainWindow()
{
    delete button0;
    delete button1;
    delete button2;
    delete button3;
    delete button4;
    delete button5;
    delete button6;
    delete labelIm;
    delete layout1;
    delete layout2;
    delete mainLayout;
    delete window;

}

void MainWindow::training()
{
    msg1 = "Input the directory with training Images";
    msg2 = "Input the file with locations";
    msg3 = "Output the file with model";
    files(true ,true ,true, false);
    getFilesTr();
    statusBar()->showMessage("Training Completed", 2000);
    inFile->close();
    outFile->close();
    menu2();
}

void MainWindow::usingModel()
{
    msg3 = "Input the file with model";
    files(false ,false ,true ,false);
    menu2();
}

void MainWindow::menu2()
{
    layout2->removeWidget(button0);
    layout2->removeWidget(button1);
    layout2->removeWidget(button4);
    layout2->removeWidget(button6);
    layout1->addWidget(labelIm);
    layout2->addWidget(button2, 8);
    layout2->addWidget(button3, 8);
    layout2->addWidget(button5, 8);
    layout2->addWidget(button6, 8);
    layout2->addWidget(button4, 8);
}

void MainWindow::getFilesTr()
{
    trainFeatures.clear();
    trainLabels.clear();
    inFile = new QFile(curInFile);
    inFile->open(QIODevice::ReadOnly);
    char temp2[200];
    int Num, Xmin, Ymin, Xmax, Ymax;
    outFile = new QFile(curOutFile);
    outFile->open(QIODevice::ReadWrite | QIODevice::Truncate);
    int cnt = 0;
    curHist = new int[2000];
    while ((inFile->readLine(temp2, 200)) > 0)
    {
        sscanf(temp2, "%d %d %d %d %d", &Num, &Ymin, &Xmin, &Ymax, &Xmax);
        getFileNameFromInt(Num);
        if (cnt > 0)
        {
            delete image;
            delete curImage;
        }
        image = new QPixmap;
        image->load(*curFile);
        labelIm->setPixmap(*image);
        curImage = new QImage(image->toImage());
        for (int j = 0; j <= 1999; j++)
            curHist[j] = 0;
        makeHOG(Ymin, Xmin, Ymax, Xmax);
        trainModel(true);
        int t = curImage->width();
        for (int m = 0; m < t - 80; m += 10)
        {
            if ((m < (Xmin - 60)) || (m > (Xmax - 20)))
            {
                for (int j = 0; j <= 1999; j++)
                    curHist[j] = 0;
                makeHOG(Ymin, m, Ymax, m + 80);
                trainModel(false);
            }
       }


        cnt++;

    }
    delete[] curHist;
    makeModel();
}

void MainWindow::getFilesSDir()
{
    inFile = new QFile(curInFile);
    inFile->open(QIODevice::ReadOnly);
    char temp2[200];
    int Num;

    while (inFile->readLine(temp2, 200) > 0)
    {
        sscanf(temp2, "%d", &Num);
        getFileNameFromInt(Num);
        useImage(Num);

    }
    delete[] curHist;
}

void MainWindow::getFilesSImg()
{
    int Num = 0;
    curFile = new QString(curInFile);
    useImage(Num);
    delete[] curHist;
}

void MainWindow::useImage(int Num)
{
    char temp2[200];
    image = new QPixmap;
    image->load(*curFile);
    outFile->open(QIODevice::Append);
    labelIm->setPixmap(*image);
    curImage = new QImage(image->toImage());;
    curHist = new int[2000];
    for (int j = 0; j <= 1999; j++)
        curHist[j] = 0;
    for (int X = 0; X < (curImage->width() - 80); X += 5)
    {
        for (int j = 0; j <= 1999; j++)
            curHist[j] = 0;
        makeHOG(0, X, 200, X + 80);
        bool b = testModel();
        if (b)
        {
            sprintf(temp2, "%d  %d  %d  %d  %d", Num, 0, X, 200 ,X + 80);
            paint(0, X, 200, X + 80);
            outFile->write(temp2);
            outFile->write("\n");
            outFile->flush();
        }
    }
    image->convertFromImage(*curImage);
    outFile->close();
    repaint();
}

void MainWindow::paint(int Ymin, int Xmin, int Ymax, int Xmax)
{
    int i , k;
    for (i = Xmin + 2; i <  Xmax - 2; i++)
        for (k = (-1); k <  2; k++)
        {
            curImage->setPixel(i + k , Ymin, qRgb(0 ,255 ,0));
            curImage->setPixel(i + k , Ymax, qRgb(0 ,255 ,0));
        }
    for (i = Ymin + 1; i <  Ymax - 2; i++)
        for (k = (-1); k <  2; k++)
        {
            curImage->setPixel(Xmin , i + k, qRgb(0 ,255 ,0));
            curImage->setPixel(Xmax , i + k, qRgb(0 ,255 ,0));
        }

}

void MainWindow::getFileNameFromInt(int i)
{
    int tc , td  , te;
    char c , d , e , f = '\\';
    QString ras = ".png";
    int k = 4;
    tc = i / 100;
    td = (i - tc * 100) / 10;
    te = i - tc * 100 - td * 10;
    if (tc == 0)
    {
        c = '0';
        k--;
    }
     else c = '0' + tc;
    if ((td == 0) && (tc == 0))
    {
        d = '0';
        k--;
    }
     else d = '0' + td;
    e = '0' + te;
    curFile = new QString(curCat);
    *curFile +=  f;
    if (k > 3) *curFile += c;
    if (k > 2) *curFile += d;
    *curFile += e;
    *curFile += ras;

    char temp[8];
    temp[0] = c;
    temp[1] = d;
    temp[2] = e;
    temp[3] = '.';
    temp[4] = 'p';
    temp[5] = 'n';
    temp[6] = 'g';
    temp[7] = '\0';
    statusBar()->showMessage(temp, 2000);
}

void MainWindow::makeHOG(int Ymin, int Xmin, int Ymax, int Xmax)
{
    int N = 3;
    int **wind1;
    wind1 = new int*[3];
    for (int i = 0; i < 3; i++)
                wind1[i] = new int[3];
    wind1[0][0] = 1;
    wind1[1][0] = 2;
    wind1[2][0] = 1;
    wind1[0][1] = 0;
    wind1[1][1] = 0;
    wind1[2][1] = 0;
    wind1[0][2] = -1;
    wind1[1][2] = -2;
    wind1[2][2] = -1;

    int **tempXred;
    tempXred = new int*[curImage->width()];
    for (int i = 0; i < curImage->width(); i++)
            tempXred[i] = new int[curImage->height()];

    int **tempXgreen;
    tempXgreen = new int*[curImage->width()];
    for (int i = 0; i < curImage->width(); i++)
            tempXgreen[i] = new int[curImage->height()];

    int **tempXblue;
    tempXblue = new int*[curImage->width()];
    for (int i = 0; i < curImage->width(); i++)
            tempXblue[i] = new int[curImage->height()];

    int **tempYred;
    tempYred = new int*[curImage->width()];
    for (int i = 0; i < curImage->width(); i++)
            tempYred[i] = new int[curImage->height()];

    int **tempYgreen;
    tempYgreen = new int*[curImage->width()];
    for (int i = 0; i < curImage->width(); i++)
            tempYgreen[i] = new int[curImage->height()];

    int **tempYblue;
    tempYblue = new int*[curImage->width()];
    for (int i = 0; i < curImage->width(); i++)
            tempYblue[i] = new int[curImage->height()];


    wash(tempXred, tempXgreen, tempXblue, Ymin, Xmin, Ymax, Xmax, N, wind1 , true);

    wash(tempYred, tempXgreen, tempYblue, Xmin, Ymin, Xmax, Ymax, N, wind1 , false);

    double Angle, green, blue, red;
    int Num;
    for (int i = Xmin; i < Xmax; i++)
    {
        for (int j = Ymin; j < Ymax; j++)
        {
            double PI = asin(1) * 2;
            Num = (((j - Ymin) / 8) * 10 + ((i - Xmin) / 8)) * 8;
            if (tempXred[i][j]) red = atan((double)tempYred[i][j] / tempXred[i][j]) + PI / 2;
            else red = PI / 2;
            if (tempXgreen[i][j]) green = atan((double)tempYgreen[i][j] / tempXgreen[i][j]) + PI / 2;
            else green = PI / 2;
            if (tempXblue[i][j]) blue = atan((double)tempYblue[i][j] / tempXblue[i][j]) + PI / 2;
            else blue = PI / 2;
            if (red > blue) Angle = red;
            else Angle = blue;
            if (green > Angle) Angle = green;
            curHist[Num + (int)trunc(Angle / PI * 8 )]++;
        }
    }


    for (int i = 0; i < curImage->width(); i++)
            delete[] tempXred[i];
    delete[] tempXred;

    for (int i = 0; i < curImage->width(); i++)
            delete[] tempXgreen[i];
    delete[] tempXgreen;

    for (int i = 0; i < curImage->width(); i++)
            delete[] tempXblue[i];
    delete[] tempXblue;

    for (int i = 0; i < curImage->width(); i++)
            delete[] tempYred[i];
    delete[] tempYred;

    for (int i = 0; i < curImage->width(); i++)
            delete[] tempYgreen[i];
    delete[] tempYgreen;

    for (int i = 0; i < curImage->width(); i++)
            delete[] tempYblue[i];
    delete[] tempYblue;

    return;
}

void MainWindow::trainModel(const bool t)
{

  // PARSE INPUT and split into train and test data

    std::vector<double> features(NUM_FEATURES);
    for (int j = 0; j < NUM_FEATURES; j++)
        features[j] = curHist[j];

    int intLab;
    if (t) intLab = 1;
     else intLab = (-1);

    // odd cnt => instance goes to test set, even => to train set
    trainFeatures.push_back(features);
    trainLabels.push_back(intLab);

  assert(trainLabels.size() == trainFeatures.size());
}


void MainWindow::makeModel()
{
  // FILL TRAIN STRUCTURE AND TRAIN
  struct problem prob;
  prob.l = int(trainLabels.size()); // number of instances
  prob.bias = -1; // bias feature
  prob.n = NUM_FEATURES; // number of features + one for bias if needed

  prob.y = Malloc(int, prob.l); // allocate space for labels
  prob.x = Malloc(struct feature_node *, prob.l); // allocate space for features

  for (size_t i = 0; i < trainLabels.size(); i++)
  {
    prob.x[i] = Malloc(struct feature_node, NUM_FEATURES+1);
    prob.x[i][NUM_FEATURES].index = -1;  // -1 marks the end of list
    for (int j = 0; j < NUM_FEATURES; j++)
    {
      prob.x[i][j].index = 1+j; // 1-based feature number
      prob.x[i][j].value = trainFeatures[i][j];
    }
    prob.y[i] = trainLabels[i];
  }


  // default values of params. don't change them if not sure (unless param.C)
  struct parameter param;
  param.solver_type = L2R_L2LOSS_SVC_DUAL;
  param.C = 1;
  param.eps = 1e-4;
  param.nr_weight = 0;
  param.weight_label = NULL;
  param.weight = NULL;

  struct model* myModel = train(&prob, &param);

  // SAVE MODEL FOR PREDICTION

  if(save_model(qPrintable(curModelFile), myModel))
  {
    std:: cerr << "can't save model to file " << std::endl;
    return;
  }

  free_and_destroy_model(&myModel);
  destroy_param(&param);
  free(prob.y);
  for (int i = 0; i < prob.l; i++) free(prob.x[i]);
  free(prob.x);
  return;
}


bool MainWindow::testModel()
{
  struct feature_node* x = Malloc(struct feature_node, NUM_FEATURES+1);
  x[NUM_FEATURES].index = -1;  // -1 marks the end of list

    for (int j = 0; j < NUM_FEATURES; j++)
    {
      x[j].index = 1+j; // 1-based feature number
      x[j].value = curHist[j];
    }

    int predict_label = predict(myModel,x);
  free(x);



  if (predict_label >= 0) return (true);
  else return (false);
}

void MainWindow::wash(int **TempRed, int **TempGreen, int **TempBlue,int koef1, int koef2, int koef3, int koef4, int N, int **wind, bool a)
{
    int i , j , k, l;
    double red, green, blue;

    for (i = koef1; i < koef3; i++)
        for (j = koef2; j < koef4; j++)
        {
            red = 0;
            green = 0;
            blue = 0;
            for (k = 2 - N ; k < N - 1 ; k++)
                for (l = 2 - N ; l < N - 1 ; l++)
                    if (((j + k) >= koef2) && ((j + k) < koef4) && ((i + l) >= koef1) && ((i + l) < koef3))
                    {
                        if (a)
                        {
                            red +=qRed(curImage->pixel(j + k,i + l)) * wind[k + 1][l + 1];
                            green +=qGreen(curImage->pixel(j + k,i + l)) * wind[k + 1][l + 1];
                            blue +=qBlue(curImage->pixel(j + k,i + l)) * wind[k + 1][l + 1];
                        }
                        else
                        {
                            red +=qRed(curImage->pixel(i + l,j + k)) * wind[k + 1][l + 1];
                            green +=qGreen(curImage->pixel(i + l,j + k)) * wind[k + 1][l + 1];
                            blue +=qBlue(curImage->pixel(i + l,j + k)) * wind[k + 1][l + 1];
                        }
                    }
            if (a)
            {
                TempRed[j][i]= red;
                TempGreen[j][i]= green;
                TempBlue[j][i]= blue;
            }
            else
            {
                TempRed[i][j] = red;
                TempGreen[i][j] = green;
                TempBlue[i][j] = blue;
            }
        }
}

void MainWindow::serIn()
{
    msg2 = "Input the Image";
    msg4 = "Output the file with locations";
    files(false ,true ,false ,true);

    outFile = new QFile(curOutFile);
    outFile->open(QIODevice::ReadWrite | QIODevice::Truncate);
    outFile->close();
    // NOW LOAD MODEL AND CLASSIFY
    if((myModel=load_model(qPrintable(curModelFile)))!=0)
    {
        getFilesSImg();
        statusBar()->showMessage("Searching in the Image Completed", 2000);
    }
    else statusBar()->showMessage("can't load model from file", 2000);
    free_and_destroy_model(&myModel);
}

void MainWindow::serDir()
{
    msg1 = "Input the directory with testing Images";
    msg2 = "Input the file with names of images";
    msg4 = "Output the file with locations";
    files(true ,true, false, true);

    outFile = new QFile(curOutFile);
    outFile->open(QIODevice::ReadWrite | QIODevice::Truncate);
    outFile->close();
    // NOW LOAD MODEL AND CLASSIFY
    if((myModel=load_model(qPrintable(curModelFile)))!=0)
    {
        getFilesSDir();
        statusBar()->showMessage("Searching in the directory Completed", 2000);
    }
    else statusBar()->showMessage("can't load model from file", 2000);
    free_and_destroy_model(&myModel);
}

void MainWindow::PRcount()
{
    char temp[200];
    msg2 = "Input the file with found locations";
    msg3 = "Input the file with right locations";
    files(false, true, true, false);
    inFile = new QFile(curInFile);
    inFile->open(QIODevice::ReadOnly);
    outFile = new QFile(curModelFile);
    outFile->open(QIODevice::ReadOnly);
    std::vector< std::vector<int> > My;
    std::vector< std::vector<int> > Need;
    int Num, Xmin, Ymin, Xmax, Ymax;

    while ((inFile->readLine(temp, 200)) > 0)
    {
        std::vector<int> Cur(5);
        sscanf(temp, "%d %d %d %d %d", &Num, &Ymin, &Xmin, &Ymax, &Xmax);
        Cur[0] = Num;
        Cur[1] = Ymin;
        Cur[2] = Xmin;
        Cur[3] = Ymax;
        Cur[4] = Xmax;

        My.push_back(Cur);
    }
    char temp2[200];
    int t = outFile->readLine(temp2, 200);
    while (t > 0)
    {
        std::vector<int> Cur(5);
        sscanf(temp2, "%d %d %d %d %d", &Num, &Ymin, &Xmin, &Ymax, &Xmax);
        Cur[0] = Num;
        Cur[1] = Ymin;
        Cur[2] = Xmin;
        Cur[3] = Ymax;
        Cur[4] = Xmax;

        Need.push_back(Cur);
        t = outFile->readLine(temp2, 200);
    }

    int i , j, TP1 = 0, TP = 0, GT = Need.size(), DET = My.size();
    bool b = true;
    for (i = 0; i < GT; i++)
    {
        for (j = 0; j < DET; j++)
        {
            if (My[j][0] == Need[i][0])
            {
                if (((My[j][2] + 40) >= Need[i][2]) && ((My[j][4] - 40) <= Need[i][4]))
                {
                    if (b) TP1++;
                    TP++;
                    b = false;
                }
            }
            else b = true;
        }
    }
    double Recall = (double)TP1 / GT, Precision = (double)TP / DET;

    sprintf(temp, "Recall = %lf, Precision = %lf ", Recall, Precision);
    QMessageBox msgBox;
    msgBox.setWindowTitle("Message");
    msgBox.setText(temp);
    msgBox.exec();
    statusBar()->showMessage(temp, 2000);
    inFile->close();
    outFile->close();
}

void MainWindow::files(bool a , bool b , bool c , bool d)
{
    QMessageBox msgBox;
    if (a)
    {

        msgBox.setWindowTitle("Message");
        msgBox.setText(msg1);
        msgBox.exec();
        QString newCat;
        do
            newCat = QFileDialog::getExistingDirectory(this);
        while (newCat.isEmpty());
        curCat = newCat;
    }

    if (b)
    {
        msgBox.setWindowTitle("Message");
        msgBox.setText(msg2);
        msgBox.exec();
        QString newInFile;
        do
            newInFile = QFileDialog::getOpenFileName(this);
        while (newInFile.isEmpty());
        curInFile = newInFile;
    }

    if (c)
    {
        msgBox.setWindowTitle("Message");
        msgBox.setText(msg3);
        msgBox.exec();
        QString newModelFile;
        do
            newModelFile = QFileDialog::getOpenFileName(this);
        while (newModelFile.isEmpty());
        curModelFile = newModelFile;

    }

    if (d)
    {
        msgBox.setWindowTitle("Message");
        msgBox.setText(msg4);
        msgBox.exec();
        QString newOutFile;
        do
            newOutFile = QFileDialog::getOpenFileName(this);
        while (newOutFile.isEmpty());
        curOutFile = newOutFile;
    }

}

void MainWindow::exit()
{
    QMessageBox msgBox;
    msgBox.setWindowTitle("Exit");
    msgBox.setText("Thank you for using my Program)) Goodbye!!");
    msgBox.exec();
    close();
}

void MainWindow::createActions()
{
    useAct = new QAction(tr("&Use the Model..."), this);
    useAct->setStatusTip(tr("Use the Model"));
    connect(button0, SIGNAL(pressed()), this, SLOT(usingModel()));

    PRAct = new QAction(tr("&Count Precision and Recall..."), this);
    PRAct->setStatusTip(tr("Count Precision and Recall"));
    connect(button6, SIGNAL(pressed()), this, SLOT(PRcount()));

    trainAct = new QAction(tr("&train..."), this);
    trainAct->setStatusTip(tr("train"));
    connect(button1, SIGNAL(pressed()), this, SLOT(training()));

    retrainAct = new QAction(tr("&retrain..."), this);
    retrainAct->setStatusTip(tr("retrain"));
    connect(button5, SIGNAL(pressed()), this, SLOT(training()));

    serDirAct = new QAction(tr("&tra..."), this);
    serDirAct->setStatusTip(tr("tra"));
    connect(button2, SIGNAL(pressed()), this, SLOT(serDir()));

    serImAct = new QAction(tr("&tr..."), this);
    serImAct->setStatusTip(tr("tr"));
    connect(button3, SIGNAL(pressed()), this, SLOT(serIn()));

    exitAct = new QAction(tr("&in..."), this);
    exitAct->setStatusTip(tr("in"));
    connect(button4, SIGNAL(pressed()), this, SLOT(exit()));


}

void MainWindow::createStatusBar()
{
    statusBar()->showMessage(tr("Ready"));
}
