#include <QPushButton>
#include <QComboBox>
#include <QLineEdit>
#include <QSpinBox>
#include <QCheckBox>
#include <QFileDialog>
#include <QApplication>
#include <QMessageBox>
#include <QPainter>
#include <QMouseEvent>
#include <QResizeEvent>
#include <QScrollBar>
#include <QClipboard>
#include <QGridLayout>
#include <QGroupBox>

#include "RGBDialog.h"
#include "dialogs.h"
#include "QFitsMainWindow.h"
#include "QFitsMainView.h"
#include "QFitsSingleBuffer.h"
#include "QFitsWidgetContour.h"
#include "lut.h"

QFitsDisplay::QFitsDisplay(QWidget *parent) : QWidget(parent) {
    data.create(100, 100);
    scalemin        = 0.;
    scalemax        = 0.;
    ignoreInCube    = false;
    image           = NULL;
    zoom            = zoomIndex;
    mx              = 0;
    my              = 0;
    brightness      = 500;
    contrast        = 500;
    colourmap       = 1;
    invertColourmap = false;
    autoScale       = true;
    scaling         = 0;
    ignoreValue     = -1e10;
    xoffset         = 0;
    yoffset         = 0;

    setFocusPolicy(Qt::StrongFocus);
}

void QFitsDisplay::paintEvent(QPaintEvent *p) {
    QPainter pa(this);
    pa.translate(xoffset, yoffset);
    pa.drawImage(0, 0, *image);
}

void QFitsDisplay::resizeEvent(QResizeEvent*) {
    emit recenter();
}

void QFitsDisplay::mouseMoveEvent(QMouseEvent *m) {
    if (m->buttons() == Qt::LeftButton) {
        setBrightnessContrast((int)((float)m->x() / (float)width() * 1000.), (int)((float)m->y() / (float)height() * 1000.));
    }
}

bool QFitsDisplay::LoadFile(char *fname, int extension) {
    QApplication::setOverrideCursor(Qt::WaitCursor);

    if (extension != 0) {
        if (data.ReadFITSExtension(fname, extension)) {
            setScaleMinMax(0.0, 0.0);
            QApplication::restoreOverrideCursor();
        } else {
            QApplication::restoreOverrideCursor();
            QMessageBox::critical(0, "FitsView", QString(fname) + " is not a valid FITS file with extension");
            return false;
        }
    } else if (data.ReadFITS(fname)) {
        setScaleMinMax(0.0, 0.0);
        QApplication::restoreOverrideCursor();
    } else {
        QImage newimage;
        if (!newimage.load(fname)) {
            QApplication::restoreOverrideCursor();
            QApplication::beep();
            QMessageBox::critical(0, "FitsView", QString(fname) + " is not a valid image");
            return false;
        } else {
            int x, y, n = 0;

            data.create(newimage.height(), newimage.width(), I1);
            for (x = 0; x < newimage.width(); x++) {
                for (y = 0; y < newimage.height(); y++) {
                    data.i1data[n++] = qGray(newimage.pixel(x, y));
                }
            }
            data.rot90(90);
            setScaleMinMax(0.0, 0.0);
            QApplication::restoreOverrideCursor();
        }
    }
    emit fileChanged(QString(fname));

    return true;
}

void QFitsDisplay::ScaleImage() {
    QApplication::setOverrideCursor(Qt::WaitCursor);
    work.copy(data);
    if (scalemin == scalemax) {
        if (ignoreInCube) {
             work.get_minmax(&scalemin, &scalemax, ignoreValue);
        } else {
            work.get_minmax(&scalemin, &scalemax);
        }
        emit newMinMax(scalemin, scalemax);
    } else {
        emit newMinMax(scalemin, scalemax);
        work.clip(scalemin, scalemax);
    }

    double  wmin = 0.,
            wmax = 0.;
    switch (scaling) {
        case 0: // linear
            wmin = scalemin;
            wmax = scalemax;
            break;
        case 1: // log
            work -= scalemin - 0.01;
            work.Log();
            work.get_minmax(&wmin, &wmax);
            break;
        case 2: // sqrt
            work -= scalemin - 0.01;
            work.Sqrt();
            work.get_minmax(&wmin, &wmax);
            break;
        case 3: // histeq
            work.histeq(data, scalemin, scalemax);
            break;
        default:
            break;
    }

    if (scaling != 3) {
        work -= wmin;
        work /= (wmax - wmin) / 250.;
        work.clip(0., 255.);
        work.setType(I1);
    }

    if (image != NULL) {
        delete image;
    }
    if (zoom > zoomIndex) {
        image = new QImage(work.Naxis(1) * (zoom - (zoomIndex-1)),
                           work.Naxis(2) * (zoom - (zoomIndex-1)),
                           QImage::Format_Indexed8);
    } else if (zoom < zoomIndex) {
        image = new QImage(work.Naxis(1) / (zoomIndex+1 - zoom),
                           work.Naxis(2) / (zoomIndex+1- zoom),
                           QImage::Format_Indexed8);
    } else {
        image = new QImage(work.Naxis(1),
                           work.Naxis(2),
                           QImage::Format_Indexed8);
    }
    image->setColorCount(NCOLORS);

    unsigned char *i1data = work.i1data;
    if (zoom == zoomIndex) {
        for (int y = image->height() - 1; y >= 0; y--) { // set image pixels
            uchar *p = image->scanLine(y);
            for (int x = 0; x < image->width(); x++) {
                *p++ = *(i1data++);
            }
        }
    } else if (zoom > zoomIndex) {
        for (int y = image->height() - 1, yy = 0; y >= 0; y--, yy++) {                 // set image pixels
            uchar *p = image->scanLine(y);
            for (int x = 0; x < image->width(); x++) {
                *p++ = i1data[work.C_I(x / (zoom - (zoomIndex-1)), yy / (zoom - (zoomIndex-1)))];
            }
        }
    } else if (zoom < zoomIndex) {
        for (int y = image->height() - 1, yy = 0; y >= 0; y--, yy++) {                 // set image pixels
            uchar *p = image->scanLine(y);
            for (int x = 0; x < image->width(); x++) {
                long value = 0;
                for (int x1 = x * (zoomIndex+1 - zoom); x1 < (x + 1) * (zoomIndex+1 - zoom); x1++) {
                    for (int y1 = yy * (zoomIndex+1 - zoom); y1 < (yy + 1) * (zoomIndex+1 - zoom); y1++) {
                        value += i1data[work.C_I(x1, y1)];
                    }
                }
                *p++ = (unsigned char)(value / ((zoomIndex+1 - zoom) * (zoomIndex+1 - zoom)));
            }
        }
    }
    setupColours();

    QApplication::restoreOverrideCursor();
}

void QFitsDisplay::setBrightnessContrast(int b, int c) {
    emit(newBrightness(b));
    emit(newContrast(c));
    if ((b != brightness) || (c != contrast)) {
        brightness = b;
        contrast = c;
        setupColours();
    }
}

void QFitsDisplay::setBrightness(int b) {
    if (b != brightness) {
        setBrightnessContrast(b, contrast);
    }
}

void QFitsDisplay::setContrast(int c) {
    if (c != contrast) {
        setBrightnessContrast(brightness, c);
    }
}

void QFitsDisplay::setupColours(void) {
    int x       = brightness,
        y       = contrast,
        w       = 0,
        top     = 0,
        bottom  = 0;

    if ((x == -1) && (y == -1)) {
        w       = 255;
        bottom  = 0;
        top     = 256;
    } else if ((x >= 0) && (x < 1000) && (y >= 0) && (y < 1000)) {
        w       = (1000 - y) * 255 * 2 / 1000;
        int mid = (1000 - x) * 255 / 1000;
        bottom  = mid - (w / 2);
        top     = mid + (w / 2);
    } else {
        return;
    }

    brightness = x;
    contrast = y;
    for (int i = bottom; i < top; i++) {
        int value = (i - bottom) * 255 / w;
        if ((value > -1) && (value < 256) && (i > -1) && (i < 256)) {
            switch (colourmap) {
                case -1:
                    colourTable[i] = currentTable[value];
                    break;
                case 1:
                    if (value < 128) {
                        colourTable[i] = qRgb(value, 0, 0);
                    } else if (value < 192) {
                        colourTable[i] = qRgb(value, (value - 128) * 2, 0);
                    } else {
                        colourTable[i] = qRgb(value, (value - 128) * 2, (value - 192) * 3);
                    }
                    break;
                case 2:
                    if (value < 64) {
                        colourTable[i] = qRgb(0, 0, value * 4);
                    } else if (value < 128) {
                        colourTable[i] = qRgb((value - 64) * 4 + 1, 0, (128 - value) * 4 - 1);
                    } else if (value < 192) {
                        colourTable[i] = qRgb(255, (value - 128) * 4, 0);
                    } else {
                        colourTable[i] = qRgb(255, 255, (value - 192) * 4);
                    }
                    break;
                case 3:
                    colourTable[i] = qRgb(value, 0, 0);
                    break;
                case 4:
                    colourTable[i] = qRgb(0, value, 0);
                    break;
                case 5:
                    colourTable[i] = qRgb(0, 0, value);
                    break;
                default:
                    colourTable[i] = qRgb(value, value, value);
                    break;
            }
        }
    }
    if (top < 255) {
        QRgb v = qRgb(255, 255, 255);
        if (colourmap == -1) {
            v = currentTable[255];
        } else if (colourmap == 3) {
            v = qRgb(255, 0, 0);
        } else if (colourmap == 4) {
            v = qRgb(0, 255, 0);
        } else if (colourmap == 5) {
            v = qRgb(0, 0, 255);
        }
        for (int i = top; i <= 255; i++) {
            colourTable[i] = v;
        }
    }

    if (bottom > 0) {
        QRgb v = qRgb(0, 0, 0);
        if (colourmap == -1) {
            v = currentTable[0];
        }
        for (int i = 0; i < bottom; i++) {
            colourTable[i] = v;
        }
    }

    if (invertColourmap) {
        for (int i = 0; i < 128; i++) {
            QRgb s = colourTable[i];
            colourTable[i] = colourTable[255 - i];
            colourTable[255 - i] = s;
        }
    }

    emit colourtableChanged();
    newColourtable();
}

void QFitsDisplay::setScaling(int scaleType) {
    scaling = scaleType;
    ScaleImage();
}

void QFitsDisplay::setColourmap(int c) {
    if (c < 0) {
        unsigned char *map = colormaps + (-c - 1) * 256 * 3;
        for (int i = 0; i < 256*3; i+=3) {
            currentTable[i/3] = qRgb((int)map[i], (int)map[i+1], (int)map[i+2]);
        }
        colourmap = -1;
    } else if (c == 10) {
        invertColourmap = !invertColourmap;
    } else {
        colourmap = c;
    }
    ScaleImage();
}

void QFitsDisplay::setInvertColourMap(bool i) {
    invertColourmap = i;
    ScaleImage();
}

void QFitsDisplay::setZoom(int newZoom) {
    zoom = newZoom;
    ScaleImage();
}

void QFitsDisplay::incZoom() {
    zoom++;
    ScaleImage();
}

void QFitsDisplay::decZoom() {
    zoom--;
    ScaleImage();
}

void QFitsDisplay::setScaleMin(double newmin) {
    scalemin = newmin;
    ScaleImage();
}

void QFitsDisplay::setScaleMax(double newmax) {
    scalemax = newmax;
    ScaleImage();
}

void QFitsDisplay::setScaleMinMax(double newmin, double newmax) {
    scalemin = newmin;
    scalemax = newmax;
    ScaleImage();
}

void QFitsDisplay::newColourtable() {
    for (int i = 0; i < NCOLORS; i++) {
        image->setColor(i, colourTable[i]);
    }

    update(visibleRegion());
}

void QFitsDisplay::setIgnore(bool ignore, double value) {
    ignoreInCube = ignore;
    ignoreValue = value;
}

QFitsDisplayWidget::QFitsDisplayWidget(QWidget *parent) : QWidget(parent) {
    viewer = new QFitsDisplay(this);
    connect(viewer, SIGNAL(colourtableChanged()),
            this, SLOT(newColourtable()));
    connect(viewer, SIGNAL(recenter()),
            this, SLOT(recenterViewer()));
}

void QFitsDisplayWidget::resizeEvent(QResizeEvent *r) {
    viewer->setGeometry(0, 0, r->size().width(), r->size().height());
    recenterViewer();
}

void QFitsDisplayWidget::LoadClicked() {
    QStringList types;
    types << "All files (*)" << "Fits files (*.fits *.fts *.fits.gz *.fts.gz)" << "Bitmaps (*.bmp *.png *.jpg *.jpeg)";

    QFileDialog *dialog = new QFileDialog(this, "QFitsView - Open File", settings.lastOpenPath);
    dialog->setNameFilters(types);

    if (dialog->exec()) {
        QStringList fnames = dialog->selectedFiles();
        if (!fnames.isEmpty()) {
            for (int i = 0; i < fnames.size(); ++i) {
                QDir dir;
                settings.lastOpenPath = dir.filePath(fnames.at(i));
                QApplication::setOverrideCursor(Qt::WaitCursor);
                viewer->LoadFile((char *)fnames.at(i).toStdString().c_str());
                QApplication::restoreOverrideCursor();
            }
        }
    }
}

void QFitsDisplayWidget::recenterViewer() {
}

ResultWidget::ResultWidget(QWidget *parent) : QWidget(parent) {
    result = new QImage();
}

void ResultWidget::paintEvent(QPaintEvent *p) {
    resize(result->width(), result->height());
    QPainter pa(this);
    pa.drawImage(0, 0, *result);
}

RGBDialog::RGBDialog(QWidget *parent) : QDialog(parent) {
    r = new QFitsDisplayWidget(this);
    r->setGeometry(0, 00, 300, 300);
    r->setColourmap(3);

    g = new QFitsDisplayWidget(this);
    g->setGeometry(300, 0, 300, 300);
    g->setColourmap(4);

    b = new QFitsDisplayWidget(this);
    b->setGeometry(600, 0, 300, 300);
    b->setColourmap(5);

    resultscroller = new QScrollArea(this);
    resultscroller->setWidgetResizable(true);
    resultscroller->setGeometry(0, 330, 300, 300);
    resultviewer = new QLabel();
    resultscroller->setWidget(resultviewer);

    options = new QWidget(this);
    QGridLayout *mainLayout = new QGridLayout();
    options->setLayout(mainLayout);
    options->move(310, 310);

    /////////////////////////////////////////////////////////////////////
    // Create & setup options
    //
    options->resize(582, 330);
    ZoomOutButton = new QPushButton("Zoom Out", options);
    ZoomOutButton->setGeometry(QRect(500, 110, 80, 30));
    ZoomOutButton->setDefault(false);
    ZoomInButton = new QPushButton("Zoom in", options);
    ZoomInButton->setGeometry(QRect(420, 110, 80, 30));
    ZoomInButton->setDefault(false);
    AlignButton = new QPushButton("Align Images", options);
    AlignButton->setGeometry(QRect(420, 150, 160, 30));
    AlignButton->setDefault(false);
    SaveButton = new QPushButton("Save Image...", options);
    SaveButton->setEnabled(false);
    SaveButton->setGeometry(QRect(420, 180, 160, 30));
    SaveButton->setDefault(false);
    CloseButton = new QPushButton("Close", options);
    CloseButton->setGeometry(QRect(420, 260, 160, 30));
    CloseButton->setDefault(false);
    QPushButton *dummyButton = new QPushButton("", options);
    dummyButton->setGeometry(0, 0, 0, 0);
    dummyButton->setVisible(false);
    dummyButton->setDefault(true);
    GroupBox1_2 = new QGroupBox(options);
    GroupBox1_2->setGeometry(QRect(0, 110, 420, 110));
    QLabel *TextLabel4_3_2 = new QLabel(GroupBox1_2);
    TextLabel4_3_2->setGeometry(QRect(20, 80, 40, 20));
    TextLabel4_3_2->setText("X shift:");
    QLabel *TextLabel2_2_2 = new QLabel(GroupBox1_2);
    TextLabel2_2_2->setGeometry(QRect(20, 50, 50, 20));
    TextLabel2_2_2->setText("Scaling:");
    QLabel *TextLabel1_2_2 = new QLabel(GroupBox1_2);
    TextLabel1_2_2->setGeometry(QRect(20, 20, 50, 20));
    TextLabel1_2_2->setText("Buffer:");
    GreenActive = new QCheckBox("Active", GroupBox1_2);
    GreenActive->setGeometry(QRect(340, 20, 70, 20));
    GreenActive->setChecked(true);
    GreenMax = new QLineEdit(GroupBox1_2);
    GreenMax->setGeometry(QRect(330, 50, 80, 20));
    QLabel *TextLabel3_2_2_2 = new QLabel(GroupBox1_2);
    TextLabel3_2_2_2->setGeometry(QRect(290, 50, 40, 20));
    TextLabel3_2_2_2->setAlignment(Qt::AlignCenter);
    TextLabel3_2_2_2->setText("Max:");
    GreenMin = new QLineEdit(GroupBox1_2);
    GreenMin->setGeometry(QRect(210, 50, 80, 20));
    QLabel *TextLabel3_3_2 = new QLabel(GroupBox1_2);
    TextLabel3_3_2->setGeometry(QRect(170, 50, 40, 20));
    TextLabel3_3_2->setAlignment(Qt::AlignCenter);
    TextLabel3_3_2->setText("Min:");
    GreenScaling = new QComboBox(GroupBox1_2);
    GreenScaling->setGeometry(QRect(70, 50, 90, 20));
    QStringList *tmpList = new QStringList();
    tmpList->append("linear");
    tmpList->append("log");
    tmpList->append("sqrt");
    tmpList->append("histeq");
    GreenScaling->insertItems(0, *tmpList);
    QLabel *TextLabel4_2_2_2 = new QLabel(GroupBox1_2);
    TextLabel4_2_2_2->setGeometry(QRect(140, 80, 49, 20));
    TextLabel4_2_2_2->setText(" Y shift:");
    GreenXShift = new QSpinBox(GroupBox1_2);
    GreenXShift->setGeometry(QRect(70, 80, 70, 20));
    GreenXShift->setMaximum(32000);
    GreenXShift->setMinimum(-32000);
    GreenMap = new QComboBox(GroupBox1_2);
    GreenMap->setGeometry(QRect(340, 80, 70, 20));
    tmpList = new QStringList();
    tmpList->append("gray");
    tmpList->append("bb");
    tmpList->append("rainbow");
    tmpList->append("red");
    tmpList->append("green");
    tmpList->append("blue");
    GreenMap->insertItems(0, *tmpList);

    GreenMap->setCurrentIndex(4);

    QLabel *TextLabel1_2_4 = new QLabel(GroupBox1_2);
    TextLabel1_2_4->setGeometry(QRect(260, 80, 73, 20));
    TextLabel1_2_4->setText("Colourmap:");
    GreenYShift = new QSpinBox(GroupBox1_2);
    GreenYShift->setGeometry(QRect(190, 80, 70, 20));
    GreenYShift->setMaximum(32000);
    GreenYShift->setMinimum(-32000);
    GreenFileButton = new QPushButton("...", GroupBox1_2);
    GreenFileButton->setGeometry(QRect(290, 16, 40, 30));
    GreenFileName = new QLineEdit(GroupBox1_2);
    GreenFileName->setGeometry(QRect(70, 20, 211, 21));
    GroupBox1 = new QGroupBox(options);
    GroupBox1->setGeometry(QRect(0, 0, 420, 110));
    QLabel *TextLabel1 = new QLabel(GroupBox1);
    TextLabel1->setGeometry(QRect(20, 20, 50, 20));
    TextLabel1->setText("Buffer:");
    QLabel *TextLabel2 = new QLabel(GroupBox1);
    TextLabel2->setGeometry(QRect(20, 50, 50, 20));
    TextLabel2->setText("Scaling:");
    QLabel *TextLabel1_2 = new QLabel(GroupBox1);
    TextLabel1_2->setGeometry(QRect(260, 80, 73, 20));
    TextLabel1_2->setText("Colourmap:");
    QLabel *TextLabel4 = new QLabel(GroupBox1);
    TextLabel4->setGeometry(QRect(20, 80, 40, 20));
    TextLabel4->setText("X shift:");
    RedActive = new QCheckBox("Active", GroupBox1);
    RedActive->setGeometry(QRect(340, 20, 70, 20));
    RedActive->setChecked(true);
    RedMax = new QLineEdit(GroupBox1);
    RedMax->setGeometry(QRect(330, 50, 80, 20));
    QLabel *TextLabel3_2 = new QLabel(GroupBox1);
    TextLabel3_2->setGeometry(QRect(290, 50, 40, 20));
    TextLabel3_2->setAlignment(Qt::AlignCenter);
    TextLabel3_2->setText("Max:");
    RedMin = new QLineEdit(GroupBox1);
    RedMin->setGeometry(QRect(210, 50, 80, 20));
    QLabel *TextLabel3 = new QLabel(GroupBox1);
    TextLabel3->setGeometry(QRect(170, 50, 40, 20));
    TextLabel3->setAlignment(Qt::AlignCenter);
    TextLabel3->setText("Min:");
    RedScaling = new QComboBox(GroupBox1);
    RedScaling->setGeometry(QRect(70, 50, 90, 20));
    tmpList = new QStringList();
    tmpList->append("linear");
    tmpList->append("log");
    tmpList->append("sqrt");
    tmpList->append("histeq");
    RedScaling->insertItems(0, *tmpList);
    RedFileButton = new QPushButton("...", GroupBox1);
    RedFileButton->setGeometry(QRect(290, 16, 40, 30));
    QLabel *TextLabel4_2 = new QLabel(GroupBox1);
    TextLabel4_2->setGeometry(QRect(140, 80, 49, 20));
    TextLabel4_2->setText(" Y shift:");
    RedXShift = new QSpinBox(GroupBox1);
    RedXShift->setGeometry(QRect(70, 80, 70, 20));
    RedXShift->setMaximum(32000);
    RedXShift->setMinimum(-32000);
    RedYShift = new QSpinBox(GroupBox1);
    RedYShift->setGeometry(QRect(190, 80, 70, 20));
    RedYShift->setMaximum(32000);
    RedYShift->setMinimum(-32000);
    RedMap = new QComboBox(GroupBox1);
    RedMap->setGeometry(QRect(340, 80, 70, 20));
    tmpList = new QStringList();
    tmpList->append("gray");
    tmpList->append("bb");
    tmpList->append("rainbow");
    tmpList->append("red");
    tmpList->append("green");
    tmpList->append("blue");
    RedMap->insertItems(0, *tmpList);
    RedMap->setCurrentIndex(3);
    RedFileName = new QLineEdit(GroupBox1);
    RedFileName->setGeometry(QRect(70, 20, 211, 21));
    GroupBox1_2_2 = new QGroupBox(options);
    GroupBox1_2_2->setGeometry(QRect(0, 220, 420, 110));
    BlueXShift = new QSpinBox(GroupBox1_2_2);
    BlueXShift->setGeometry(QRect(70, 80, 70, 20));
    BlueXShift->setMaximum(32000);
    BlueXShift->setMinimum(-32000);
    QLabel *TextLabel4_3_3 = new QLabel(GroupBox1_2_2);
    TextLabel4_3_3->setGeometry(QRect(20, 80, 40, 20));
    TextLabel4_3_3->setText("X shift:");
    QLabel *TextLabel2_2_3 = new QLabel(GroupBox1_2_2);
    TextLabel2_2_3->setGeometry(QRect(20, 50, 50, 20));
    TextLabel2_2_3->setText("Scaling:");
    BlueMap = new QComboBox(GroupBox1_2_2);
    BlueMap->setGeometry(QRect(340, 80, 70, 20));
    tmpList = new QStringList();
    tmpList->append("gray");
    tmpList->append("bb");
    tmpList->append("rainbow");
    tmpList->append("red");
    tmpList->append("green");
    tmpList->append("blue");
    BlueMap->insertItems(0, *tmpList);

    BlueMap->setCurrentIndex(5);
    BlueYShift = new QSpinBox(GroupBox1_2_2);
    BlueYShift->setGeometry(QRect(190, 80, 70, 20));
    BlueYShift->setMaximum(32000);
    BlueYShift->setMinimum(-32000);
    QLabel *TextLabel4_2_2_3 = new QLabel(GroupBox1_2_2);
    TextLabel4_2_2_3->setGeometry(QRect(140, 80, 49, 20));
    TextLabel4_2_2_3->setText(" Y shift:");
    QLabel *TextLabel1_2_5 = new QLabel(GroupBox1_2_2);
    TextLabel1_2_5->setGeometry(QRect(260, 80, 73, 20));
    TextLabel1_2_5->setText("Colourmap:");
    BlueMax = new QLineEdit(GroupBox1_2_2);
    BlueMax->setGeometry(QRect(330, 50, 80, 20));
    QLabel *TextLabel3_2_2_3 = new QLabel(GroupBox1_2_2);
    TextLabel3_2_2_3->setGeometry(QRect(290, 50, 40, 20));
    TextLabel3_2_2_3->setAlignment(Qt::AlignCenter);
    TextLabel3_2_2_3->setText("Max:");
    BlueMin = new QLineEdit(GroupBox1_2_2);
    BlueMin->setGeometry(QRect(210, 50, 80, 20));
    QLabel *TextLabel3_3_3 = new QLabel(GroupBox1_2_2);
    TextLabel3_3_3->setGeometry(QRect(170, 50, 40, 20));
    TextLabel3_3_3->setAlignment(Qt::AlignCenter);
    TextLabel3_3_3->setText("Min:");
    BlueScaling = new QComboBox(GroupBox1_2_2);
    BlueScaling->setGeometry(QRect(70, 50, 90, 20));
    tmpList = new QStringList();
    tmpList->append("linear");
    tmpList->append("log");
    tmpList->append("sqrt");
    tmpList->append("histeq");
    BlueScaling->insertItems(0, *tmpList);
    QLabel *TextLabel1_2_3 = new QLabel(GroupBox1_2_2);
    TextLabel1_2_3->setGeometry(QRect(20, 20, 41, 20));
    TextLabel1_2_3->setText("Buffer:");
    BlueActive = new QCheckBox("Active", GroupBox1_2_2);
    BlueActive->setGeometry(QRect(340, 20, 70, 20));
    BlueActive->setChecked(true);
    BlueFileButton = new QPushButton("...", GroupBox1_2_2);
    BlueFileButton->setGeometry(QRect(290, 16, 40, 30));
    BlueFileName = new QLineEdit(GroupBox1_2_2);
    BlueFileName->setGeometry(QRect(70, 20, 211, 21));



    connect(RedFileButton, SIGNAL(clicked()), r, SLOT(LoadClicked()));
    connect(RedFileName, SIGNAL(returnPressed()), this, SLOT(LoadRed()));
    connect(r->viewer, SIGNAL(fileChanged(const QString &)), RedFileName, SLOT(setText(const QString &)));
    connect(RedScaling, SIGNAL(activated(int)), r, SLOT(setScaling(int)));
    connect(RedMin, SIGNAL(returnPressed()), this, SLOT(updateRedMinMax()));
    connect(RedMax, SIGNAL(returnPressed()), this, SLOT(updateRedMinMax()));
    connect(r->viewer, SIGNAL(newMinMax(double, double)), this, SLOT(showRedMinMax(double, double)));
    connect(RedXShift, SIGNAL(valueChanged(int)), this, SLOT(updateColourImage()));
    connect(RedYShift, SIGNAL(valueChanged(int)), this, SLOT(updateColourImage()));
    connect(RedMap, SIGNAL(activated(int)), r, SLOT(setColourmap(int)));
    connect(RedActive, SIGNAL(clicked()), this, SLOT(updateColourImage()));

    connect(GreenFileButton, SIGNAL(clicked()), g, SLOT(LoadClicked()));
    connect(GreenFileName, SIGNAL(returnPressed()), this, SLOT(LoadGreen()));
    connect(g->viewer, SIGNAL(fileChanged(const QString &)), GreenFileName, SLOT(setText(const QString &)));
    connect(GreenScaling, SIGNAL(activated(int)), g, SLOT(setScaling(int)));
    connect(GreenMin, SIGNAL(returnPressed()), this, SLOT(updateGreenMinMax()));
    connect(GreenMax, SIGNAL(returnPressed()), this, SLOT(updateGreenMinMax()));
    connect(g->viewer, SIGNAL(newMinMax(double, double)), this, SLOT(showGreenMinMax(double, double)));
    connect(GreenXShift, SIGNAL(valueChanged(int)), this, SLOT(updateColourImage()));
    connect(GreenYShift, SIGNAL(valueChanged(int)), this, SLOT(updateColourImage()));
    connect(GreenMap, SIGNAL(activated(int)), g, SLOT(setColourmap(int)));
    connect(GreenActive, SIGNAL(clicked()), this, SLOT(updateColourImage()));

    connect(BlueFileButton, SIGNAL(clicked()), b, SLOT(LoadClicked()));
    connect(BlueFileName, SIGNAL(returnPressed()), this, SLOT(LoadBlue()));
    connect(b->viewer, SIGNAL(fileChanged(const QString &)), BlueFileName, SLOT(setText(const QString &)));
    connect(BlueScaling, SIGNAL(activated(int)), b, SLOT(setScaling(int)));
    connect(BlueMin, SIGNAL(returnPressed()), this, SLOT(updateBlueMinMax()));
    connect(BlueMax, SIGNAL(returnPressed()), this, SLOT(updateBlueMinMax()));
    connect(b->viewer, SIGNAL(newMinMax(double, double)), this, SLOT(showBlueMinMax(double, double)));
    connect(BlueXShift, SIGNAL(valueChanged(int)), this, SLOT(updateColourImage()));
    connect(BlueYShift, SIGNAL(valueChanged(int)), this, SLOT(updateColourImage()));
    connect(BlueMap, SIGNAL(activated(int)), b, SLOT(setColourmap(int)));
    connect(BlueActive, SIGNAL(clicked()), this, SLOT(updateColourImage()));


    connect(ZoomInButton, SIGNAL(clicked()), r, SLOT(incZoom()));
    connect(ZoomInButton, SIGNAL(clicked()), g, SLOT(incZoom()));
    connect(ZoomInButton, SIGNAL(clicked()), b, SLOT(incZoom()));
    connect(ZoomOutButton, SIGNAL(clicked()), r, SLOT(decZoom()));
    connect(ZoomOutButton, SIGNAL(clicked()), g, SLOT(decZoom()));
    connect(ZoomOutButton, SIGNAL(clicked()), b, SLOT(decZoom()));

    connect(AlignButton, SIGNAL(clicked()), this, SLOT(alignImages()));
    connect(CloseButton, SIGNAL(clicked()), this, SLOT(close()));
    connect(SaveButton, SIGNAL(clicked()), this, SLOT(saveColourImage()));

    connect(r->viewer, SIGNAL(colourtableChanged()), this, SLOT(updateColourImage()));
    connect(g->viewer, SIGNAL(colourtableChanged()), this, SLOT(updateColourImage()));
    connect(b->viewer, SIGNAL(colourtableChanged()), this, SLOT(updateColourImage()));
    connect(r->viewer, SIGNAL(fileChanged(const QString &)),
            RedFileName, SLOT(setText(const QString &)));
    connect(g->viewer, SIGNAL(fileChanged(const QString &)),
            GreenFileName, SLOT(setText(const QString &)));
    connect(b->viewer, SIGNAL(fileChanged(const QString &)),
            BlueFileName, SLOT(setText(const QString &)));

    connect(resultscroller->verticalScrollBar(), SIGNAL(valueChanged(int)),
            this, SLOT(ScrollY(int)));
    connect(resultscroller->horizontalScrollBar(), SIGNAL(valueChanged(int)),
            this, SLOT(ScrollX(int)));
    setWindowTitle("QFitsView - RGB Image Creator");

    resize(900, 640);
}

void RGBDialog::updateColourImage() {
    int ra = RedActive->isChecked(),
        ga = GreenActive->isChecked(),
        ba = BlueActive->isChecked();
    if (!ra && !ga && !ba) {
        return;
    }

    QImage red, green, blue;
    int zoom = 0.;
    if (ra) {
        red = *(r->viewer->image);
        zoom = r->viewer->zoom;
    }
    if (ga) {
        green = *(g->viewer->image);
        zoom = g->viewer->zoom;
    }
    if (ba) {
        blue = *(b->viewer->image);
        zoom = b->viewer->zoom;
    }

    double dzoom = 1.0;
    if (zoom > zoomIndex) {
        dzoom = (double)(zoom - zoomIndex);
    }
    if (zoom < zoomIndex) {
        dzoom = 1. / (double)(zoomIndex - zoom);
    }

    int rsx = RedXShift->value() * dzoom,
        rsy = RedYShift->value() * dzoom,
        gsx = GreenXShift->value() * dzoom,
        gsy = GreenYShift->value() * dzoom,
        bsx = BlueXShift->value() * dzoom,
        bsy = BlueYShift->value() * dzoom;

    int width = 0;
    if (ra) {
        width = red.width() + rsx;
    } else if (ga) {
        width = green.width() + gsx;
    } else if (ba) {
        width = blue.width() + bsx;
    }

    if (ra && red.width() + rsx < width) {
        width = red.width() + rsx;
    }
    if (ga && green.width() + gsx < width) {
        width = green.width() + gsx;
    }
    if (ba && blue.width() + bsx < width) {
        width = blue.width() + bsx;
    }

    int height = 0;
    if (ra) {
        height = red.height() + rsy;
    } else if (ga) {
        height = green.height() + gsy;
    } else if (ba) {
        height = blue.height() + bsy;
    }

    if (ra && red.height() + rsy < height) {
        height = red.height() + rsy;
    }
    if (ga && green.height() + gsy < height) {
        height = green.height() + gsy;
    }
    if (ba && blue.height() + bsy < height) {
        height = blue.height() + bsy;
    }

    int x0 = 0;
    if (ra && rsx > x0) {
        x0 = rsx;
    }
    if (ga && gsx > x0) {
        x0 = gsx;
    }
    if (ba && bsx > x0) {
        x0 = bsx;
    }

    int y0 = 0;
    if (ra && rsy > y0) {
        y0 = rsy;
    }
    if (ga && gsy > y0) {
        y0 = gsy;
    }
    if (ba && bsy > y0) {
        y0 = bsy;
    }




    QImage *result = new QImage(width, height, QImage::Format_RGB32);
    for (int y = y0; y < height; y++) {
        QRgb    *p = (QRgb *)result->scanLine(y);
        uchar   *rl = NULL,
                *gl = NULL,
                *bl = NULL;
        if (ra) {
            rl = red.scanLine(y-rsy) - rsx;
        }
        if (ga) {
            gl = green.scanLine(y-gsy) - gsx;
        }
        if (ba) {
            bl = blue.scanLine(y-bsy) - bsx;
        }
        for (int x = x0; x < width; x++) {
            int rr = 0,
                gg = 0,
                bb = 0;
            if (ra) {
                QRgb redvalue = red.color(*rl++);
                rr += qRed(redvalue);
                gg += qGreen(redvalue);
                bb += qBlue(redvalue);
            }
            if (ga) {
                QRgb greenvalue = green.color(*gl++);
                rr += qRed(greenvalue);
                gg += qGreen(greenvalue);
                bb += qBlue(greenvalue);
            }
            if (ba) {
                QRgb bluevalue = blue.color(*bl++);
                rr += qRed(bluevalue);
                gg += qGreen(bluevalue);
                bb += qBlue(bluevalue);
            }

            if (rr > 255) {
                rr = 255;
            }
            if (gg > 255) {
                gg = 255;
            }
            if (bb > 255) {
                bb = 255;
            }

            *p++ = qRgb(rr, gg, bb);
        }
    }
    resultviewer->setPixmap(QPixmap::fromImage(*result));
    SaveButton->setEnabled(true);
}

void RGBDialog::ScrollX(int value) {
    r->viewer->xoffset = -value;
    g->viewer->xoffset = -value;
    b->viewer->xoffset = -value;

    r->update();
    g->update();
    b->update();
}

void RGBDialog::ScrollY(int value) {
    r->viewer->yoffset = -value;
    g->viewer->yoffset = -value;
    b->viewer->yoffset = -value;

    r->update();
    g->update();
    b->update();
}

void RGBDialog::LoadRed() {
    r->viewer->LoadFile((char *)RedFileName->text().toStdString().c_str());
}

void RGBDialog::LoadGreen() {
    g->viewer->LoadFile((char *)GreenFileName->text().toStdString().c_str());
}

void RGBDialog::LoadBlue() {
    b->viewer->LoadFile((char *)BlueFileName->text().toStdString().c_str());
}

void RGBDialog::updateRedMinMax() {
    r->viewer->setScaleMinMax(RedMin->text().toDouble(), RedMax->text().toDouble());
}

void RGBDialog::updateGreenMinMax() {
    g->viewer->setScaleMinMax(GreenMin->text().toDouble(), GreenMax->text().toDouble());
}

void RGBDialog::updateBlueMinMax() {
    b->viewer->setScaleMinMax(BlueMin->text().toDouble(), BlueMax->text().toDouble());
}

void RGBDialog::showRedMinMax(double min, double max) {
    RedMin->setText(QString::number(min));
    RedMax->setText(QString::number(max));
}

void RGBDialog::showGreenMinMax(double min, double max) {
    GreenMin->setText(QString::number(min));
    GreenMax->setText(QString::number(max));
}

void RGBDialog::showBlueMinMax(double min, double max) {
    BlueMin->setText(QString::number(min));
    BlueMax->setText(QString::number(max));
}

/*
 * Take WCS information from all images and superpose then
 */

void RGBDialog::alignImages() {
    // first check if any buffer is active and if they all contain WCS information
    bool ra = RedActive->isChecked(),
         ga = GreenActive->isChecked(),
         ba = BlueActive->isChecked(),
         ok = true;

    if (!ra && !ga && !ba) {
        ok = false;
    }

    if (ok){
        if (ra) {
            ok = r->viewer->data.hasRefPix();
        }
    }
    if (ok){
        if (ga) {
            ok = g->viewer->data.hasRefPix();
        }
    }
    if (ok) {
        if (ba) {
            ok = b->viewer->data.hasRefPix();
        }
    }
    if (!ok) {
        QMessageBox::information(this, "Align Images", "Not all buffers contain WCS information");
        return;
    }

    QApplication::setOverrideCursor(Qt::WaitCursor);

    // rescale images to maximum pixel scale
    double  maxscale = 0.,
            factor   = 0.;

    if (ra) {
        maxscale = fabs(r->viewer->data.getCDELT(1));
    }
    if (ga) {
        if (fabs(g->viewer->data.getCDELT(1)) < maxscale) {
            maxscale = fabs(g->viewer->data.getCDELT(1));
        }
    }
    if (ba) {
        if (fabs(b->viewer->data.getCDELT(1)) < maxscale) {
            maxscale = fabs(b->viewer->data.getCDELT(1));
        }
    }

    if (ra && (maxscale != fabs(r->viewer->data.getCDELT(1)))) {
        factor = fabs(r->viewer->data.getCDELT(1)) / maxscale;
        r->viewer->data.rebin((int)((double)r->viewer->data.Naxis(1) * factor), (int)((double)r->viewer->data.Naxis(2) * factor));
    }
    if (ga && (fabs(maxscale) != g->viewer->data.getCDELT(1))) {
        factor = fabs(g->viewer->data.getCDELT(1)) / maxscale;
        g->viewer->data.rebin((int)((double)g->viewer->data.Naxis(1) * factor), (int)((double)g->viewer->data.Naxis(2) * factor));
    }
    if (ba && (maxscale != fabs(b->viewer->data.getCDELT(1)))) {
        factor = b->viewer->data.getCDELT(1) / maxscale;
        b->viewer->data.rebin((int)((double)b->viewer->data.Naxis(1) * factor), (int)((double)b->viewer->data.Naxis(2) * factor));
    }

    // clip to smallest image
    double  minra = 0.0,  maxra = 360.0,  mindec = -90.0,  maxdec = 90.0,
           rminra = 0.0, rmaxra = 360.0, rmindec = -90.0, rmaxdec = 90.0,
           gminra = 0.0, gmaxra = 360.0, gmindec = -90.0, gmaxdec = 90.0,
           bminra = 0.0, bmaxra = 360.0, bmindec = -90.0, bmaxdec = 90.0;

    if (ra) {
        double tempr;
        rminra = (1.0 - r->viewer->data.getCRPIX(1)) * r->viewer->data.getCDELT(1) + r->viewer->data.getCRVAL(1);
        rmaxra = ((double)r->viewer->data.Naxis(1) - r->viewer->data.getCRPIX(1)) * r->viewer->data.getCDELT(1) + r->viewer->data.getCRVAL(1);
        if (rminra > rmaxra) {
            SWAP(rminra, rmaxra);
        }
        rmindec = (1.0 - r->viewer->data.getCRPIX(2)) * r->viewer->data.getCDELT(2) + r->viewer->data.getCRVAL(2);
        rmaxdec = ((double)r->viewer->data.Naxis(2) - r->viewer->data.getCRPIX(2)) * r->viewer->data.getCDELT(2) + r->viewer->data.getCRVAL(2);
        if (rmindec > rmaxdec) {
            SWAP(rmindec, rmaxdec);
        }
    }
    if (ga) {
        double tempr;
        gminra = (1.0 - g->viewer->data.getCRPIX(1)) * g->viewer->data.getCDELT(1) + g->viewer->data.getCRVAL(1);
        gmaxra = ((double)g->viewer->data.Naxis(1) - g->viewer->data.getCRPIX(1)) * g->viewer->data.getCDELT(1) + g->viewer->data.getCRVAL(1);
        if (gminra > gmaxra) {
            SWAP(gminra, gmaxra);
        }
        gmindec = (1.0 - g->viewer->data.getCRPIX(2)) * g->viewer->data.getCDELT(2) + g->viewer->data.getCRVAL(2);
        gmaxdec = ((double)g->viewer->data.Naxis(2) - g->viewer->data.getCRPIX(2)) * g->viewer->data.getCDELT(2) + g->viewer->data.getCRVAL(2);
        if (gmindec > gmaxdec) {
            SWAP(gmindec, gmaxdec);
        }
    }
    if (ba) {
        double tempr;
        bminra = (1.0 - b->viewer->data.getCRPIX(1)) * b->viewer->data.getCDELT(1) + b->viewer->data.getCRVAL(1);
        bmaxra = ((double)b->viewer->data.Naxis(1) - b->viewer->data.getCRPIX(1)) * b->viewer->data.getCDELT(1) + b->viewer->data.getCRVAL(1);
        if (bminra > bmaxra) {
            SWAP(bminra, bmaxra);
        }
        bmindec = (1.0 - b->viewer->data.getCRPIX(2)) * b->viewer->data.getCDELT(2) + b->viewer->data.getCRVAL(2);
        bmaxdec = ((double)b->viewer->data.Naxis(2) - b->viewer->data.getCRPIX(2)) * b->viewer->data.getCDELT(2) + b->viewer->data.getCRVAL(2);
        if (bmindec > bmaxdec) {
            SWAP(bmindec, bmaxdec);
        }
    }

    if (rminra > minra) {
        minra = rminra;
    }
    if (gminra > minra) {
        minra = gminra;
    }
    if (bminra > minra) {
        minra = bminra;
    }
    if (rmaxra < maxra) {
        maxra = rmaxra;
    }
    if (gmaxra < maxra) {
        maxra = gmaxra;
    }
    if (bmaxra < maxra) {
        maxra = bmaxra;
    }
    if (rmindec > mindec) {
        mindec = rmindec;
    }
    if (gmindec > mindec) {
        mindec = gmindec;
    }
    if (bmindec > mindec) {
        mindec = bmindec;
    }
    if (rmaxdec < maxdec) {
        maxdec = rmaxdec;
    }
    if (gmaxdec < maxdec) {
        maxdec = gmaxdec;
    }
    if (bmaxdec < maxdec) {
        maxdec = bmaxdec;
    }

    long x1 = 0, x2 = 0, y1 = 0, y2 = 0;
    if (ra) {
        long tempr;
        x1 = r->viewer->data.getCRPIX(1) + (minra - r->viewer->data.getCRVAL(1)) / r->viewer->data.getCDELT(1);
        x2 = r->viewer->data.getCRPIX(1) + (maxra - r->viewer->data.getCRVAL(1)) / r->viewer->data.getCDELT(1);
        if (x1 > x2) {
            SWAP(x1, x2);
        }
        if (x1 < 1) {
            x1 = 1;
        }
        if (x2 > r->viewer->data.Naxis(1)) {
            x2 = r->viewer->data.Naxis(1);
        }

        y1 = r->viewer->data.getCRPIX(2) + (mindec - r->viewer->data.getCRVAL(2)) / r->viewer->data.getCDELT(2);
        y2 = r->viewer->data.getCRPIX(2) + (maxdec - r->viewer->data.getCRVAL(2)) / r->viewer->data.getCDELT(2);
        if (y1 > y2) {
            SWAP(y1, y2);
        }
        if (y1 < 1) {
            y1 = 1;
        }
        if (y2 > r->viewer->data.Naxis(2)) {
            y2 = r->viewer->data.Naxis(2);
        }

        if (!((x1 == 1) && (x2 == r->viewer->data.Naxis(1)) && (y1 == 1) && (y2 == r->viewer->data.Naxis(2)))) {
            Fits tmp;
            r->viewer->data.extractRange(tmp, x1, x2, y1, y2, 1, 1);
            r->viewer->data.copy(tmp);
        }
    }
    if (ga) {
        long tempr;
        x1 = g->viewer->data.getCRPIX(1) + (minra - g->viewer->data.getCRVAL(1)) / g->viewer->data.getCDELT(1);
        x2 = g->viewer->data.getCRPIX(1) + (maxra - g->viewer->data.getCRVAL(1)) / g->viewer->data.getCDELT(1);
        if (x1 > x2) {
            SWAP(x1, x2);
        }
        if (x1 < 1) {
            x1 = 1;
        }
        if (x2 > g->viewer->data.Naxis(1)) {
            x2 = g->viewer->data.Naxis(1);
        }

        y1 = g->viewer->data.getCRPIX(2) + (mindec - g->viewer->data.getCRVAL(2)) / g->viewer->data.getCDELT(2);
        y2 = g->viewer->data.getCRPIX(2) + (maxdec - g->viewer->data.getCRVAL(2)) / g->viewer->data.getCDELT(2);
        if (y1 > y2) {
            SWAP(y1, y2);
        }
        if (y1 < 1) {
            y1 = 1;
        }
        if (y2 > g->viewer->data.Naxis(2)) {
            y2 = g->viewer->data.Naxis(2);
        }

        if (!((x1 == 1) && (x2 == g->viewer->data.Naxis(1)) && (y1 == 1) && (y2 == g->viewer->data.Naxis(2)))) {
            Fits tmp;
            g->viewer->data.extractRange(tmp, x1, x2, y1, y2, 1, 1);
            g->viewer->data.copy(tmp);
        }
    }

    if (ra) {
        r->viewer->setScaleMinMax(RedMin->text().toDouble(), RedMax->text().toDouble());
    }
    if (ga) {
        g->viewer->setScaleMinMax(GreenMin->text().toDouble(), GreenMax->text().toDouble());
    }
    if (ba) {
        b->viewer->setScaleMinMax(BlueMin->text().toDouble(), BlueMax->text().toDouble());
    }

    updateColourImage();
    QApplication::restoreOverrideCursor();
}

void RGBDialog::saveColourImage() {
    QString selectedFilter;
    QString filename = getSaveImageFilename(&selectedFilter);

    if (!filename.isNull()) {
        selectedFilter.remove(0,1);
        if (!filename.endsWith(selectedFilter)) {
            filename += selectedFilter;
        }

#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
        resultviewer->pixmap()->save(filename);
#else
        resultviewer->pixmap().save(filename);
#endif /* QT_VERSION */

        QFileInfo finfo(filename);
        settings.lastSavePath = finfo.absoluteDir().path();
        settings.lastSavePath.replace("\\", "/");
    }
}

dpPopup::dpPopup(QWidget *parent) : QComboBox(parent) {
}

void dpPopup::populateAndSelect(const QString sel) {
    clear();
    addItem("<none>", -1);
    for (auto var:dpuser_vars) {
        if (var.second.type == typeFits) {
            if ((var.first != "") && (var.first.find("_") == std::string::npos)) {
               addItem(var.first.c_str(), var.first.c_str());
            }
        }
    }
    if (!sel.isEmpty()) setCurrentText(sel);
}

void dpPopup::showPopup() {
    populateAndSelect(currentText());
//    QString sel = currentText();
//    clear();
//    addItem("<none>", -1);
//    for (int i = 0; i < nvariables; i++) {
//        if (variables[i].type == typeFits) {
//            if ((svariables[i] != "") && (svariables[i].contains("_") == 0)) {
//               addItem(svariables[i].c_str(), i);
//            }
//        }
//    }
//    setCurrentText(sel);
    QComboBox::showPopup();
}

dpCombineItem::dpCombineItem(QWidget *parent) : QFrame(parent) {
    QGridLayout *lay = new QGridLayout(this);

    setFrameStyle(QFrame::StyledPanel);
    QLabel *bufferlabel = new QLabel("Buffer:", this);
    bufferlabel->adjustSize();
    lay->addWidget(bufferlabel, 0, 0, Qt::AlignRight | Qt::AlignVCenter);
    buffer = new dpPopup(this);
    buffer->addItem("<none>", -1);
    lay->addWidget(buffer, 0, 1, 1, 3);

    QLabel *xshiftlabel = new QLabel("X shift:", this);
    xshiftlabel->adjustSize();
    lay->addWidget(xshiftlabel, 1, 0, Qt::AlignRight | Qt::AlignVCenter);
    xshift = new QDoubleSpinBox(this);
    xshift->setMaximum(9999.9);
    xshift->setMinimum(-9999.9);
    xshift->adjustSize();
    lay->addWidget(xshift, 1, 1);

    QLabel *yshiftlabel = new QLabel("Y shift:", this);
    yshiftlabel->adjustSize();
    lay->addWidget(yshiftlabel, 1, 2, Qt::AlignRight | Qt::AlignVCenter);
    yshift = new QDoubleSpinBox(this);
    yshift->setMaximum(9999.9);
    yshift->setMinimum(-9999.9);
    yshift->adjustSize();
    lay->addWidget(yshift, 1, 3);

    QLabel *scalelabel = new QLabel("Scale:", this);
    scalelabel->adjustSize();
    lay->addWidget(scalelabel, 2, 0, Qt::AlignRight | Qt::AlignVCenter);
    scale = new QDoubleSpinBox(this);
    scale->setSingleStep(0.1);
    scale->setValue(1.);
    scale->adjustSize();
    lay->addWidget(scale, 2, 1);

    connect(buffer, SIGNAL(activated(int)), this, SIGNAL(somethingChanged()));
    connect(xshift, SIGNAL(valueChanged(double)), this, SIGNAL(somethingChanged()));
    connect(yshift, SIGNAL(valueChanged(double)), this, SIGNAL(somethingChanged()));
    connect(scale, SIGNAL(valueChanged(double)), this, SIGNAL(somethingChanged()));

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

dpCombineDialog::dpCombineDialog(QWidget *parent) : QDialog(parent) {
    dontupdate = false;
    resultscroller = new QScrollArea(this);
    resultscroller->setAlignment(Qt::AlignCenter);
    resultscroller->setGeometry(0, 0, 300, 300);
    resultviewer = new QLabel();
    resultscroller->setWidget(resultviewer);

    buf1 = new dpCombineItem(this);
    buf1->move(resultscroller->x() + resultscroller->width() + 10, 10);
    buf2 = new dpCombineItem(this);
    buf2->move(resultscroller->x() + resultscroller->width() + 10, buf1->y() + buf1->height() + 10);
    buf3 = new dpCombineItem(this);
    buf3->move(resultscroller->x() + resultscroller->width() + 10, buf2->y() + buf2->height() + 10);
    connect(buf1, SIGNAL(somethingChanged()), this, SLOT(updateColourImage()));
    connect(buf2, SIGNAL(somethingChanged()), this, SLOT(updateColourImage()));
    connect(buf3, SIGNAL(somethingChanged()), this, SLOT(updateColourImage()));

    alignButton = new QPushButton("Align Buffers", this);
    alignButton->setGeometry(buf3->x(), buf3->y() + buf3->height() + 10, buf3->width()/2-5, 30);
    copyButton = new QPushButton("Copy Image", this);
    copyButton->setGeometry(buf3->x(), alignButton->y() + alignButton->height() + 10, buf3->width()/2-5, 30);
    saveButton = new QPushButton("Save Image...", this);
    saveButton->setEnabled(false);
    saveButton->setGeometry(copyButton->x() + copyButton->width() + 10, copyButton->y(), copyButton->width(), 30);
    connect(alignButton, SIGNAL(clicked()), this, SLOT(alignBuffers()));
    connect(copyButton, SIGNAL(clicked()), this, SLOT(copyImage()));
    connect(saveButton, SIGNAL(clicked()), this, SLOT(saveImage()));

    setWindowTitle("QFitsView - Colour Image Combiner");
    adjustSize();
    setMinimumSize(size());
}

void dpCombineDialog::resizeEvent(QResizeEvent *r) {
    resultscroller->setGeometry(0, 0, r->size().width() - buf1->width() - 20, r->size().height());
    buf1->move(resultscroller->x() + resultscroller->width() + 10, 10);
    buf2->move(resultscroller->x() + resultscroller->width() + 10, buf1->y() + buf1->height() + 10);
    buf3->move(resultscroller->x() + resultscroller->width() + 10, buf2->y() + buf2->height() + 10);
    alignButton->move(buf3->x(), buf3->y() + buf3->height() + 10);
    copyButton->move(buf3->x(), alignButton->y() + alignButton->height() + 10);
    saveButton->move(copyButton->x() + copyButton->width() + 10, copyButton->y());
}

void dpCombineDialog::mapChannel(QFitsSingleBuffer *sb, QImage *color, int xs, int ys, double sc, int *cwidth, int *cheight) {
    QReadLocker locker(&buffersLock);
    if (sb != NULL) {
        disconnect(sb, 0, 0, 0);
        if (sb->getViewMode() == ViewImage) {
            *color = QImage((sb->getImage()->width() + xs)*sc, (sb->getImage()->height() + ys)*sc, QImage::Format_RGB32);
            color->fill(0);
            QTransform T;
            T.translate(xs, ys);
            T.scale(sc, sc);
            QPainter p(color);
            p.begin(color);
            p.setTransform(T);
            p.drawImage(0, 0, *(sb->getImage()));
            p.end();
            sb->setUsedInCombine(true);
            if (color->width() > *cwidth) {
                *cwidth = color->width();
            }
            if (color->height() > *cheight) {
                *cheight = color->height();
            }
        } else if (sb->getViewMode() == ViewContour) {
            QFitsWidgetContour *wc = dynamic_cast<QFitsWidgetContour *>(sb->getState());
            if ((wc != NULL) &&
                (sb->getDpData()->type == typeFits))
            {
                Fits *f = sb->getDpData()->fvalue;
                *color = QImage((f->Naxis(1) + xs)*sc, (f->Naxis(2) + ys)*sc, QImage::Format_RGB32);
                QPainter p(color);
                p.begin(color);
                p.setRenderHint(QPainter::Antialiasing);
                p.fillRect(color->rect(), QColor(0, 0, 0));
                p.setPen(QPen(QColor(255, 255, 255)));
                QTransform T;
                T.translate(0, f->Naxis(2));
                T.scale(1, -1);
                T.translate(xs, -ys);
                T.translate(0, (1. - sc)*f->Naxis(2));
                T.scale(sc, sc);
                wc->contour->paintContour(p, T);
                p.end();
                *color = color->convertToFormat(QImage::Format_Indexed8);
                sb->setUsedInCombine(true);
                if (color->width() > *cwidth) {
                    *cwidth = color->width();
                }
                if (color->height() > *cheight) {
                    *cheight = color->height();
                }
            }
        } else {
            sb->setUsedInCombine(false);
        }
    }
}

void dpCombineDialog::updateColourImage() {
    if (dontupdate) {
        return;
    }

    bool ra = buf1->buffer->currentText() != "<none>",
         ga = buf2->buffer->currentText() != "<none>",
         ba = buf3->buffer->currentText() != "<none>";
    if (!ra && !ga && !ba) {
        return;
    }

    int     width  = 0,
            height = 0;
    QImage  red, green, blue;
    if (ra) {
        std::string index = buf1->buffer->currentText().toStdString();
        QFitsSingleBuffer *sb = dynamic_cast<QFitsSingleBuffer *>(fitsMainWindow->main_view->getBuffer(index));
        mapChannel(sb, &red, buf1->xshift->value(), buf1->yshift->value(), buf1->scale->value(), &width, &height);
    }
    if (ga) {
        std::string index = buf2->buffer->currentText().toStdString();
        QFitsSingleBuffer *sb = dynamic_cast<QFitsSingleBuffer *>(fitsMainWindow->main_view->getBuffer(index));
        mapChannel(sb, &green, buf2->xshift->value(), buf2->yshift->value(), buf2->scale->value(), &width, &height);
    }
    if (ba) {
        std::string index = buf3->buffer->currentText().toStdString();
        QFitsSingleBuffer *sb = dynamic_cast<QFitsSingleBuffer *>(fitsMainWindow->main_view->getBuffer(index));
        mapChannel(sb, &blue, buf3->xshift->value(), buf3->yshift->value(), buf3->scale->value(), &width, &height);
    }

    result = QImage(width, height, QImage::Format_RGB32);
    for (int x = 0; x < result.width(); x++) {
        for (int y = 0; y < result.height(); y++) {
            int rr = 0,
                gg = 0,
                bb = 0;
            if (ra) {
                if (x < red.width() && y < red.height()) {
                    QRgb redvalue = red.pixel(x, y);
                    rr += qRed(redvalue);
                    gg += qGreen(redvalue);
                    bb += qBlue(redvalue);
                }
            }
            if (ga) {
                if (x < green.width() && y < green.height()) {
                    QRgb greenvalue = green.pixel(x, y);
                    rr += qRed(greenvalue);
                    gg += qGreen(greenvalue);
                    bb += qBlue(greenvalue);
                }
            }
            if (ba) {
                if (x < blue.width() && y < blue.height()) {
                    QRgb bluevalue = blue.pixel(x, y);
                    rr += qRed(bluevalue);
                    gg += qGreen(bluevalue);
                    bb += qBlue(bluevalue);
                }
            }

            if (rr > 255) {
                rr = 255;
            }
            if (gg > 255) {
                gg = 255;
            }
            if (bb > 255) {
                bb = 255;
            }

            result.setPixel(x, y, qRgb(rr, gg, bb));
        }
    }

    resultviewer->resize(result.size());
    resultviewer->setPixmap(QPixmap::fromImage(result));
    saveButton->setEnabled(true);
}

void dpCombineDialog::copyImage() {
    QApplication::clipboard()->setImage(result, QClipboard::Clipboard);
}

void dpCombineDialog::saveImage() {
    QString selectedFilter;
    QString filename = getSaveImageFilename(&selectedFilter);

    if (!filename.isNull()) {
        selectedFilter.remove(0,1);
        if (!filename.endsWith(selectedFilter)) {
            filename += selectedFilter;
        }

        result.save(filename);

        QFileInfo finfo(filename);
        settings.lastSavePath = finfo.absoluteDir().path();
        settings.lastSavePath.replace("\\", "/");
    }
}

/*
 * Take WCS information from all images and superpose then
 */

void dpCombineDialog::alignBuffers() {
    // prevent flicker on result image
    dontupdate = true;

    // first check if any buffer is active and if they all contain WCS information


    QReadLocker locker(&buffersLock);

    bool ra = buf1->buffer->currentText() != "<none>",
         ga = buf2->buffer->currentText() != "<none>",
         ba = buf3->buffer->currentText() != "<none>",
         ok = true;
    if (!ra && !ga && !ba) {
        ok = false;
    }

    Fits *redbuffer   = NULL,
         *greenbuffer = NULL,
         *bluebuffer  = NULL;
    if (ok){
        if (ra) {
            std::string index = buf1->buffer->currentText().toStdString();
            QFitsSingleBuffer *sb = dynamic_cast<QFitsSingleBuffer *>(fitsMainWindow->main_view->getBuffer(index));
            if ((sb != NULL) && (sb->getDpData()->type == typeFits)) {
                redbuffer = sb->getDpData()->fvalue;
                ok = redbuffer->hasRefPix();
            } else {
                ok = false;
            }
        }
    }
    if (ok){
        if (ga) {
            std::string index = buf2->buffer->currentText().toStdString();
            QFitsSingleBuffer *sb = dynamic_cast<QFitsSingleBuffer *>(fitsMainWindow->main_view->getBuffer(index));
            if ((sb != NULL) && (sb->getDpData()->type == typeFits)) {
                greenbuffer = sb->getDpData()->fvalue;
                ok = greenbuffer->hasRefPix();
            } else {
                ok = false;
            }
        }
    }
    if (ok) {
        if (ba) {
            std::string index = buf3->buffer->currentText().toStdString();
            QFitsSingleBuffer *sb = dynamic_cast<QFitsSingleBuffer *>(fitsMainWindow->main_view->getBuffer(index));
            if ((sb != NULL) && (sb->getDpData()->type == typeFits)) {
                bluebuffer = sb->getDpData()->fvalue;
                ok = bluebuffer->hasRefPix();
            } else {
                ok = false;
            }
        }
    }
    if (!ok) {
        QMessageBox::information(this, "Align Images", "Not all buffers contain WCS information");
        dontupdate = FALSE;
        return;
    }

    // rescale images to maximum pixel scale
    double minscale = 0.0;
    if (ra) {
        minscale = fabs(redbuffer->getCDELT(1));
    }
    if (ga) {
        if (fabs(greenbuffer->getCDELT(1)) < minscale) {
            minscale = fabs(greenbuffer->getCDELT(1));
        }
    }
    if (ba) {
        if (fabs(bluebuffer->getCDELT(1)) < minscale) {
            minscale = fabs(bluebuffer->getCDELT(1));
        }
    }

    if (ra) {
        buf1->scale->setValue(fabs(redbuffer->getCDELT(1)) / minscale);
    }
    if (ga) {
        buf2->scale->setValue(fabs(greenbuffer->getCDELT(1)) / minscale);
    }
    if (ba) {
        buf3->scale->setValue(fabs(bluebuffer->getCDELT(1)) / minscale);
    }

    // calc pixel shift between buffers
    double  pos1x = -1e30, pos1xr = 0., pos1xg = 0., pos1xb = 0.,
            pos1y = -1e30, pos1yr = 0., pos1yg = 0., pos1yb = 0.;
    if (ra) {
        double cd1_1 = 0., cd2_2 = 0., cd1_2 = 0., cd2_1 = 0., crota2 = 0.;
        redbuffer->GetFloatKey("CROTA2", &crota2),
        redbuffer->GetFloatKey("CD1_1", &cd1_1),
        redbuffer->GetFloatKey("CD2_1", &cd2_1),
        redbuffer->GetFloatKey("CD1_2", &cd1_2),
        redbuffer->GetFloatKey("CD2_2", &cd2_2),

        worldpos(1, 1, redbuffer->getCRVAL(1),
                 redbuffer->getCRVAL(2),
                 redbuffer->getCRPIX(1),
                 redbuffer->getCRPIX(2),
                 redbuffer->getCDELT(1),
                 redbuffer->getCDELT(2),
                 crota2, cd1_1, cd2_1, cd1_2, cd2_2,
                 redbuffer->crtype,
                 &pos1xr, &pos1yr);

        if (redbuffer->getCDELT(1) < 0.) {
            pos1xr *= -1.;
        }
        if (redbuffer->getCDELT(2) < 0.) {
            pos1yr *= -1.;
        }
        if (pos1xr > pos1x) {
            pos1x = pos1xr;
        }
        if (pos1yr > pos1y) {
            pos1y = pos1yr;
        }
    }
    if (ga) {
        double cd1_1 = 0., cd2_2 = 0., cd1_2 = 0., cd2_1 = 0., crota2 = 0.;
        greenbuffer->GetFloatKey("CROTA2", &crota2),
        greenbuffer->GetFloatKey("CD1_1", &cd1_1),
        greenbuffer->GetFloatKey("CD2_1", &cd2_1),
        greenbuffer->GetFloatKey("CD1_2", &cd1_2),
        greenbuffer->GetFloatKey("CD2_2", &cd2_2),

        worldpos(1, 1, greenbuffer->getCRVAL(1),
                 greenbuffer->getCRVAL(2),
                 greenbuffer->getCRPIX(1),
                 greenbuffer->getCRPIX(2),
                 greenbuffer->getCDELT(1),
                 greenbuffer->getCDELT(2),
                 crota2, cd1_1, cd2_1, cd1_2, cd2_2,
                 greenbuffer->crtype,
                 &pos1xg, &pos1yg);

        if (greenbuffer->getCDELT(1) < 0.) {
            pos1xg *= -1.;
        }
        if (greenbuffer->getCDELT(2) < 0.) {
            pos1yg *= -1.;
        }
        if (pos1xg > pos1x) {
            pos1x = pos1xg;
        }
        if (pos1yg > pos1y) {
            pos1y = pos1yg;
        }
    }
    if (ba) {
        double cd1_1 = 0., cd2_2 = 0., cd1_2 = 0., cd2_1 = 0., crota2 = 0.;
        bluebuffer->GetFloatKey("CROTA2", &crota2),
        bluebuffer->GetFloatKey("CD1_1", &cd1_1),
        bluebuffer->GetFloatKey("CD2_1", &cd2_1),
        bluebuffer->GetFloatKey("CD1_2", &cd1_2),
        bluebuffer->GetFloatKey("CD2_2", &cd2_2),

        worldpos(1, 1, bluebuffer->getCRVAL(1),
                 bluebuffer->getCRVAL(2),
                 bluebuffer->getCRPIX(1),
                 bluebuffer->getCRPIX(2),
                 bluebuffer->getCDELT(1),
                 bluebuffer->getCDELT(2),
                 crota2, cd1_1, cd2_1, cd1_2, cd2_2,
                 bluebuffer->crtype,
                 &pos1xb, &pos1yb);

        if (bluebuffer->getCDELT(1) < 0.) {
            pos1xb *= -1.;
        }
        if (bluebuffer->getCDELT(2) < 0.) {
            pos1yb *= -1.;
        }
        if (pos1xb > pos1x) {
            pos1x = pos1xb;
        }
        if (pos1yb > pos1y) {
            pos1y = pos1yb;
        }
    }
    pos1xr -= pos1x; pos1yr -= pos1y;
    pos1xg -= pos1x; pos1yg -= pos1y;
    pos1xb -= pos1x; pos1yb -= pos1y;

    if (ra) {
        pos1x = pos1xr;
        pos1y = pos1yr;
    }
    if (ga) {
        if (pos1xg < pos1x) {
            pos1x = pos1xg;
        }
        if (pos1yg < pos1y) {
            pos1y = pos1yg;
        }
    }
    if (ba) {
        if (pos1xb < pos1x) {
            pos1x = pos1xb;
        }
        if (pos1yb < pos1y) {
            pos1y = pos1yb;
        }
    }
    pos1xr -= pos1x; pos1yr -= pos1y;
    pos1xg -= pos1x; pos1yg -= pos1y;
    pos1xb -= pos1x; pos1yb -= pos1y;

    if (ra) {
        buf1->xshift->setValue(pos1xr / fabs(redbuffer->getCDELT(1)) * buf1->scale->value());
        buf1->yshift->setValue(pos1yr / fabs(redbuffer->getCDELT(2)) * buf1->scale->value());
    }
    if (ga) {
        buf2->xshift->setValue(pos1xg / fabs(greenbuffer->getCDELT(1)) * buf2->scale->value());
        buf2->yshift->setValue(pos1yg / fabs(greenbuffer->getCDELT(2)) * buf2->scale->value());
    }
    if (ba) {
        buf3->xshift->setValue(pos1xb / fabs(bluebuffer->getCDELT(1)) * buf3->scale->value());
        buf3->yshift->setValue(pos1yb / fabs(bluebuffer->getCDELT(2)) * buf3->scale->value());
    }
    dontupdate = false;
    updateColourImage();
}
