#include <QToolButton>
#include <QMessageBox>
#include <QProcess>
#include <QTemporaryDir>
#include <QTextStream>
#include <QScrollBar>
#include <QFitsGlobal.h>
#include <QFitsMainWindow.h>
#include <QFitsMainView.h>
#include <QFitsSingleBuffer.h>
#include "qtdpuser.h"
#include "galfit.h"
#include "events.h"

int parseGalfitResult(const Fits &result, const dpString &key, double *val, double *err) {
    char value[81];
    if (result.GetStringKey(key.c_str(), value)) {
        if (value[0] == '*') value[0] = ' ';
        dpString svalue(value);
        int pos;
        if ((pos = svalue.find(" +/- ")) < svalue.length()) {
            *val = svalue.toDouble();
            *err = svalue.mid(pos + 5, svalue.length()).toDouble();
            return 1;
        } else if (value[0] == '[') {
            *val = svalue.mid(1, svalue.length()).toDouble();
            *err = 0.0;
            return 2;
        }
    }
    return 0;
}

QStringList dpGalfitControl::getParameters(bool ro) {
    QStringList rv;
    rv << "# IMAGE PARAMTERS";
    if (ro) {
        rv << "A) none";
        rv << "B) result.fits";
        rv << "C) none";
        rv << "D) none";
        rv << "E) 1";
        rv << "F) none";
        rv << "G) none";
        rv << "H) 1 " + QString::number(xmax->maximum()) + " 1 " + QString::number(ymax->maximum());
    } else {
        rv << "A) input.fits";
        rv << "B) result.fits";
        if (SigmaImage->currentText() == "none") rv << "C) none";
        else rv << "C) sigma.fits";
        if (PSFImage->currentText() == "none") rv << "D) none";
        else rv << "D) psf.fits";
        rv << "E) 1";
        if (BadPixelMask->currentText() == "none") rv << "F) none";
        else rv << "F) mask.fits";
        rv << "G) constraints";
        rv << "H) " + xmin->text() + " " + xmax->text() + " " + ymin->text() + " " + ymax->text();
    }
    rv << "I) " + xconv->text() + " " + yconv->text();
    rv << "J) " + mzero->text();
    rv << "K) " + cdelt1->text() + " " + cdelt2->text();
    rv << "O) regular";
    rv << "P) 0";

    return rv;
}

dpGalfitSky::dpGalfitSky(QWidget *parent)  : dpGalfitComponent(parent) {
    setupUi(this);
    widget->adjustSize();
    dx->fixed->setChecked(true);
    dy->fixed->setChecked(true);
}

void dpGalfitSky::setValues(double b, double x, double y) {
    background->setValue(b);
    dx->setValue(x);
    dy->setValue(y);
}

QStringList dpGalfitSky::getParameters(bool ro) {
    QStringList rv;
    rv << "# Object number sky";
    rv << "0) sky";
    if (ro) {
        rv << "1) " + (background_result->text().isEmpty() ? background->text() : background_result->text()) + " 0";
        rv << "2) " + (dx_result->text().isEmpty() ? dx->text() : dx_result->text()) + " 0";
        rv << "3) " + (dy_result->text().isEmpty() ? dy->text() : dy_result->text()) + " 0";
    } else {
        rv << "1) " + background->text() + (background->isFixed() ? " 0" : " 1");
        rv << "2) " + dx->text() + (dx->isFixed() ? " 0" : " 1");
        rv << "3) " + dy->text() + (dy->isFixed() ? " 0" : " 1");
    }
    rv << "Z) 0";

    return rv;
}

QStringList dpGalfitSky::getConstraints(int component) {
    QStringList rv;
    if (background->isConstrained()) rv << QString::number(component) + " 1 " + background->lowerBound->text() + " to " + background->upperBound->text();
    if (dx->isConstrained()) rv << QString::number(component) + " 2 " + dx->lowerBound->text() + " to " + dx->upperBound->text();
    if (dy->isConstrained()) rv << QString::number(component) + " 3 " + dy->lowerBound->text() + " to " + dy->upperBound->text();

    return rv;
}

void dpGalfitSky::setResult(const int component, const Fits &result) {
    char value[81];
    dpString key;
    double val, err;
    key.sprintf("COMP_%i", component);
    if (!result.GetStringKey(key.c_str(), value)) return;
    if (dpString(value).left(3) != "sky") return;

    key.sprintf("%i_SKY", component);
    if (parseGalfitResult(result, key, &val, &err)) {
        background_result->setText(QString::number(val));
        background_error->setText(QString::number(err));
    }
    key.sprintf("%i_DSDX", component);
    if (parseGalfitResult(result, key, &val, &err)) {
        dx_result->setText(QString::number(val));
        dx_error->setText(QString::number(err));
    }
    key.sprintf("%i_DSDY", component);
    if (parseGalfitResult(result, key, &val, &err)) {
        dy_result->setText(QString::number(val));
        dy_error->setText(QString::number(err));
    }
}

void dpGalfitHidden::setValues(double c0v, double b1v, double b2v, double b3v) {
    c0->setValue(c0v);
    b1->setValue(b1v);
    b2->setValue(b2v);
    b3->setValue(b3v);
}

QStringList dpGalfitHidden::getParameters(bool ro) {
    QStringList rv;
    if (ro) {
        if (c0_do->isChecked()) rv << "C0) " + (c0_result->text().isEmpty() ? c0->text() : c0_result->text()) + " 0";
        if (b1_do->isChecked()) rv << "B1) " + (b1_result->text().isEmpty() ? b1->text() : b1_result->text()) + " 0";
        if (b2_do->isChecked()) rv << "B2) " + (b2_result->text().isEmpty() ? b2->text() : b2_result->text()) + " 0";
        if (b3_do->isChecked()) rv << "B3) " + (b3_result->text().isEmpty() ? b3->text() : b3_result->text()) + " 0";
    } else {
        if (c0_do->isChecked()) rv << "C0) " + c0->text() + (c0->isFixed() ? " 0" : " 1");
        if (b1_do->isChecked()) rv << "B1) " + b1->text() + (b1->isFixed() ? " 0" : " 1");
        if (b2_do->isChecked()) rv << "B2) " + b2->text() + (b2->isFixed() ? " 0" : " 1");
        if (b3_do->isChecked()) rv << "B3) " + b3->text() + (b3->isFixed() ? " 0" : " 1");
    }

    return rv;
}

QStringList dpGalfitHidden::getConstraints(int component) {
    QStringList rv;
    if (c0_do->isChecked()) if (c0->isConstrained()) rv << QString::number(component) + " c0 " + c0->lowerBound->text() + " to " + c0->upperBound->text();
    if (b1_do->isChecked()) if (b1->isConstrained()) rv << QString::number(component) + " b1 " + b1->lowerBound->text() + " to " + b1->upperBound->text();
    if (b2_do->isChecked()) if (b2->isConstrained()) rv << QString::number(component) + " b2 " + b2->lowerBound->text() + " to " + b2->upperBound->text();
    if (b3_do->isChecked()) if (b3->isConstrained()) rv << QString::number(component) + " b3 " + b3->lowerBound->text() + " to " + b3->upperBound->text();

    return rv;
}

void dpGalfitHidden::setResult(const int component, const Fits &result) {
    dpString key;
    double val, err;

    c0_result->clear();
    c0_error->clear();
    b1_result->clear();
    b1_error->clear();
    b2_result->clear();
    b2_error->clear();
    b3_result->clear();
    b3_error->clear();

    key.sprintf("%i_C0", component);
    if (parseGalfitResult(result, key, &val, &err)) {
        c0_result->setText(QString::number(val));
        c0_error->setText(QString::number(err));
    }
    key.sprintf("%i_B1", component);
    if (parseGalfitResult(result, key, &val, &err)) {
        b1_result->setText(QString::number(val));
        b1_error->setText(QString::number(err));
    }
    key.sprintf("%i_B2", component);
    if (parseGalfitResult(result, key, &val, &err)) {
        b2_result->setText(QString::number(val));
        b2_error->setText(QString::number(err));
    }
    key.sprintf("%i_B3", component);
    if (parseGalfitResult(result, key, &val, &err)) {
        b3_result->setText(QString::number(val));
        b3_error->setText(QString::number(err));
    }
}

dpGalfitGaussian::dpGalfitGaussian(QWidget *parent) : dpGalfitComponent(parent) {
    setupUi(this);
    widget->adjustSize();
    adjustSize();
    hidden = new dpGalfitHidden(this);
    hidden->move(0, height());
    adjustSize();
}

void dpGalfitGaussian::setValues(double x, double y, double m, double f, double r, double a) {
    posx->setValue(x);
    posy->setValue(y);
    mag->setValue(m);
    fwhm->setValue(f);
    ratio->setValue(r);
    angle->setValue(a);
}

QStringList dpGalfitGaussian::getParameters(bool ro) {
    QStringList rv;
    rv << "# Object number gaussian";
    rv << "0) gaussian           # object type";
    if (ro) {
        rv << "1) " + (posx_result->text().isEmpty() ? posx->text() : posx_result->text()) + " " + (posy_result->text().isEmpty() ? posy->text() : posy_result->text()) + " 0 0";
        rv << "3) " + (mag_result->text().isEmpty() ? mag->text() : mag_result->text()) + " 0";
        rv << "4) " + (fwhm_result->text().isEmpty() ? fwhm->text() : fwhm_result->text()) + " 0";
        rv << "9) " + (ratio_result->text().isEmpty() ? ratio->text() : ratio_result->text()) + " 0";
        rv << "10) " + (angle_result->text().isEmpty() ? angle->text() : angle_result->text()) + " 0";
     } else {
        rv << "1) " + posx->text() + " " + posy->text() + (posx->isFixed() ? " 0" : " 1") + (posy->isFixed() ? " 0" : " 1");
        rv << "3) " + mag->text() + (mag->isFixed() ? " 0" : " 1");
        rv << "4) " + fwhm->text() + (fwhm->isFixed() ? " 0" : " 1");
        rv << "9) " + ratio->text() + (ratio->isFixed() ? " 0" : " 1");
        rv << "10) " + angle->text() + (angle->isFixed() ? " 0" : " 1");
    }
    rv << "Z) 0";
    rv << hidden->getParameters();

    return rv;
}

int galfitGetXmin() {
    return fitsMainWindow->galfit->galfitControl->xmin->value();
}

int galfitGetYmin() {
    return fitsMainWindow->galfit->galfitControl->ymin->value();
}

QStringList dpGalfitGaussian::getConstraints(int component) {
    QStringList rv;
//    if (posx->isConstrained()) rv << QString::number(component) + " x " + posx->lowerBound->text() + " to " + posx->upperBound->text();
    if (posx->isConstrained()) rv << QString::number(component) + " x " + QString::number(posx->lowerBound->value() - galfitGetXmin() + 1) + " to " + QString::number(posx->upperBound->value()  - galfitGetXmin() + 1);
//    if (posy->isConstrained()) rv << QString::number(component) + " y " + posy->lowerBound->text() + " to " + posy->upperBound->text();
    if (posy->isConstrained()) rv << QString::number(component) + " y " + QString::number(posy->lowerBound->value() - galfitGetYmin() + 1) + " to " + QString::number(posy->upperBound->value() - galfitGetYmin() + 1);
    if (mag->isConstrained()) rv << QString::number(component) + " mag " + mag->lowerBound->text() + " to " + mag->upperBound->text();
    if (fwhm->isConstrained()) rv << QString::number(component) + " rs " + fwhm->lowerBound->text() + " to " + fwhm->upperBound->text();
    if (ratio->isConstrained()) rv << QString::number(component) + " q " + ratio->lowerBound->text() + " to " + ratio->upperBound->text();
    if (angle->isConstrained()) rv << QString::number(component) + " pa " + angle->lowerBound->text() + " to " + angle->upperBound->text();
    rv << hidden->getConstraints(component);

    return rv;
}

void dpGalfitGaussian::setResult(const int component, const Fits &result) {
    char value[81];
    dpString key;
    double val, err;
    key.sprintf("COMP_%i", component);
    if (!result.GetStringKey(key.c_str(), value)) return;
    if (dpString(value).left(8) != "gaussian") return;

    key.sprintf("%i_XC", component);
    if (parseGalfitResult(result, key, &val, &err)) {
        posx_result->setText(QString::number(val));
        posx_error->setText(QString::number(err));
    }
    key.sprintf("%i_YC", component);
    if (parseGalfitResult(result, key, &val, &err)) {
        posy_result->setText(QString::number(val));
        posy_error->setText(QString::number(err));
    }
    key.sprintf("%i_MAG", component);
    if (parseGalfitResult(result, key, &val, &err)) {
        mag_result->setText(QString::number(val));
        mag_error->setText(QString::number(err));
    }
    key.sprintf("%i_FWHM", component);
    if (parseGalfitResult(result, key, &val, &err)) {
        fwhm_result->setText(QString::number(val));
        fwhm_error->setText(QString::number(err));
    }
    key.sprintf("%i_AR", component);
    if (parseGalfitResult(result, key, &val, &err)) {
        ratio_result->setText(QString::number(val));
        ratio_error->setText(QString::number(err));
    }
    key.sprintf("%i_PA", component);
    if (parseGalfitResult(result, key, &val, &err)) {
        angle_result->setText(QString::number(val));
        angle_error->setText(QString::number(err));
    }
    hidden->setResult(component, result);
}

dpGalfitSersic::dpGalfitSersic(QWidget *parent) : dpGalfitComponent(parent) {
    setupUi(this);
    widget->adjustSize();
    adjustSize();
    hidden = new dpGalfitHidden(this);
    hidden->move(0, height());
    adjustSize();
}

void dpGalfitSersic::setValues(double x, double y, double m, double e, double i, double r, double a) {
    posx->setValue(x);
    posy->setValue(y);
    mag->setValue(m);
    re->setValue(e);
    sersic->setValue(i);
    ratio->setValue(r);
    angle->setValue(a);
}

QStringList dpGalfitSersic::getParameters(bool ro) {
    QStringList rv;
    rv << "# Object number sersic";
    rv << "0) sersic           # object type";
    if (ro) {
        rv << "1) " + (posx_result->text().isEmpty() ? posx->text() : posx_result->text()) + " " + (posy_result->text().isEmpty() ? posy->text() : posy_result->text()) + " 0 0";
        rv << "3) " + (mag_result->text().isEmpty() ? mag->text() : mag_result->text()) + " 0";
        rv << "4) " + (re_result->text().isEmpty() ? re->text() : re_result->text()) + " 0";
        rv << "5) " + (sersic_result->text().isEmpty() ? sersic->text() : sersic_result->text()) + " 0";
        rv << "9) " + (ratio_result->text().isEmpty() ? ratio->text() : ratio_result->text()) + " 0";
        rv << "10) " + (angle_result->text().isEmpty() ? angle->text() : angle_result->text()) + " 0";
    } else {
        rv << "1) " + posx->text() + " " + posy->text() + (posx->isFixed() ? " 0" : " 1") + (posy->isFixed() ? " 0" : " 1");
        rv << "3) " + mag->text() + (mag->isFixed() ? " 0" : " 1");
        rv << "4) " + re->text() + (re->isFixed() ? " 0" : " 1");
        rv << "5) " + sersic->text() + (sersic->isFixed() ? " 0" : " 1");
        rv << "9) " + ratio->text() + (ratio->isFixed() ? " 0" : " 1");
        rv << "10) " + angle->text() + (angle->isFixed() ? " 0" : " 1");
    }
    rv << "Z) 0";
    rv << hidden->getParameters();

    return rv;
}

QStringList dpGalfitSersic::getConstraints(int component) {
    QStringList rv;
//    if (posx->isConstrained()) rv << QString::number(component) + " x " + posx->lowerBound->text() + " to " + posx->upperBound->text();
    if (posx->isConstrained()) rv << QString::number(component) + " x " + QString::number(posx->lowerBound->value() - galfitGetXmin() + 1) + " to " + QString::number(posx->upperBound->value()  - galfitGetXmin() + 1);
//    if (posy->isConstrained()) rv << QString::number(component) + " y " + posy->lowerBound->text() + " to " + posy->upperBound->text();
    if (posy->isConstrained()) rv << QString::number(component) + " y " + QString::number(posy->lowerBound->value() - galfitGetYmin() + 1) + " to " + QString::number(posy->upperBound->value() - galfitGetYmin() + 1);
    if (mag->isConstrained()) rv << QString::number(component) + " mag " + mag->lowerBound->text() + " to " + mag->upperBound->text();
    if (re->isConstrained()) rv << QString::number(component) + " re " + re->lowerBound->text() + " to " + re->upperBound->text();
    if (sersic->isConstrained()) rv << QString::number(component) + " n " + sersic->lowerBound->text() + " to " + sersic->upperBound->text();
    if (ratio->isConstrained()) rv << QString::number(component) + " q " + ratio->lowerBound->text() + " to " + ratio->upperBound->text();
    if (angle->isConstrained()) rv << QString::number(component) + " pa " + angle->lowerBound->text() + " to " + angle->upperBound->text();
    rv << hidden->getConstraints(component);

    return rv;
}

void dpGalfitSersic::setResult(const int component, const Fits &result) {
    char value[81];
    dpString key;
    double val, err;
    key.sprintf("COMP_%i", component);
    if (!result.GetStringKey(key.c_str(), value)) return;
    if (dpString(value).left(6) != "sersic") return;

    key.sprintf("%i_XC", component);
    if (parseGalfitResult(result, key, &val, &err)) {
        posx_result->setText(QString::number(val));
        posx_error->setText(QString::number(err));
    }
    key.sprintf("%i_YC", component);
    if (parseGalfitResult(result, key, &val, &err)) {
        posy_result->setText(QString::number(val));
        posy_error->setText(QString::number(err));
    }
    key.sprintf("%i_MAG", component);
    if (parseGalfitResult(result, key, &val, &err)) {
        mag_result->setText(QString::number(val));
        mag_error->setText(QString::number(err));
    }
    key.sprintf("%i_RE", component);
    if (parseGalfitResult(result, key, &val, &err)) {
        re_result->setText(QString::number(val));
        re_error->setText(QString::number(err));
    }
    key.sprintf("%i_N", component);
    if (parseGalfitResult(result, key, &val, &err)) {
        sersic_result->setText(QString::number(val));
        sersic_error->setText(QString::number(err));
    }
    key.sprintf("%i_AR", component);
    if (parseGalfitResult(result, key, &val, &err)) {
        ratio_result->setText(QString::number(val));
        ratio_error->setText(QString::number(err));
    }
    key.sprintf("%i_PA", component);
    if (parseGalfitResult(result, key, &val, &err)) {
        angle_result->setText(QString::number(val));
        angle_error->setText(QString::number(err));
    }
    hidden->setResult(component, result);
}

dpGalfitDialog::dpGalfitDialog(QWidget *parent) : QDialog(parent) {
    setWindowTitle("Galfit");

    galfitControl = new dpGalfitControl;
    galfitControl->setupUi(this);
    galfitControl->layoutWidget->adjustSize();

    adjustSize();

    output = new QTextEdit(this);
    output->setGeometry(0, height(), width(), height() / 2);

    QFont theFont(settings.textfont);
    output->setFont(theFont);

    objects = new QTabWidget(this);
    objects->setGeometry(width(), 0, width(), height() * 1.5);
    objects->setTabsClosable(true);

    connect(objects, SIGNAL(tabCloseRequested(int)), this, SLOT(closeObject(int)));

    QMenu *addObject = new QMenu("+", this);
    addObject->addAction("Sky", this, SLOT(addSky()));
    addObject->addAction("Gaussian", this, SLOT(addGaussian()));
    addObject->addAction("Sersic", this, SLOT(addSersic()));
    QToolButton *tb = new QToolButton();
    tb->setText("+");
    tb->setPopupMode(QToolButton::InstantPopup);
    tb->setMenu(addObject);
    // Add empty, not enabled tab to tabWidget
    objects->addTab(new QLabel("Add components by pressing \"+\""), QString());
    objects->setTabEnabled(0, false);
    // Add tab button to current tab. Button will be enabled, but tab -- not
    objects->tabBar()->setTabButton(0, QTabBar::RightSide, tb);

#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
    connect(galfitControl->InputDataImage, SIGNAL(activated(QString)), this, SLOT(newInputDataImage(QString)));
#else
    connect(galfitControl->InputDataImage, SIGNAL(textActivated(const QString &)), this, SLOT(newInputDataImage(QString)));
#endif /* QT_VERSION */

    adjustSize();

    QPushButton *buttonRun = new QPushButton("Run", this);
    buttonRun->setDefault(true);
    QPushButton *buttonCancel = new QPushButton("Cancel", this);
    QPushButton *buttonNewBuffer = new QPushButton("New Buffer from fit", this);

    buttonRun->setGeometry(10, height() + 10, width() / 4, buttonRun->height());
    buttonCancel->setGeometry(buttonRun->x() + buttonRun->width() + 10, height() + 10, width() / 4, buttonCancel->height());
    buttonNewBuffer->setGeometry(buttonCancel->x() + buttonCancel->width() + 10, height() + 10, width() / 4, buttonNewBuffer->height());

    connect(buttonRun, SIGNAL(clicked()), this, SLOT(runGalfit()));
    connect(buttonCancel, SIGNAL(clicked()), this, SLOT(close()));
    connect(buttonNewBuffer, SIGNAL(clicked()), this, SLOT(newBufferClicked()));

    adjustSize();
    setFixedSize(size());
}

void dpGalfitDialog::updateandshow() {
    QStringList buffList;
    for (auto var:dpuser_vars) {
        if (var.second.type == typeFits) {
            buffList += QString(var.first.c_str());
        }
    }
    galfitControl->InputDataImage->clear();
    galfitControl->InputDataImage->addItems(buffList);
    galfitControl->InputDataImage->setCurrentText(QString(fitsMainWindow->getCurrentBufferIndex().c_str()));
    newInputDataImage(QString(fitsMainWindow->getCurrentBufferIndex().c_str()));
    galfitControl->SigmaImage->clear();
    galfitControl->SigmaImage->addItem("none");
    galfitControl->SigmaImage->addItems(buffList);
    galfitControl->SigmaImage->setCurrentText("none");
    galfitControl->PSFImage->clear();
    galfitControl->PSFImage->addItem("none");
    galfitControl->PSFImage->addItems(buffList);
    galfitControl->PSFImage->setCurrentText("none");
    galfitControl->BadPixelMask->clear();
    galfitControl->BadPixelMask->addItem("none");
    galfitControl->BadPixelMask->addItems(buffList);
    galfitControl->BadPixelMask->setCurrentText("none");

    show();
}

void dpGalfitDialog::addSky(double background, double dx, double dy) {
//    QWidget *obj = new QWidget();
    dpGalfitSky *sky = new dpGalfitSky();
//    sky->obj = new QWidget();
//    sky->setupUi(sky);
    sky->setValues(background, dx, dy);
    objects->setCurrentIndex(objects->addTab(sky, "Sky"));
}

void dpGalfitDialog::addGaussian(double x, double y, double m, double f, double r, double a) {
    double x_cen = x;
    double y_cen = y;

    if (x == 1 && y == 1) {
        dpGalfitGaussian *g = dynamic_cast<dpGalfitGaussian *>(objects->currentWidget());
        if (g != NULL) {
            x_cen = g->posx->value();
            y_cen = g->posy->value();
        } else {
            dpGalfitSersic *s = dynamic_cast<dpGalfitSersic *>(objects->currentWidget());
            if (s != NULL) {
                x_cen = s->posx->value();
                y_cen = s->posy->value();
            } else {
                x_cen = galfitControl->xmax->value() / 2.0 + 1.0;
                y_cen = galfitControl->ymax->value() / 2.0 + 1.0;
            }
        }
    }
//    QWidget *obj = new QWidget();
    dpGalfitGaussian *gauss = new dpGalfitGaussian();
//    gauss->setupUi(obj);
    gauss->setValues(x_cen, y_cen, m, f, r, a);
    connect(gauss->hidden->newBuffer, SIGNAL(clicked()), this, SLOT(newBufferComponentClicked()));
    objects->setCurrentIndex(objects->addTab(gauss, "Gaussian"));
}

void dpGalfitDialog::addSersic(double x, double y, double m, double e, double i, double r, double a, bool sersic_fixed) {
    double x_cen = x;
    double y_cen = y;

    if (x == 1 && y == 1) {
        dpGalfitSersic *s = dynamic_cast<dpGalfitSersic *>(objects->currentWidget());
        if (s != NULL) {
            x_cen = s->posx->value();
            y_cen = s->posy->value();
        } else {
            dpGalfitGaussian *g = dynamic_cast<dpGalfitGaussian *>(objects->currentWidget());
            if (g != NULL) {
                x_cen = g->posx->value();
                y_cen = g->posy->value();
            } else {
                x_cen = galfitControl->xmax->value() / 2.0 + 1.0;
                y_cen = galfitControl->ymax->value() / 2.0 + 1.0;
            }
        }
    }
//    QWidget *obj = new QWidget();
    dpGalfitSersic *sersic = new dpGalfitSersic();
//    gauss->setupUi(obj);
    sersic->setValues(x_cen, y_cen, m, e, i, r, a);
    if (sersic_fixed) sersic->sersic->fixed->setChecked(true);
    connect(sersic->hidden->newBuffer, SIGNAL(clicked()), this, SLOT(newBufferComponentClicked()));
    objects->setCurrentIndex(objects->addTab(sersic, "Sersic"));
}

void dpGalfitDialog::clearObjects() {
    for (int i = objects->count() - 1; i > 0; i--) delete objects->widget(i);
}

void dpGalfitDialog::closeObject(int i) {
    delete objects->widget(i);
}

void dpGalfitDialog::newInputDataImage(const QString &which) {
    QFitsSingleBuffer *sb = dynamic_cast<QFitsSingleBuffer *>(fitsMainWindow->main_view->getBuffer(which.toStdString()));
    if (sb != NULL) {
        galfitControl->xmin->setValue(1);
        galfitControl->xmax->setMaximum(sb->getDpData()->fvalue->Naxis(1));
        galfitControl->xmax->setValue(sb->getDpData()->fvalue->Naxis(1));
        galfitControl->ymin->setValue(1);
        galfitControl->ymax->setMaximum(sb->getDpData()->fvalue->Naxis(2));
        galfitControl->ymax->setValue(sb->getDpData()->fvalue->Naxis(2));

        double exptime = 1.;
        if (sb->getDpData()->fvalue->GetFloatKey("EXPTIME", &exptime)) galfitControl->exptime->setValue(exptime);
        double gain = 7.;
        if (sb->getDpData()->fvalue->GetFloatKey("GAIN", &gain)) galfitControl->gain->setValue(gain);
        int ncombine = 1;
        if (sb->getDpData()->fvalue->GetIntKey("NCOMBINE", &ncombine)) galfitControl->ncombine->setValue(ncombine);
    }
}

void dpGalfitDialog::runGalfit() {
    if (settings.galfitPath.isEmpty()) {
        QMessageBox::critical(this, "QFitsView", "Need to set path to galfit executable first!");
        return;
    }
    QProcess process;
    QTemporaryDir dir("galfit");
    if (!dir.isValid()) {
        QMessageBox::critical(this, "QFitsView", "Could not create temporary files to run galfit!");
        return;
    }
    // write out FITS files for galfit input
    dpString origFname; // since these are temporary files, we don't save the file name in the FITS class
    QFitsSingleBuffer *sb = dynamic_cast<QFitsSingleBuffer *>(fitsMainWindow->main_view->getBuffer(galfitControl->InputDataImage->currentText().toStdString()));
    if (sb != NULL) {
        if (galfitControl->exptime->value() != 1.0) sb->getDpData()->fvalue->SetFloatKey("EXPTIME", galfitControl->exptime->value());
        if (galfitControl->gain->value() != 7.0) sb->getDpData()->fvalue->SetFloatKey("GAIN", galfitControl->gain->value());
        if (galfitControl->ncombine->value() != 1) sb->getDpData()->fvalue->SetIntKey("NCOMBINE", galfitControl->ncombine->value());
        origFname = sb->getDpData()->fvalue->getFileName();
        sb->getDpData()->fvalue->WriteFITS(dir.filePath("input.fits").toStdString().c_str());
        sb->getDpData()->fvalue->setFileName(origFname.c_str());
    } else {
        QMessageBox::critical(this, "QFitsView", "Input buffer is not a FITS!");
        return;
    }
    if (galfitControl->SigmaImage->currentText() != "none") {
        sb = dynamic_cast<QFitsSingleBuffer *>(fitsMainWindow->main_view->getBuffer(galfitControl->SigmaImage->currentText().toStdString()));
        if (sb != NULL) {
            origFname = sb->getDpData()->fvalue->getFileName();
            sb->getDpData()->fvalue->WriteFITS(dir.filePath("sigma.fits").toStdString().c_str());
            sb->getDpData()->fvalue->setFileName(origFname.c_str());
        }
    }
    if (galfitControl->PSFImage->currentText() != "none") {
        sb = dynamic_cast<QFitsSingleBuffer *>(fitsMainWindow->main_view->getBuffer(galfitControl->PSFImage->currentText().toStdString()));
        if (sb != NULL) {
            origFname = sb->getDpData()->fvalue->getFileName();
            sb->getDpData()->fvalue->WriteFITS(dir.filePath("psf.fits").toStdString().c_str());
            sb->getDpData()->fvalue->setFileName(origFname.c_str());
        }
    }
    if (galfitControl->BadPixelMask->currentText() != "none") {
        sb = dynamic_cast<QFitsSingleBuffer *>(fitsMainWindow->main_view->getBuffer(galfitControl->BadPixelMask->currentText().toStdString()));
        if (sb != NULL) {
            origFname = sb->getDpData()->fvalue->getFileName();
            sb->getDpData()->fvalue->WriteFITS(dir.filePath("mask.fits").toStdString().c_str());
            sb->getDpData()->fvalue->setFileName(origFname.c_str());
        }
    }
    QStringList feedme;
    feedme << galfitControl->getParameters();
    for (int i = 0; i < objects->count(); i++) {
        dpGalfitComponent *comp = dynamic_cast<dpGalfitComponent *>(objects->widget(i));
        if (comp != NULL) feedme << comp->getParameters();
    }

    QFile feedmefile(dir.filePath("galfit.feedme"));
    feedmefile.open(QIODevice::WriteOnly);
    QTextStream feedmeout(&feedmefile);
    feedmeout << feedme.join("\n");
    feedmeout << "\n";
    feedmefile.close();

    QStringList constraints;
    for (int i = 0; i < objects->count(); i++) {
        dpGalfitComponent *comp = dynamic_cast<dpGalfitComponent *>(objects->widget(i));
        if (comp != NULL) constraints << comp->getConstraints(i);
    }
    QFile constraintsfile(dir.filePath("constraints"));
    constraintsfile.open(QIODevice::WriteOnly);
    QTextStream constraintsout(&constraintsfile);
    constraintsout << constraints.join("\n");
    constraintsout << "\n";
    constraintsfile.close();

    QString result;
    process.setWorkingDirectory(dir.path());
    process.start(settings.galfitPath, { "galfit.feedme" });
    if (!process.waitForStarted(5000)) {
        QMessageBox::critical(this, "QFitsView", "Could not start galfit executable!");
        process.kill();
        return;
    }
    QApplication::setOverrideCursor(Qt::WaitCursor);
    process.waitForFinished();
    QApplication::restoreOverrideCursor();
    result.append(process.readAllStandardOutput().constData());
    result.append(process.readAllStandardError().constData());

    QFile logfile(dir.filePath("galfit.01"));

    logfile.open(QIODevice::ReadOnly);
    result.append(logfile.readAll().constData());
    logfile.close();

    int e = process.exitCode();
    output->setText(result);
    output->verticalScrollBar()->setValue(output->verticalScrollBar()->maximum() * 2);

    Fits rv;
    if (!rv.ReadFITSExtension(dir.filePath("result.fits").toStdString().c_str(), 2)) {
        QMessageBox::critical(this, "QFitsView", "galfit crashed!");
        return;
    }

    for (int i = 0; i < objects->count(); i++) {
        dpGalfitComponent *comp = dynamic_cast<dpGalfitComponent *>(objects->widget(i));
        if (comp != NULL) comp->setResult(i, rv);
    }

//    QString bufName = freeBufferName().c_str();
//    injectVariable(bufName, rv);

//    QString cmd = "view " + bufName;
//    dpuser_widget->executeCommand(cmd);
}

void dpGalfitDialog::newBufferClicked() {
    newBufferFromComponent(-1);
}

void dpGalfitDialog::newBufferComponentClicked() {
    newBufferFromComponent(objects->currentIndex());
}

void dpGalfitDialog::newBufferFromComponent(int component) {
    if (settings.galfitPath.isEmpty()) {
        QMessageBox::critical(this, "QFitsView", "Need to set path to galfit executable first!");
        return;
    }
    QProcess process;
    QTemporaryDir dir("galfit");
    if (!dir.isValid()) {
        QMessageBox::critical(this, "QFitsView", "Could not create temporary files to run galfit!");
        return;
    }
    QStringList feedme;
    feedme << galfitControl->getParameters(true);

    if (component > 0) {
        dpGalfitComponent *comp = dynamic_cast<dpGalfitComponent *>(objects->widget(component));
        if (comp != NULL) feedme << comp->getParameters(true);
    } else {
        for (int i = 0; i < objects->count(); i++) {
            dpGalfitComponent *comp = dynamic_cast<dpGalfitComponent *>(objects->widget(i));
            if (comp != NULL) feedme << comp->getParameters();
        }
    }
    QFile feedmefile(dir.filePath("galfit.feedme"));
    feedmefile.open(QIODevice::WriteOnly);
    QTextStream feedmeout(&feedmefile);
    feedmeout << feedme.join("\n");
    feedmeout << "\n";
    feedmefile.close();
    process.setWorkingDirectory(dir.path());
    process.start(settings.galfitPath, { "galfit.feedme" });
    if (!process.waitForStarted(5000)) {
        QMessageBox::critical(this, "QFitsView", "Could not start galfit executable!");
        process.kill();
        return;
    }
    QApplication::setOverrideCursor(Qt::WaitCursor);
    process.waitForFinished();
    QApplication::restoreOverrideCursor();
    Fits rv;
    if (!rv.ReadFITS(dir.filePath("result.fits").toStdString().c_str())) {
        QMessageBox::critical(this, "QFitsView", "galfit crashed!");
        return;
    }
    rv.setFileName("");
    QString bufName = freeBufferName().c_str();
    injectVariable(bufName, rv);

    QString cmd = "view " + bufName;
    dpuser_widget->executeCommand(cmd);
}



