# include <QResizeEvent>
#include "QFitsMainWindow.h"
#include "QFitsMainView.h"
#include "QFitsSingleBuffer.h"
#include "QFitsMultiBuffer.h"
#include "QFitsWidget2D.h"
#include "QFitsView2D.h"
#include "QFitsWedge.h"
#include "extra/tetrixwindow.h"
#include "dialogs.h"

#include <QPainter>

dummyBuffer::dummyBuffer(QWidget *parent, int myID)
    : QWidget(parent) {
    theID = myID;
}

void dummyBuffer::mousePressEvent(QMouseEvent *e) {
    emit currentID(theID);
}

QFitsGrid::QFitsGrid(QFitsMainView *parent, int howmany)
    : QWidget(parent) {
    activeID = 0;
    myParent = parent;
    for (int i = 0; i < howmany; i++) {
        widgets[i] = new dummyBuffer(this, i);
        gridMap.insert(i, widgets[i]);
        connect(widgets[i], SIGNAL(currentID(int)), this, SLOT(activeIDChanged(int)));
    }
    grid = new QGridLayout(this);
    grid->setSpacing(1);
    grid->setContentsMargins(1, 1, 1, 1);
    grid->addWidget(widgets[0], 0, 0);
    widgets[0]->show();
    grid->addWidget(widgets[1], 0, 1);
    widgets[1]->show();
    if (howmany > 2) {
        grid->addWidget(widgets[2], 1, 0);
        widgets[2]->show();
        grid->addWidget(widgets[3], 1, 1);
        widgets[3]->show();
    }
}

void QFitsGrid::paintEvent(QPaintEvent *e) {
    QPainter p;
    QPen pen;

    p.begin(this);
    pen.setColor(QColor(255,0,0));
    pen.setWidth(2);
    p.setPen(pen);
    p.drawRect(grid->cellRect(activeRow(), activeColumn()));
    p.end();
}

void QFitsGrid::addWidget(QWidget *which) {
    int where = activeID;

    for (int i = 0; i < grid->columnCount()*grid->rowCount(); i++) {
        if (widgets[i]->isVisible()) {
            activeID = i;
            break;
        }
    }

    gridMap[activeID]->hide();
    gridMap.remove(activeID);

    int oldpos = gridMap.key(which, -1);
    if (oldpos != -1) gridMap.remove(oldpos);
    gridMap.insert(activeID, which);
    grid->addWidget(which, activeRow(), activeColumn());

    update();
}

void QFitsGrid::addWidget(QWidget *which, int row, int column) {
    dp_debug("addWidget: %i %i", row, column);
    gridMap[indexOf(row, column)]->hide();

    grid->addWidget(which, row, column);
    gridMap.remove(indexOf(row, column));
    gridMap.insert(indexOf(row, column), which);
    activeIDChanged(indexOf(row, column));
}

void QFitsGrid::removeWidget(QWidget *which) {
    int where = gridMap.key(which, -1);
    if (where != -1) {
        grid->removeWidget(which);
        gridMap.remove(where);
        activeID = where;
        int r, c, rs, cs;
        grid->getItemPosition(activeID, &r, &c, &rs, &cs);
        grid->addWidget(widgets[activeID], r, c);
        widgets[activeID]->show();
        gridMap.insert(where, widgets[where]);
        update();
    }
}

void QFitsGrid::activeIDChanged(int newID) {
    if (newID < 0) {
        activeID = grid->indexOf(myParent->getCurrentBuffer());
        int r, c, rs, cs;
        grid->getItemPosition(activeID, &r, &c, &rs, &cs);
        activeID = indexOf(r, c);
    } else {
        activeID = newID;
    }
    update();
}

int QFitsGrid::indexOf(int row, int column) {
    return row * grid->columnCount() + column;
}

int QFitsGrid::activeRow() {
    if (activeID == 0 || activeID == 1) return 0;
    else return 1;
}

int QFitsGrid::activeColumn() {
    if (activeID == 0 || activeID == 2) return 0;
    else return 1;
}

QFitsMainView::QFitsMainView(QFitsMainWindow *parent)
                            : QWidget(parent), myParent(parent)
{
    currentBuf = "";
    autoScale = true;

    wedge = new QFitsWedge(this);
    grid = NULL;
    numberOfBuffersToShow = 1;
}

QFitsMainView::~QFitsMainView() {
    if (!bufferMap.empty()) {
        bufferMap.clear();
    }
    if (wedge != NULL) {
        delete wedge;
        wedge = NULL;
    }
}

bool QFitsMainView::bufferMapContainsKey(std::string i) {
    return (bufferMap.count(i) > 0);
}

std::string QFitsMainView::getCurrentBufferIndex() {
    return currentBuf;
}

int QFitsMainView::getBufferSize() {
    return bufferMap.size();
}

void QFitsMainView::setCurrentBufferFromWidget(QFitsBaseBuffer *which) {
    std::string val = "";
    for (auto var:bufferMap) {
        if (var.second == which) {
            val = var.first;
            break;
        }
    }
    if (val != "") {
        setCurrentBufferIndex(val);
        getCurrentBuffer()->activateBuffer();
        fitsMainWindow->applyAppearance();
        if (grid) {
            grid->activeIDChanged(0);
        }
    }
}

void QFitsMainView::setCurrentBufferIndex(std::string val) {
    recentBuffers.removeAll(val);
    recentBuffers.prepend(val);

    currentBuf = val;

    fitsMainWindow->setActualSB(getCurrentBuffer()->getFirstImageBuffer());
}

void QFitsMainView::showMultiple(int howMany) {
    numberOfBuffersToShow = howMany;
    if (grid) {
        for (auto var:bufferMap) {
            var.second->setParent(this);
        }
//        QMapIterator<std::string, QFitsBaseBuffer*> i(bufferMap);
//        while(i.hasNext()) {
//            i.next();
//    //        if (i.value()->isVisible()) {
//                i.value()->setParent(this);
//    //        }
//        }

        delete grid;
    }
    if (howMany == 1) {
        grid = NULL;
        getCurrentBuffer()->show();
        initialResize();
        return;
    }

    grid = new QFitsGrid(this, howMany);
    grid->setGeometry(QRect(0, 0, width(), height() - wedge->minimumHeight()));
    grid->show();
    grid->addWidget(getCurrentBuffer(), 0, 0);
    if (howMany >= 2 && recentBuffers.count() >= 2) {
        grid->addWidget(bufferMap.at(recentBuffers.value(1)));
        bufferMap.at(recentBuffers.value(1))->show();
    }
    if (howMany >= 4) {
        if (recentBuffers.count() >= 3) {
            grid->addWidget(bufferMap.at(recentBuffers.value(2)));
            bufferMap.at(recentBuffers.value(2))->show();
        }
        if (recentBuffers.count() >= 4) {
            grid->addWidget(bufferMap.at(recentBuffers.value(3)));
            bufferMap.at(recentBuffers.value(3))->show();
        }
    }
}

QFitsBaseBuffer* QFitsMainView::getCurrentBuffer() {
    if (currentBuf.size())
        if (bufferMap.count(currentBuf) > 0)
            return bufferMap.at(currentBuf);

    return NULL;
}

QFitsBaseBuffer* QFitsMainView::getBuffer(std::string i) {
    if (bufferMap.count(i) > 0)
        return bufferMap.at(i);
    else
        return NULL;
}

//void QFitsMainView::showFitsBuffer(QFitsBaseBuffer *which) {
//    if (grid) {
//        grid->addWidget(which);
//        for (int i = 0; i < numberOfBuffersToShow; i++) {
//            if (grid->gridMap.value(i, NULL) == NULL) {
//                grid->gridMap.insert(i, grid->widgets[i]);
//                grid->widgets[i]->show();
//            }
//        }
//    }
//}

void QFitsMainView::deleteBaseBuffer(std::string id)
{
    QFitsBaseBuffer *bb = bufferMap.at(id);
    if (bb != NULL) {
        if (grid != NULL) {
            grid->removeWidget(bb);
        }
        delete bb;
    }
    bufferMap.erase(id);
    recentBuffers.removeAll(id);
}

bool QFitsMainView::hasBaseBuffer(std::string id) {
    return bufferMap.find(id) != bufferMap.end();
}

void QFitsMainView::hideAllBuffers() {
//    QMapIterator<std::string, QFitsBaseBuffer*> i(bufferMap);
//    while(i.hasNext()) {
//        i.next();
    for (auto var:bufferMap) {
        QFitsBaseBuffer *bb = var.second;
        if (grid != NULL) {
            if (grid->gridMap.key(dynamic_cast<QWidget*>(bb), -1) == -1) {
                var.second->hide();
            }
        } else {
            var.second->hide();
        }
    }
}

//
// to be called when a new QFitsSingleBuffer is to be created
//
QFitsBaseBuffer* QFitsMainView::addNewBuffer(std::string id,
                                             dpuserType *dpt)
{
    fitsMainWindow->setActualSB(NULL);
    switch (dpt->type) {
    case typeFits:
    case typeStrarr:
        {
        QFitsSingleBuffer* sb = new QFitsSingleBuffer(this, dpt, 0, "");
        bufferMap[id] = sb;
        currentBuf = id;
        fitsMainWindow->setActualSB(sb);
        if (grid != NULL) {
            grid->addWidget(sb);
        }
        return sb;
        }
        break;
    case typeDpArr:
        {
        QFitsMultiBuffer *mb = new QFitsMultiBuffer(this, dpt, 0, "");
        bufferMap[id] = mb;
        currentBuf = id;
        fitsMainWindow->setActualSB(mb->getFirstImageBuffer());
        if (grid != NULL) {
            grid->addWidget(mb);
        }
        return mb;
        }
        break;
    default:
        break;
    }
    return NULL;
}

//
// to be called when a QFitsMultiBuffer is to be changed into a QFitsSingleBuffer
//
QFitsBaseBuffer* QFitsMainView::recreateBuffer(std::string id,
                                               dpuserType *dpt)
{
    QFitsBaseBuffer *old_bb = bufferMap.at(id);
    bufferMap.erase(id);

    // remember old GUI settings
//    dpViewMode  viewMode = old_bb->getViewMode();
    dpScaleMode imageScaleRange = old_bb->getImageScaleRange();
    dpCubeMode  cubeMode        = old_bb->getCubeMode();
    QAction     *colormap       = old_bb->getColormap();
    double      zoomIndex       = old_bb->getZoomFactor(),
                zoom_3Dwire     = old_bb->getZoomFactor_3Dwire();
    int         brightness      = old_bb->getBrightness(),
                contrast        = old_bb->getContrast(),
                imageScaleMethod = old_bb->getImageScalingMethod(),
                rotation        = old_bb->getRotation();
//                specChannelMinX = old_bb->getSpecChannelMinX(),
//                specChannelMaxX = old_bb->getSpecChannelMaxX(),
//                mtv_lastActiveSection = old_bb->mtv_getLastActiveSection();
//    double      specPhysMinX,
//                specPhysMaxX,
//                specPhysMinY,
//                specPhysMaxY,
//                imageMinValue,
//                imageMaxValue;
//    bool        tableViewDirty,
    bool        invertColormap  = old_bb->getInvertColormap(),
                flipX           = old_bb->getFlipX(),
                flipY           = old_bb->getFlipY();
//                hasSpectrum,
//                hasImage,
//                hasCube,
//                hasStrArr,
//                hasMixedView;
//    double              *cubeCenter,
//                        *lineWidth;
//    int                 linecont1center,
//                        linecont1width,
//                        linecont2center,
//                        linecont2width;
//    bool                linecont1,
//                        linecont2;
//    BufferAppearance    bufAppearance;
//    QFitsMarkers        *cursorSpectrumMarkers;  // the pixels taken into account
//    Fits                *cubeDisplay1D;          // the 1D view for 3D data or
                                                 // spectrum view for QFitsWidgetTable
//    QFitsSingleBuffer   *lockedSingleBuffer,
//                        *markedSingleBuffer;
//    QFitsMultiBuffer    *myMultiBuffer;
//    QFV::Orientation    cubeSpecOrientation = old_bb->getCubeSpecOrientation();
    bool usedInCombine          = old_bb->getUsedInCombine();

    if (old_bb != NULL) {
        delete old_bb;
    }

    QFitsBaseBuffer *new_bb = addNewBuffer(id, dpt);

    if (resetGUIsettings) {
        // set GUI settings
    //    new_bb->setViewMode(viewMode);
        new_bb->setImageScaleRange(imageScaleRange);
        new_bb->setCubeMode(cubeMode);
        new_bb->setColormap(colormap);
        new_bb->setZoomFactor(zoomIndex);
        new_bb->setZoomFactor_3Dwire(zoom_3Dwire);
        new_bb->setBrightness(brightness);
        new_bb->setContrast(contrast);
        new_bb->setImageScalingMethod(imageScaleMethod);
        new_bb->setRotation(rotation);
        new_bb->setInvertColormap(invertColormap);
        new_bb->setFlipX(flipX);
        new_bb->setFlipY(flipY);
    }

    new_bb->setUsedInCombine(usedInCombine);

    return new_bb;
}

//
// called by QFitsMainWindow::dpuserVar(): Fits is changed in dpuser, so reset
// the associated buffer
//
QFitsBaseBuffer* QFitsMainView::updateBuffer(std::string id,
                                             dpuserType *dpt)
{
    switch (dpt->type) {
    case typeFits:
    case typeStrarr:
    case typeDpArr:
        return recreateBuffer(id, dpt);
        break;
    default:
        break;
    }
    return NULL;
}

void QFitsMainView::initialResize() {
    myResizeEvent(size().width(), size().height());
}

void QFitsMainView::resizeEvent(QResizeEvent *e) {
    myResizeEvent(e->size().width(), e->size().height());
}

void QFitsMainView::myResizeEvent(int wMainView, int hMainView) {
    int hWedge = wedge->minimumHeight();
    if (grid != NULL) {
        grid->setGeometry(QRect(0, 0,
                          wMainView, hMainView - hWedge));
        wedge->setGeometry(0, hMainView - hWedge,
                           wMainView, hWedge);
    } else {
        // resize all buffers (this way they will have
        //the correct size when switching buffers)
        QFitsBaseBuffer *bb = NULL;
//        QMapIterator<std::string, QFitsBaseBuffer*> i(bufferMap);
//        while (i.hasNext()) {
//            i.next();
        for (auto var:bufferMap) {
            bb = var.second;
            if (bb != NULL) {
                if (!bb->getAppearance().hideWedge) {
                    bb->setGeometry(0, 0,
                                    wMainView, hMainView - hWedge);
                    wedge->setGeometry(0, hMainView - hWedge,
                                       wMainView, hWedge);
                } else {
                    bb->setGeometry(0, 0,
                                    wMainView, hMainView);
                }
            }
        }
    }
}

void QFitsMainView::setAutoScale(bool scale) {
    autoScale = scale;
    if (autoScale) {
        getCurrentBuffer()->updateScaling();
    }
}

void QFitsMainView::saveImage() {
    getCurrentBuffer()->saveImage();
}

void QFitsMainView::saveFits() {
    getCurrentBuffer()->saveFits();
    fitsMainWindow->dpuserVar(getCurrentBufferIndex());
}

void QFitsMainView::printImage() {
    getCurrentBuffer()->printImage();
}

void QFitsMainView::updateOrientation() {
    getCurrentBuffer()->orientationChanged();
}

void QFitsMainView::enableMovie(bool b) {
    getCurrentBuffer()->enableMovie(b);
}

void QFitsMainView::setColormap(QAction *action) {
    getCurrentBuffer()->setColormap(action);
}

void QFitsMainView::setInvertColormap(bool val) {
    getCurrentBuffer()->setInvertColormap(val);
}

void QFitsMainView::setBrightness(int i) {
    getCurrentBuffer()->setBrightness(i);
}

void QFitsMainView::setContrast(int i) {
    getCurrentBuffer()->setContrast(i);
}

void QFitsMainView::setMovieSpeed(int i) {
    getCurrentBuffer()->setMovieSpeed(i);
}

void QFitsMainView::keyPressEvent(QKeyEvent *e) {
//    if (cursor().shape() != Qt::WaitCursor) {
//        if ((e->key() == Qt::Key_Right) && (e->modifiers() & Qt::CTRL)) {
//            // if next buffer should be shown
//            fitsMainWindow->bufferChangedByKey = true;
//            fitsMainWindow->fillBufferMenu();
//            int currentKey = fitsMainWindow->bufferMenuList.key(getCurrentBufferIndex().c_str(), 0);
//            if (fitsMainWindow->bufferMenuList.value(currentKey + 1, "") != "") {
//                    fitsMainWindow->buffersClicked(currentKey + 6);
//            } else {
//                    fitsMainWindow->buffersClicked(5);
//            }
//        } else if ((e->key() == Qt::Key_Left) && (e->modifiers() & Qt::CTRL)) {
//            // if previous buffer should be shown
//            fitsMainWindow->bufferChangedByKey = true;
//            fitsMainWindow->fillBufferMenu();
//            int currentKey = fitsMainWindow->bufferMenuList.key(getCurrentBufferIndex().c_str(), 0);
//            if (fitsMainWindow->bufferMenuList.value(currentKey - 1, "") != "") {
//                    fitsMainWindow->buffersClicked(currentKey + 4);
//            } else {
//                    fitsMainWindow->buffersClicked(4 + fitsMainWindow->bufferMenuList.size());
//            }
//        } else {
//            fitsMainWindow->keyPressEvent(e);
//        }
//    } else {
//        fitsMainWindow->keyPressEvent(e);
//    }
}

void QFitsMainView::playTetris() {
    srand(QTime(0,0,0).secsTo(QTime::currentTime()));
    TetrixWindow tetris;
    tetris.setWindowModality(Qt::NonModal);
    tetris.exec();
}

void QFitsMainView::slotRedoManualSpectrum() {
    getCurrentBuffer()->createManualSpectrum();
}
