#include "MainWindow.h"
#include "ui_MainWindow.h"
#include "TMath.h"
#include "TString.h"
//#include <iostream>
//#include "TList.h"
#include "TStyle.h"
#include "TFile.h"
#include "TTree.h"
#include "TGraph.h"
#include "TAxis.h"

#include "qrootcanvas.h"
#include <QVBoxLayout>
#include <TSystem.h>
#include <QTimer>
#include <QDebug>
#include <QWindow>

#include <QMessageBox>

#include "reconstruction.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    connect(ui->actionOpen, &QAction::triggered, this, &MainWindow::handleOpen);
    connect(ui->actionExit, &QAction::triggered, this, &MainWindow::handleClose);
    connect(ui->actionSave, &QAction::triggered, this, &MainWindow::handleSave);
    connect(ui->actionSave_As, &QAction::triggered, this, &MainWindow::handleSaveAs);

    connect(ui->actionStart, &QAction::triggered, this, &MainWindow::handleStart);
    connect(ui->actionStop, &QAction::triggered, this, &MainWindow::handleStop);
    connect(ui->actionClear, &QAction::triggered, this, &MainWindow::handleClear);

    connect(ui->actionAbout, &QAction::triggered, this, &MainWindow::handleAbout);

    config = new Setup(Vector3D(SOURCE_X, SOURCE_Y, SOURCE_Z));
    coneQueue = new SPSC(config->capacity);

    workerThread = new Worker(this, config, coneQueue);
    connect(workerThread, &Worker::finished, workerThread, &QObject::deleteLater);
    connect(workerThread, &Worker::finished, this, &MainWindow::notifyThreadFinished);
    connect(this, &MainWindow::threadStopped, workerThread, &Worker::stopExecution);
    workerThread->start();

    createHist();
    createGridlines();
    createCountsLabel();
    ui->canvas->Canvas()->Modified();
    ui->canvas->Canvas()->Update();
}

void MainWindow::changeEvent(QEvent *e)
{
    if(e->type() == QEvent::WindowStateChange){
        QWindowStateChangeEvent *event = static_cast<QWindowStateChangeEvent *>(e);
        if((event->oldState() & Qt::WindowMaximized)||
                (event->oldState() & Qt::WindowMinimized)||
                (event->oldState() & Qt::WindowNoState &&
                 this->windowState() == Qt::WindowMaximized)){
            if(ui->canvas->Canvas()){
                ui->canvas->Canvas()->Resize();
                ui->canvas->Canvas()->Update();
            }
        }
    }
}

void MainWindow::closeEvent (QCloseEvent *event)
{
    QMessageBox::StandardButton resBtn = QMessageBox::question( this, "Close",
                                                                tr("Are you sure you want to quit?\n"),
                                                                QMessageBox::Cancel | QMessageBox::No | QMessageBox::Yes,
                                                                QMessageBox::Yes);
    if (resBtn != QMessageBox::Yes) {
        event->ignore();
    } else {
        finished=true;
        if (workerThread) {
//            qDebug() << "Exiting thread..";
            aborted=true;
            // send stop signal to worker thread
            emit threadStopped();
        }
//        event->accept();
    }
}

MainWindow::~MainWindow()
{
    delete ui;
    if (coneQueue)
        delete coneQueue;
    if (config)
        delete config;
    for (int i = 0; i < longitudes.size(); ++i) {
       if(longitudes[i]) {
           delete longitudes[i];
       }
    }
    for (int i = 0; i < latitudes.size(); ++i) {
       if (latitudes[i]) {
           delete latitudes[i];
       }
    }
}

void MainWindow::run()
{
    std::vector<Cone> cones(config->chuckSize);
    bool emptyQueue = false;
    while (!finished)
    {
        if (!stop)
        {
            int i(0);
            while (i < config->chuckSize)
            {
                emptyQueue = !(coneQueue->wait_dequeue_timed(cones[i],
                                                           std::chrono::milliseconds(10)));
                if (emptyQueue) break;
                i++;
            }
            // update image
            updateImage(cones.cbegin(), cones.cbegin() + i, true);
            if (emptyQueue && threadExecutionFinished)
            {
                // stop updating after this final run
                finished = true;
//                qDebug() << "Processing finished.";
                QMessageBox messageBox;
                messageBox.setText("Image reconstruction finished.");
                messageBox.setWindowTitle("Finished");
                messageBox.exec();
                break;
            }
            // gSystem->Sleep(100);
        }
        QApplication::processEvents();
    }
}

void MainWindow::updateImage(std::vector<Cone>::const_iterator first,
                             std::vector<Cone>::const_iterator last,
                             const bool& normalized)
{
    if (first == last)
        return;
    // update image
    if (normalized)
        addConesNormalized(config, hist, counts, first, last);
    else
        addCones(config, hist, counts, first, last);
    // redraw
    redraw();
}

void MainWindow::createHist()
{
    hist = new TH2D("ROI", " ; Azimuth (degree); Elevation (degree)",
                    config->phiBins, -180, 180,
                    config->thetaBins, -90, 90);
    // init image
    for (int i = 0; i < config->phiBins; i++)
    {
        for (int j = 0; j < config->thetaBins; j++)
        {
            hist->SetBinContent(i+1, j+1, 0);
        }
    }
    gStyle->SetOptStat(0);
    hist->GetZaxis()->SetLabelSize(0.02);
    hist->Draw("z aitoff");

}

void MainWindow::createGridlines()
{
    const int Nl = 19; // Number of drawn latitudes
    const int NL = 19; // Number of drawn longitudes
    const int M = 30;
    float conv=M_PI/180;
    float la, lo, x, y, z;
    for (int j=0;j<Nl;++j) {
        // latitudes[j]=new TGraph();
        latitudes.push_back(new TGraph());
        la = -90+180/(Nl-1)*j;
        for (int i=0;i<M+1;++i) {
            lo = -180+360/M*i;
            z  = sqrt(1+cos(la*conv)*cos(lo*conv/2));
            x  = 180*cos(la*conv)*sin(lo*conv/2)/z;
            y  = 90*sin(la*conv)/z;
            latitudes[j]->SetPoint(i,x,y);
        }
    }

    for (int j=0;j<NL;++j) {
        // longitudes[j]=new TGraph();
        longitudes.push_back(new TGraph());
        lo = -180+360/(NL-1)*j;
        for (int i=0;i<M+1;++i) {
            la = -90+180/M*i;
            z  = sqrt(1+cos(la*conv)*cos(lo*conv/2));
            x  = 180*cos(la*conv)*sin(lo*conv/2)/z;
            y  = 90*sin(la*conv)/z;
            longitudes[j]->SetPoint(i,x,y);
        }
    }
    // Draw the grid
    TPad *pad2 = new TPad("pad2","",0,0,1,1);
    pad2->SetFillStyle(4000);
    pad2->SetFillColor(0);
    pad2->SetBorderSize(0);
    pad2->SetFrameBorderMode(0);
    pad2->SetFrameLineColor(0);
    pad2->SetFrameBorderMode(0);
    pad2->Draw();
    pad2->cd();
    Double_t ymin = -89.5;
    Double_t ymax = 89.5;
    Double_t dy = (ymax-ymin)/0.8; //10 per cent margins top and bottom
    Double_t xmin = -180;
    Double_t xmax = 180;
    Double_t dx = (xmax-xmin)/0.8; //10 per cent margins left and right

    pad2->Range(xmin-0.1*dx,ymin-0.1*dy,xmax+0.1*dx,ymax+0.1*dy);

    for (int j=0;j<Nl;++j) latitudes[j]->Draw("l");
    for (int j=0;j<NL;++j) longitudes[j]->Draw("l");
}

void MainWindow::createCountsLabel()
{
    std::string strtmp = "Total counts: " + std::to_string(counts);
    countsText = new TText(0.7, 0.92, strtmp.c_str());
    countsText->SetTextSizePixels(5);
    countsText->SetNDC(kTRUE);
    countsText->Draw("SAME");
}

void MainWindow::aitoff2xy(const double& l, const double& b, double &Al, double &Ab)
{
    Double_t x, y;
    Double_t alpha2 = (l/2)*TMath::DegToRad();
    Double_t delta  = b*TMath::DegToRad();
    Double_t r2     = TMath::Sqrt(2.);
    Double_t f      = 2*r2/TMath::Pi();
    Double_t cdec   = TMath::Cos(delta);
    Double_t denom  = TMath::Sqrt(1. + cdec*TMath::Cos(alpha2));
    x      = cdec*TMath::Sin(alpha2)*2.*r2/denom;
    y      = TMath::Sin(delta)*r2/denom;
    x     *= TMath::RadToDeg()/f;
    y     *= TMath::RadToDeg()/f;
    //  x *= -1.; // for a skymap swap left<->right
    Al = x;
    Ab = y;
}

void MainWindow::redraw()
{
    std::string strtmp = "Total counts: " + std::to_string(counts);
    countsText->SetText(0.7, 0.92, strtmp.c_str());
    ui->canvas->Canvas()->Modified();
    ui->canvas->Canvas()->Update();
}

void MainWindow::handleOpen()
{
    qDebug() << "Open file clicked";
}

void MainWindow::handleSave()
{
    qDebug() << "Save clicked";
}

void MainWindow::handleSaveAs()
{
    qDebug() << "Save as clicked";
}

void MainWindow::handleClose()
{
    qDebug() << "Close clicked";
    this->close();
}

void MainWindow::handleStart()
{
    stop=false;
//    qDebug() << "Start clicked";
}

void MainWindow::handleStop()
{
    stop=true;
//    qDebug() << "Stop clicked";
}

void MainWindow::handleClear()
{
    counts = 0;
    hist->Reset();
    redraw();
//    qDebug() << "Clear clicked";
}

void MainWindow::handleAbout()
{
    QMessageBox messageBox;
    messageBox.setText("This application is created using Qt 5.13.2. Source code is available at Gitlab.");
    messageBox.setWindowTitle("About");
    messageBox.exec();
//    qDebug() << "About clicked";
}

void MainWindow::notifyThreadFinished()
{
    // pop up a message box indicating processing is finished.
    threadExecutionFinished = true;
}