#include <QMouseEvent>

#include "QFitsBaseBuffer.h"
#include "QFitsSingleBuffer.h"
#include "QFitsMultiBuffer.h"
#include "QFitsBaseWidget.h"
#include "QFitsMainView.h"
#include "lut.h"
#include "QFitsMainWindow.h"
#include "QFitsViewingTools.h"
#include "QFitsTools.h"
#include "QFitsToolBar.h"
#include "QFitsMarkers.h"
#include "QFitsCubeSpectrum.h"
#include "QFitsWedge.h"
#include "RGBDialog.h"

QFitsBaseBuffer::QFitsBaseBuffer(QFitsMainView *main_view)
                                : QWidget(main_view)
{
    myMultiBuffer = NULL;
    init(-1, "");
}

QFitsBaseBuffer::QFitsBaseBuffer(QFitsMultiBuffer *mb)
                                : QWidget(mb)
{
    myMultiBuffer = mb;
    init(-1, "");
}

QFitsBaseBuffer::~QFitsBaseBuffer() {
    if (cursorSpectrumMarkers) {
        delete cursorSpectrumMarkers;
        cursorSpectrumMarkers = NULL;
    }
    free(cubeCenter); cubeCenter = NULL;
    free(lineWidth); lineWidth = NULL;
}

void QFitsBaseBuffer::init(int dbgnr, QString dbg_upper) {
if (dbgnr != -1) {
    dbg_nr = dbgnr;
    if ((dbgnr == 0) & (dbg_upper.size() == 0)){
        dbg_level += "|";
    }
    dbg_level += dbg_upper + QString::number(dbgnr) + "|";
}
    lockedSingleBuffer  = NULL;
    markedSingleBuffer  = NULL;
    dpData              = NULL;
    cubeDisplay1D       = NULL;
    viewMode            = ViewUndefined;
    imageScaleRange     = (dpScaleMode)settings.defaultLimits;
    cubeMode            = DisplayCubeSingle;

    for (int i = 0; i < fitsMainWindow->mapActions->actions().size(); i++) {
        if (fitsMainWindow->mapActions->actions().at(i)->iconText() == "bb") {
            colormap = fitsMainWindow->mapActions->actions().at(i);
            continue;
        }
    }

    zoom                = zoomIndex;
    zoom_3Dwire         = zoomIndex + 1;    // set to 200%
    brightness          = 500;
    contrast            = 500;
    imageScaleMethod    = 0;
    rotation            = 0;
    cubeCenter          = (double*)calloc(QFV::Wavelength+1, sizeof(double)); //cubeCenter[0] isn't used, just here for simplifying indexing
    lineWidth           = (double*)calloc(QFV::Wavelength+1, sizeof(double)); //lineWidth[0]  isn't used, just here for simplifying indexing
    specChannelMinX     = 0;
    specChannelMaxX     = 0;
    specPhysMinY        = 0.;
    specPhysMaxY        = 0.;
    specPhysMinX        = 0.;
    specPhysMaxX        = 0.;
    imageMinValue       = 0.;
    imageMaxValue       = 0.;
    cubeSpecOrientation = QFV::Vertical;
    mtv_lastActiveSection = 0;
    mtv_lastActiveOrientation = QFV::Vertical;

    dirty               = true;
    tableViewDirty      = true;
    invertColormap      = false;
    flipX               = false;
    flipY               = false;
    usedInCombine       = false;
    hasSpectrum         = false;
    hasImage            = false;
    hasCube             = false;
    hasStrArr           = false;
    bufAppearance.hideWedge                     = false;
    bufAppearance.hideViewingtools              = false;
    bufAppearance.hideViewingtoolsCutsplot      = false;
    bufAppearance.hideSpectrum                  = false;
    bufAppearance.enableSpectrumControls        = false;
    bufAppearance.showToolbarZoom               = false;
    bufAppearance.showToolbarOrientation        = false;
    bufAppearance.showToolbarScaling            = false;
    bufAppearance.showToolbarMovie              = false;
    bufAppearance.showToolbarArithmetics        = false;
    bufAppearance.showToolbarCube               = false;

    bufAppearance.toolsVisible                  = Tools_None;

    // SolidLine, DashLine
    // Qt::Dense1Pattern is almost Qt::SolidPattern
    // Qt::Dense7Pattern is almost Qt::NoBrush
    cursorSpectrumMarkers = new QFitsMarkers(QColor(0, 200, 0), QColor(0, 255, 0, 70),
                                             QColor(200, 0, 0), QColor(255, 0, 0, 70),
                                             QColor(0, 200, 200), QColor(0, 255, 255, 70),
                                             Qt::SolidLine,
                                             Qt::SolidPattern);
}

void QFitsBaseBuffer::activateBuffer() {
    fitsMainWindow->main_view->hideAllBuffers();
}

// handle QFitsViewingTools
BufferAppearance QFitsBaseBuffer::updateAppearance() {
    show();

    // basic setup depending on selected view
    // will be modified in QFitsSingleBuffer/QFitsMultiBuffer
    // depending on kind of data (1D/2D/3D)

    //
    // set window caption
    //
    bufAppearance.windowTitle = fitsMainWindow->createCaption(fitsMainWindow->main_view->getCurrentBufferIndex(), true);

    //
    // set wedge appearance
    //
    switch (viewMode) {
    case ViewImage:
    case ViewContour:
//#ifdef HAS_VTK
    case View3D:
//#endif
        bufAppearance.hideWedge = false;
        break;
    case ViewWiregrid:
    case ViewTable:
        bufAppearance.hideWedge = true;
        break;
    default:
        break;
    }

    //
    // set ViewingTools appearance
    //
    if (settings.showViewingTools != 0) {
        switch (viewMode) {
        case ViewImage:
            bufAppearance.hideViewingtools = false;
            bufAppearance.hideViewingtoolsCutsplot = false;
            break;
        case ViewWiregrid:
        case ViewContour:
        case ViewTable:
//#ifdef HAS_VTK
        case View3D:
//#endif
            bufAppearance.hideViewingtools = false;
            bufAppearance.hideViewingtoolsCutsplot = true;
            break;        
        default:
            break;
        }
    }

    //
    // set CubeSpectrum-Appearance
    //
    bufAppearance.hideSpectrum = false; // updated in QFitsSingleBuffer::updateAppearance()

    switch (viewMode) {
    case ViewImage:
    case ViewWiregrid:
    case ViewContour:
//#ifdef HAS_VTK
    case View3D:
//#endif
        bufAppearance.enableSpectrumControls = true;
        break;
    case ViewTable:
        bufAppearance.enableSpectrumControls = false;
        break;
    default:
        break;
    }

    //
    // set Toolbar-Appearance
    //
    switch (viewMode) {
    case ViewImage:
        bufAppearance.showToolbarScaling      = true;
        bufAppearance.showToolbarArithmetics  = true;
        // done in QFitsSingleBuffer::updateAppearance()
        // bufAppearance.showToolbarZoom        = false;
        // bufAppearance.showToolbarOrientation = false;
        // bufAppearance.showToolbarMovie       = false;
        // bufAppearance.showToolbarCube        = false;
        break;
    case ViewWiregrid:
        bufAppearance.showToolbarScaling      = true;
        bufAppearance.showToolbarArithmetics  = true;
        bufAppearance.showToolbarZoom         = false;
        bufAppearance.showToolbarOrientation  = false;
        bufAppearance.showToolbarMovie        = false;
        bufAppearance.showToolbarCube         = false;
        break;
    case ViewContour:
        bufAppearance.showToolbarScaling      = true;
        bufAppearance.showToolbarArithmetics  = true;
        bufAppearance.showToolbarZoom         = true;
        bufAppearance.showToolbarOrientation  = true;
        bufAppearance.showToolbarMovie        = false;
        bufAppearance.showToolbarCube         = false;
        break;
    case ViewTable:
        bufAppearance.showToolbarScaling      = false;
        bufAppearance.showToolbarArithmetics  = true;
        bufAppearance.showToolbarZoom         = false;
        bufAppearance.showToolbarOrientation  = false;
        // done in QFitsSingleBuffer::updateAppearance()
        //bufAppearance.enableToolbarMovie        = false;
        bufAppearance.showToolbarCube         = false;
        break;
//#ifdef HAS_VTK
    case View3D:
        bufAppearance.showToolbarScaling      = true;
        bufAppearance.showToolbarArithmetics  = false;
        bufAppearance.showToolbarZoom         = true;
        bufAppearance.showToolbarOrientation  = false;
        bufAppearance.showToolbarMovie        = false;
        bufAppearance.showToolbarCube         = true;
        break;
//#endif
    default:
        break;
    }

    return bufAppearance;
}

std::string QFitsBaseBuffer::getBufferIndex() {
    return fitsMainWindow->main_view->getCurrentBufferIndex();
}

void QFitsBaseBuffer::setOrientation(int r, bool flip, bool flop) {
    rotation = r;
    flipX = flip;
    flipY = flop;
}

void QFitsBaseBuffer::notifyCombiner() {
    if ((usedInCombine)  &&
        ((viewMode == ViewImage) || (viewMode == ViewContour)))
    {
            fitsMainWindow->combinedialog->updateColourImage();
    }
}

QVector<QRgb>* QFitsBaseBuffer::setActualColortable() {
     QVector<QRgb> *colorVec;

    // calculate the colormap scaling
    int bottom, top, width;
    QRgb current2Table[NCOLORS];
    calculateColortableScaling(current2Table, &bottom, &top, &width);
    if ((bottom == -1) && (top == -1) && (width == -1)) {
        // cursor outside of FitsWidget: do nothing
        return NULL;
    }

    colorVec = new QVector<QRgb>(NCOLORS);
    // apply colortable here
    int value, i;
    for (i = bottom; i < top; i++) {
        value = (i - bottom) * (NCOLORS-1) / width;
        if ((value > -1) && (value < NCOLORS) && (i > -1) && (i < NCOLORS)) {
            colorVec->replace(i, current2Table[value]);
        }
    }
    if (top < (NCOLORS-1)) {
        for (i = top; i <= NCOLORS-1; i++) {
            colorVec->replace(i, current2Table[NCOLORS-1]);
        }
    }
    if (bottom > 0) {
        for (i = 0; i < bottom; i++) {
            colorVec->replace(i, current2Table[0]);
        }
    }
    if (invertColormap) {
        QRgb s;
        for (int i = 0; i < NCOLORS/2; i++) {
            s = colorVec->at(i);
            colorVec->replace(i, colorVec->at(NCOLORS-1 - i));
            colorVec->replace(NCOLORS-1 - i, s);
        }
    }

    return colorVec;
}

bool QFitsBaseBuffer::isMultiBufferChild() {
    if (myMultiBuffer != NULL) {
        return true;
    } else {
        return false;
    }
}

int QFitsBaseBuffer::indexOfMultiBuffer() {
    int index = -1;
    if (isMultiBufferChild()) {
        index = myMultiBuffer->indexOf(this);
    }

    return index;
}

QString QFitsBaseBuffer::getExtensionIndicesString() {
    QString extensionString;
    QVector<int> extensionVec = getExtensionIndicesVec();

    for (int i = 0; i < extensionVec.size(); i++) {
        extensionString += "[" + QString::number(extensionVec.at(i)) + "]";
    }

    return extensionString;
}

QVector<int> QFitsBaseBuffer::getExtensionIndicesVec() {
    QVector<int> extensionVec;
    int index = indexOfMultiBuffer();

    if (index > -1) {
        extensionVec += myMultiBuffer->getExtensionIndicesVec();
        extensionVec += index;
    }

    return extensionVec;
}

double QFitsBaseBuffer::getZoomFactor() {
    return convertZoomIndexToDouble(zoom);
}

void QFitsBaseBuffer::setZoomFactor(double zoomFactor) {
    zoom = convertZoomDoubleToIndex(zoomFactor);
}

double QFitsBaseBuffer::getZoomFactor_3Dwire() {
    return convertZoomIndexToDouble(zoom_3Dwire);
}

void QFitsBaseBuffer::setZoomFactor_3Dwire(double zoomFactor) {
    zoom_3Dwire = convertZoomDoubleToIndex(zoomFactor);
}

Fits* QFitsBaseBuffer::getCubeDisplay1D() {
    if (isMultiBufferChild()) {
        return myMultiBuffer->getCubeDisplay1D();
    } else {
        return cubeDisplay1D;
    }
}

void  QFitsBaseBuffer::setCubeDisplay1D(Fits *f) {
    if ((f == NULL) || (f->Naxis(0) == 1)) {
        if (isMultiBufferChild()) {
            myMultiBuffer->setCubeDisplay1D(f);
        } else {
            if ((cubeDisplay1D != NULL) && (cubeDisplay1D != f)) {
                delete cubeDisplay1D; cubeDisplay1D = NULL;
            }
            cubeDisplay1D = f;
        }
    } else {
        if (isMultiBufferChild()) {
            myMultiBuffer->setCubeDisplay1D(NULL);
        }
    }
}

QFitsSingleBuffer* QFitsBaseBuffer::getLockedSingleBuffer() {
    if (isMultiBufferChild()) {
        return myMultiBuffer->getLockedSingleBuffer();
    } else {
        return lockedSingleBuffer;
    }
}

void QFitsBaseBuffer::setLockedSingleBuffer(QFitsSingleBuffer *sb) {
    if (isMultiBufferChild()) {
        myMultiBuffer->setLockedSingleBuffer(sb);
    } else {
        lockedSingleBuffer = sb;
    }
}

QFitsSingleBuffer* QFitsBaseBuffer::getMarkedSingleBuffer() {
    if (isMultiBufferChild()) {
        return myMultiBuffer->getMarkedSingleBuffer();
    } else {
        return markedSingleBuffer;
    }
}

void QFitsBaseBuffer::setMarkedSingleBuffer(QFitsSingleBuffer *sb, bool fromLock) {
    if (isMultiBufferChild()) {
        myMultiBuffer->setMarkedSingleBuffer(sb, fromLock);
    } else {
        bool locked = (getLockedSingleBuffer() != NULL);
        if ((locked && fromLock) || !locked) {
            QFitsSingleBuffer *rememberMe = sb;
            if (markedSingleBuffer == NULL) {
                // no Buffer has been marked, mark it now
                markedSingleBuffer = sb;
                if (rememberMe->isMultiBufferChild()) {
                    QFitsBaseView *bv = rememberMe->getState()->getView();
                    bv->switchBackgroundColor();
                    bv->update();
                }
            } else {
                if ((markedSingleBuffer == sb) && locked) {
                    // do nothing here
                } else if ((markedSingleBuffer == sb) || (sb == NULL)) {
                    // marked buffer is the same, unmark it now
                    rememberMe = markedSingleBuffer;
                    markedSingleBuffer = NULL;
                    if (rememberMe->isMultiBufferChild()) {
                        QFitsBaseView *bv = rememberMe->getState()->getView();
                        bv->switchBackgroundColor();
                        bv->update();
                    }
                } else {
                    // marked buffer is another one, unmark the other and
                    // mark this one
                    if (markedSingleBuffer->isMultiBufferChild()) {
                        QFitsBaseView *bv = markedSingleBuffer->getState()->getView();
                        bv->switchBackgroundColor();
                        bv->update();
                    }
                    if (rememberMe->isMultiBufferChild()) {
                        QFitsBaseView *bv = rememberMe->getState()->getView();
                        bv->switchBackgroundColor();
                        bv->update();
                    }
                    fitsMainWindow->spectrum->update();
                    markedSingleBuffer = sb;
                    if (fitsMainWindow->headerVisible()) {
                        fitsMainWindow->DisplayFITSHeader();
                    }
                }
            }
        }
    }
}

int QFitsBaseBuffer::mtv_getLastActiveSection() {
    if (isMultiBufferChild()) {
        return myMultiBuffer->mtv_getLastActiveSection();
    } else {
        return mtv_lastActiveSection;
    }
}

QFV::Orientation QFitsBaseBuffer::mtv_getLastActiveOrientation() {
    if (isMultiBufferChild()) {
        return myMultiBuffer->mtv_getLastActiveOrientation();
    } else {
        return mtv_lastActiveOrientation;
    }
}

void QFitsBaseBuffer::mtv_setLastActiveSection(int section, QFV::Orientation  orientation) {
    if (isMultiBufferChild()) {
        myMultiBuffer->mtv_setLastActiveSection(section, orientation);
    } else {
        mtv_lastActiveSection = section;
        mtv_lastActiveOrientation = orientation;
    }
}

void  QFitsBaseBuffer::mtv_setMinMaxXbrute(int min, int max) {
    specPhysMinX = min;
    specPhysMaxX = max;

    specChannelMinX = min;
    specChannelMaxX = max;
}

QFV::Orientation QFitsBaseBuffer::getCubeSpecOrientation() {
    if ((cubeSpecOrientation == Qt::Vertical) &&
        (dpData->fvalue != NULL) &&
        (dpData->fvalue->Naxis(0) == 1))
    {
        return QFV::Horizontal; // return here orientation of dim 1
    }
    return cubeSpecOrientation;
}

double QFitsBaseBuffer::getCubeCenter(QFV::Orientation o) {
    if (o == QFV::Default) {
        o = getCubeSpecOrientation();
    }
    return cubeCenter[o];
}

double QFitsBaseBuffer::getLineWidth(QFV::Orientation o) {
    if (o == QFV::Default) {
        o = getCubeSpecOrientation();
    }
    return lineWidth[o];
}

void QFitsBaseBuffer::calculateColortableScaling(QRgb *current2Table,
                                                 int *bottom, int *top,
                                                 int *width) {
    QString colormapStr = colormap->iconText();

    int x, y;
    if (mapcolors.count(colormapStr)) {
        type_singlecolormap themap = mapcolors[colormapStr];
        for (int i = 0; i < 256; ++i) {
             current2Table[i] = qRgb(themap.red[i],
                                     themap.green[i],
                                     themap.blue[i]);
        }
    } else {
        for (int i = 0; i < 256; ++i) {
            current2Table[i] = qRgb(i, i, i);
        }
    }

    x = brightness;
    y = contrast;

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

void QFitsBaseBuffer::mousePressEvent(QMouseEvent *e) {
    dp_debug("mouse pressed: %p", this);
    if (e->button() == Qt::LeftButton) {
        fitsMainWindow->main_view->setCurrentBufferFromWidget(this);
    }
}

double QFitsBaseBuffer::convertZoomIndexToDouble(int zoomInt) {
    double zoomDbl = 1.;
    if (zoomInt > zoomIndex) {
        zoomDbl = zoomInt - zoomIndex + 1;
    } else if (zoomInt < zoomIndex) {
        zoomDbl = 1. / (zoomIndex - zoomInt + 1);
    }
    return zoomDbl;
}

int QFitsBaseBuffer::convertZoomDoubleToIndex(double zoomDbl) {
    zoomDbl = adjustZoomValuePrecise(zoomDbl);
    int zoomInt = 10;
    if (zoomDbl > 1.0) {
        zoomInt = int(zoomDbl / 1.0 - 1.0 + 0.5) + zoomIndex;
    } else if (zoomDbl < 1.0) {
        zoomInt = zoomIndex - int(1.0 / zoomDbl) + 1;
    }
    return zoomInt;
}

// adjusts the zoom level to the lower index (needed for "Fit window")
double QFitsBaseBuffer::adjustZoomValueLower(double zoomValue) {
    double adjustedZoomValue = -1.;
    if (zoomValue > 0.) {
        adjustedZoomValue = 1.;
        if (zoomValue >= 1.) {
            while (adjustedZoomValue <= zoomValue) {
                adjustedZoomValue += 1.;
            }
            adjustedZoomValue -= 1.;
        } else if (zoomValue < 1.) {
            int factor = 2;
            double tmp = adjustedZoomValue;
            while (tmp > zoomValue) {
                tmp = adjustedZoomValue;
                tmp /= factor++;
            }
            adjustedZoomValue /= --factor;
        }
    }
    return adjustedZoomValue;
}

double QFitsBaseBuffer::adjustZoomValuePrecise(double zoomValue) {
    double adjustedZoomValue = -1.;
    if (zoomValue > 0.) {
        adjustedZoomValue = 1.;
        if (zoomValue >= 1.) {
            adjustedZoomValue = zoomValue;
        } else if (zoomValue < 1.) {
            int     zoomIntHi = zoomIndex - int(1.0 / zoomValue) + 1;
            double  zoomDblHi = convertZoomIndexToDouble(zoomIntHi),
                    zoomDblLo  = convertZoomIndexToDouble(zoomIntHi - 1);
            if ((zoomDblHi - zoomValue) > (zoomValue - zoomDblLo)) {
                adjustedZoomValue = zoomDblLo;
            } else {
                adjustedZoomValue = zoomDblHi;
            }
        }
    }
    return adjustedZoomValue;
}
