#include "platesolving.h"
#include <QSettings>
#include <QMessageBox>
#include "ui_platesolving.h"
#include "solver.h"
#include "imageringlist.h"
#include "platesolvingsettings.h"

PlateSolving::PlateSolving(QWidget *parent)
    : QDockWidget(parent)
    , _ui(new Ui::PlateSolving)
{
    _ui->setupUi(this);

    _solver = new Solver(this);
    QSettings settings;
    _solver->setIndexFolder(settings.value("platesolving/indexPath", Solver::getTenmonIndexPath()).toString());
    auto profiles = StellarSolver::getBuiltInProfiles();
    int profileIdx = settings.value("platesolving/profile", 0).toInt();
    _solver->setParameters(profiles[profileIdx]);

    for(auto &profile : profiles)
    {
        _ui->profileComboBox->addItem(profile.listName);
    }
    _ui->profileComboBox->setCurrentIndex(profileIdx);
    _ui->profileComboBox->setToolTip(profiles[profileIdx].description);
    _ui->scaleUnit->setCurrentIndex(settings.value("platesolving/scaleUnit", 1).toInt());

    connect(_ui->profileComboBox, &QComboBox::currentIndexChanged, [this](int index){
        auto profiles = StellarSolver::getBuiltInProfiles();
        _solver->setParameters(profiles[index]);
        _ui->profileComboBox->setToolTip(profiles[index].description);
        QSettings settings;
        settings.setValue("platesolving/profile", index);
    });

    connect(_ui->extractButton, &QPushButton::clicked, this, &PlateSolving::extract);
    connect(_ui->solveButton, &QPushButton::clicked, this, &PlateSolving::solve);
    connect(_ui->settingsButton, &QPushButton::clicked, this, &PlateSolving::settings);
    connect(_ui->abortButton, &QPushButton::clicked, this, &PlateSolving::abort);
    connect(_ui->updateButton, &QPushButton::clicked, this, &PlateSolving::updateHeader);
    connect(_ui->raStart, &QDoubleSpinBox::valueChanged, [this](double val){ _ui->raLabel->setText("RA " + SkyPoint::toHMS(val)); });
    connect(_solver, &Solver::solvingDone, this, &PlateSolving::solvingDone);
    connect(_solver, &Solver::extractionDone, this, &PlateSolving::extractionDone);
    connect(_solver, &Solver::logOutput, [this](const QString &log){ _ui->log->appendPlainText(log); });
    connect(_solver, &Solver::headerUpdated, this, &PlateSolving::headerUpdated);
}

PlateSolving::~PlateSolving()
{
    QSettings settings;
    settings.setValue("platesolving/profile", _ui->profileComboBox->currentIndex());
    settings.setValue("platesolving/scaleUnit", _ui->scaleUnit->currentIndex());
    delete _ui;
}

void PlateSolving::extract()
{
    if(!_rawImage)return;
    _ui->solveButton->setDisabled(true);
    _ui->extractButton->setDisabled(true);
    _ui->log->clear();

    _solver->loadImage(_rawImage, _path);
    _solvingTime.start();
    _solver->extractSources(_ui->withHFR->isChecked());
}

void PlateSolving::extractionDone()
{
    auto stars = _solver->getStars();
    float a = 0;
    float b = 0;
    float hfr = 0;
    for(auto &star : stars)
    {
        a += star.a;
        b += star.b;
        hfr += star.HFR;
    }
    if(size_t size = stars.size())
    {
        a /= size;
        b /= size;
        hfr /= size;
    }

    _ui->stars->setText(QString::number(stars.size()));
    _ui->hfr->setText(QString("%1pix Ecc:%2").arg(hfr).arg(std::sqrt(1 - (b*b)/(a*a))));
    _ui->log->appendPlainText(QString("Extraction finished in %1 ms").arg(_solvingTime.elapsed()));

    _ui->solveButton->setDisabled(false);
    _ui->extractButton->setDisabled(false);
}

void PlateSolving::solve()
{
    if(!_rawImage)return;
    _ui->solveButton->setDisabled(true);
    _ui->extractButton->setDisabled(true);
    _ui->log->clear();

    _solver->loadImage(_rawImage, _path);
    if(_ui->usePosition->isChecked())
        _solver->setSearchPosition(_ui->raStart->value(), _ui->decStart->value());

    if(_ui->useScale->isChecked())
    {
        SSolver::ScaleUnits scaleUnit;
        switch(_ui->scaleUnit->currentIndex())
        {
        default:
        case 0:
            scaleUnit = SSolver::ScaleUnits::DEG_WIDTH; break;
        case 1:
            scaleUnit = SSolver::ScaleUnits::ARCMIN_WIDTH; break;
        case 2:
            scaleUnit = SSolver::ScaleUnits::ARCSEC_PER_PIX; break;
        case 3:
            scaleUnit = SSolver::ScaleUnits::FOCAL_MM; break;
        }
        _solver->setSearchScale(_ui->fovLow->value(), _ui->fovHigh->value(), scaleUnit);
    }
    _solvingTime.start();
    _solver->solveImage();
}

void PlateSolving::solvingDone()
{
    _ui->solveButton->setDisabled(false);
    _ui->extractButton->setDisabled(false);

    auto solution = _solver->getSolution();
    _ui->ra->setText(SkyPoint::toHMS(solution.ra / 15.0));
    _ui->dec->setText(SkyPoint::toDMS(solution.dec));
    _ui->orientation->setText(QString::number(solution.orientation) + "°");
    _ui->fieldWidth->setText(QString::number(solution.fieldWidth) + "'");
    _ui->fieldHeight->setText(QString::number(solution.fieldHeight) + "\"");
    _ui->pixelScale->setText(QString::number(solution.pixscale) + "\"/pix");
    _ui->log->appendPlainText(QString("Solving finished in %1 ms").arg(_solvingTime.elapsed()));
}

void PlateSolving::abort()
{
    _solver->abort();
}

void PlateSolving::updateHeader()
{
    QString error;
    if(!_solver->updateHeader(error))
        QMessageBox::warning(this, tr("Header update failed"), error);
}

void PlateSolving::imageLoaded(Image *image)
{
    if(image && image->rawImage())
    {
        _rawImage = image->rawImage();
        _path = image->name();
        _ui->ra->clear();
        _ui->dec->clear();
        _ui->orientation->clear();
        _ui->fieldWidth->clear();
        _ui->fieldHeight->clear();
        _ui->pixelScale->clear();
        _ui->hfr->clear();
        _ui->stars->clear();

        const ImageInfoData &info = image->info();
        SkyPointScale pointScale = info.getCenterRaDec();
        if(!std::isnan(pointScale.point.RA()) && !std::isnan(pointScale.point.DEC()))
        {
            _ui->raStart->setValue(pointScale.point.RAHour());
            _ui->decStart->setValue(pointScale.point.DEC());
            _ui->usePosition->setChecked(true);
        }
        else
        {
            _ui->usePosition->setChecked(false);
        }

        if(pointScale.scaleValid)
        {
            _ui->scaleUnit->setCurrentIndex(2);
            _ui->fovLow->setValue(pointScale.scaleLow);
            _ui->fovHigh->setValue(pointScale.scaleHigh);
            _ui->useScale->setChecked(true);
        }
        else
        {
            _ui->useScale->setChecked(false);
        }
    }
}

void PlateSolving::settings()
{
    PlateSolvingSettings settings(this);
    settings.exec();
    _solver->setIndexFolder(settings.indexDirectory());
}
