#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 <QFileDialog>
#include <QDateTime>
#include <QMessageBox>

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->actionScreenshot, &QAction::triggered, this, &MainWindow::handleScreenshot);
    connect(ui->actionAbout, &QAction::triggered, this, &MainWindow::handleAbout);

    config = new Setup(Vector3D(SOURCE_X, SOURCE_Y, SOURCE_Z));
    image = new RecoImage(config);
    createGridlines();
    createCountsLabel();

    // update image on worker thread
    workerThread = new Worker(this, config, image);
    connect(workerThread, &Worker::finished, workerThread, &QObject::deleteLater);
    connect(workerThread, &Worker::finished, this, &MainWindow::notifyThreadFinished);
    connect(ui->actionStart, &QAction::triggered, workerThread, &Worker::handleStart);
    connect(ui->actionStop, &QAction::triggered, workerThread, &Worker::handleStop);
    connect(ui->actionClear, &QAction::triggered, workerThread, &Worker::handleClear);
    connect(this, &MainWindow::threadStopped, workerThread, &Worker::stopExecution);
    workerThread->start();

    // update every 0.1 second
    QTimer *timer = new QTimer(this);
    connect(timer, &QTimer::timeout, this, QOverload<>::of(&MainWindow::redraw));
    timer->start(1000);

    setWindowTitle(tr("Back projection"));
    ui->canvas->Canvas()->Modified();
    ui->canvas->Canvas()->Update();
    ui->statusBar->showMessage("Ready.");
}

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 {
        if (workerThread) {
//            qDebug() << "Exiting thread..";
            // send stop signal to worker thread
            aborted=true;
            emit threadStopped();
            gSystem->Sleep(500);
        }
        event->accept();
    }
}

MainWindow::~MainWindow()
{
    delete ui;
    if (image)
        delete image;
    if (countsText)
        delete countsText;
    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::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(image->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()
{
    image->mMutex.lock();
    std::string strtmp = "Total counts: " + std::to_string(image->counts);
    countsText->SetText(0.7, 0.92, strtmp.c_str());
    ui->canvas->Canvas()->Modified();
    ui->canvas->Canvas()->Update();
    image->mMutex.unlock();
}

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

bool MainWindow::handleSave()
{
//    qDebug() << "Save clicked";
    if (curFile.isEmpty()) {
        return handleSaveAs();
    } else {
        return saveCanvas(curFile);
    }
}

bool MainWindow::handleSaveAs()
{
    QString fileName = QFileDialog::getSaveFileName(this,
            tr("Save Image"), "",
            tr("PNG file (*.png);;ROOT file (*.root);;C file (*.C);;text file (*.txt)"));
    if (!fileName.isEmpty() && saveCanvas(fileName)){
        curFile = fileName;
        return true;
    }
    return false;
}

bool MainWindow::saveCanvas(const QString& fileName)
{
    QFileInfo fileInfo(fileName);
    QString ext=fileInfo.completeSuffix();
    bool saved=false;
    if (ext == "root" || ext == "png" || ext == "C") {
        qDebug() << "Save as " << ext << " file.";
        image->mMutex.lock();
        ui->canvas->Canvas()->SaveAs(fileName.toLocal8Bit().constData());
        image->mMutex.unlock();
        saved = true;
    }
    else if (ext == "txt") {
        saved = image->saveImage(fileName.toStdString());
    }
    if (saved)
    {
        ui->statusBar->showMessage(QString("Image saved to: %1").arg(fileName));
    }
    return saved;
}
void MainWindow::handleClose()
{
//    qDebug() << "Close clicked";
    this->close();
}

void MainWindow::handleScreenshot()
{
    QDateTime dateTime = QDateTime::currentDateTime();
    QString currentDateTime = dateTime.toString("yyyy-MM-dd-HH-mm-ss");
    QString fileName = QString("Screenshot-%1.png").arg(currentDateTime);
    saveCanvas(fileName);
}
void MainWindow::handleAbout()
{
    QMessageBox::about(this, tr("About Application"),
             tr("This application is created using Qt 5.13.2. Source code is available at Gitlab."));
//    qDebug() << "About clicked";
}

void MainWindow::notifyThreadFinished()
{
    // pop up a message box indicating processing is finished.
    if (!aborted)
    {
        QMessageBox messageBox;
        messageBox.setText("Image reconstruction finished.");
        messageBox.setWindowTitle("Finished");
        messageBox.exec();
    }
}