#include <QFileDialog>
#include <QResizeEvent>

#include "QFitsSingleBuffer.h"
#include "QFitsMultiBuffer.h"
#include "QFitsMainWindow.h"
#include "QFitsMainView.h"
#include "QFitsMarkers.h"
#include "QFitsWidget1D.h"
#include "QFitsWidget2D.h"
#include "QFitsWidget3D.h"
#include "QFitsWidgetWiregrid.h"
#include "QFitsWidgetContour.h"
#include "QFitsWidgetTable.h"
#ifdef HAS_VTK
    #include "QFitsView3D.h"        // should go away here
#endif
#include "QFitsCubeSpectrum.h"
#include "QFitsViewingTools.h"
#include "QFitsWedge.h"
#include "fits.h"
#include "dialogs.h"
#include "imred.h"
#include "qtdpuser.h"

#include <gsl/gsl_math.h>
#include <gsl/gsl_sort.h>
#include <gsl/gsl_statistics.h>

QFitsSingleBuffer::QFitsSingleBuffer(QFitsMainView *main_view, dpuserType *dpt,
                                     int dbg, QString dbg_upper)
                                    : QFitsBaseBuffer(main_view)
{
    init(dpt, dbg, dbg_upper);
}

QFitsSingleBuffer::QFitsSingleBuffer(QFitsMultiBuffer *mb, dpuserType* dpt,
                                     int dbg, QString dbg_upper)
                                   : QFitsBaseBuffer(mb)
{
    init(dpt, dbg, dbg_upper);
}

QFitsSingleBuffer::~QFitsSingleBuffer() {
    if (widgetState != NULL) {
        delete widgetState; widgetState = NULL;
    }
    if (manualSpectrum) {
        delete manualSpectrum; manualSpectrum = NULL;
    }
    if (cubeDisplay2D) {
        delete cubeDisplay2D; cubeDisplay2D = NULL;
    }
    if (image) {
        delete image; image = NULL;
    }
}

void QFitsSingleBuffer::init(dpuserType *dpt, int dbg, QString dbg_upper) {
    QFitsBaseBuffer::init(dbg, dbg_upper);
    dp_debug("   INIT SB (%p) %s\n", this, dbg_level.toStdString().c_str()); fflush(stdout);
    QReadLocker locker(&buffersLock);

    prevWidgetState     = NULL;
    widgetState         = NULL;
    dpData              = dpt;
    hasManualSpectrum   = false;

    setDirty(true);

    if (dpData->type == typeFits) {
        switch (Naxis(0)) {
        case 1:
            hasSpectrum = true;
            setCubeSpecOrientation(QFV::Horizontal);
            break;
        case 2:
            hasImage = true;
            setCubeSpecOrientation(QFV::Vertical);
            break;
        case 3 :
            hasCube = true;
            setCubeSpecOrientation(QFV::Wavelength);
            break;
        default:
            break;
        }
    } else if (dpData->type == typeStrarr) {
        hasStrArr = true;
    }

    if ((dpData->fvalue != NULL) &&
        (dpData->fvalue->Naxis(0) > 1))
    {
        xcenter = (double)(dpData->fvalue->Naxis(1)) / 2.;
        ycenter = (double)(dpData->fvalue->Naxis(2)) / 2.;
    }

    widthVisible    = -1.;
    heightVisible   = -1.;
    ignoreInCube    = false;
    ignoreValue     = 1e-10;
//#ifdef HAS_VTK
//    camSettings     = NULL;
//    firstView3D     = true;
//#endif

    // create initial Widget
    if ((prevWidgetState == NULL) && (widgetState == NULL)) {
        widgetList.clear();
        // create initial QFitsWidget
        manualSpectrum = new Fits();
        cubeDisplay2D = new Fits();
        image = new QImage(1, 1, QImage::Format_Indexed8);
        image->setColorCount(NCOLORS);

        if (dpData->showAsTable()) {
            if (isMultiBufferChild() && (myMultiBuffer->getViewMode() == ViewTable)) {
                // don't append QFitsWidgetTable here because QFitsMultiBuffer is already
                // taking care of this
            } else {
                widgetList.append(new QFitsWidgetTable(this));
            }
            viewMode = ViewTable;
        } else {
            if (Naxis(0) == 1) {
                widgetList.append(new QFitsWidget1D(this));
            } else {
                widgetList.append(new QFitsWidget2D(this));
            }
            viewMode = ViewImage;
        }

        prevWidgetState = NULL;
        widgetState = NULL;
        if (widgetList.size() > 0) {
            widgetState = widgetList.last();
        }

        if (Naxis(0) > 1) {
            connect(this, SIGNAL(newBrightness(int)),
                    fitsMainWindow->imagedisplay->spinboxBrightness, SLOT(setValue(int)));
            connect(this, SIGNAL(newContrast(int)),
                    fitsMainWindow->imagedisplay->spinboxContrast, SLOT(setValue(int)));
        }
    } else if (widgetState != NULL) {
        // update widgetList
        prevWidgetState = widgetState;
        widgetState = NULL;

        if (dpData->showAsTable()) {
            for (int i = 0; i < widgetList.size(); i++) {
                if (dynamic_cast<QFitsWidgetTable*>(widgetList.at(i)) != NULL) {
                    widgetList.removeAt(i);
                    widgetState = NULL;
                    break;
                }
            }
            if (widgetState == NULL) {
                widgetList.append(new QFitsWidgetTable(this));
                widgetState = widgetList.last();
            }
            viewMode = ViewTable;
        } else {
            if (Naxis(0) == 1) {
                // remove any present QFitsWidget2D
                for (int i = 0; i < widgetList.size(); i++) {
                    if (dynamic_cast<QFitsWidget2D*>(widgetList.at(i)) != NULL) {
                        widgetList.removeAt(i);
                        break;
                    }
                }

                for (int i = 0; i < widgetList.size(); i++) {
                    if (dynamic_cast<QFitsWidget1D*>(widgetList.at(i)) != NULL) {
                        widgetState = widgetList.at(i);
                    }
                }
                if (widgetState == NULL) {
                    widgetList.append(new QFitsWidget1D(this));
                    widgetState = widgetList.last();
                }
            } else {
                // remove any present QFitsWidget1D
                for (int i = 0; i < widgetList.size(); i++) {
                    if (dynamic_cast<QFitsWidget1D*>(widgetList.at(i)) != NULL) {
                        widgetList.removeAt(i);
                        break;
                    }
                }

                for (int i = 0; i < widgetList.size(); i++) {
                    if (dynamic_cast<QFitsWidget2D*>(widgetList.at(i)) != NULL) {
                        widgetState = widgetList.at(i);
                        break;
                    }
                }
                if (widgetState == NULL) {
                    widgetList.append(new QFitsWidget2D(this));
                    widgetState = widgetList.last();
                }
            }

            // init any present QFitsWidgetTable
            for (int i = 0; i < widgetList.size(); i++) {
                if (dynamic_cast<QFitsWidgetTable*>(widgetList.at(i)) != NULL) {
                    dynamic_cast<QFitsWidgetTable*>(widgetList.at(i))->init(dpData);
                    break;
                }
            }

            viewMode = ViewImage;
        }
    }

    ResetSpectrum();
    ResetContinuum();
}

double QFitsSingleBuffer::calcZoomFitWindow(int w, int h, FitZoom fz) {


    if ((Naxis(0) > 1) &&
        (dpData->type == typeFits) &&
         dpData->fvalue != NULL)
    {
        if (dpData->fvalue != NULL) {
            QReadLocker locker(&buffersLock);

            double  value   = -1.,
                    imgW    = 0.,
                    imgH    = 0.,
                    widW    = w,
                    widH    = h;

            if ((rotation % 180) == 0) {
                imgW = (double)Naxis(1);
                imgH = (double)Naxis(2);
            } else {
                imgW = (double)Naxis(2);
                imgH = (double)Naxis(1);
            }

            if (fz == FitZoomWindow) {
                // Fit Window
                if ((imgW > widW) || (imgH > widH)) {
                    // image > widget
//@                    value = imgW / widW;
                    value = widW / imgW;
//@                    if (imgH / widH < value) {
//@                        value = imgH / widH;
                    if (widH / imgH < value) {
                        value = widH / imgH;
                    }
                } else {
                    // image < widget
                    value = widW / imgW;
                    if (widH / imgH < value) {
                        value = widH / imgH;
                    }
                }
                value = adjustZoomValueLower(value);
            } else if (fz == FitZoomWidth) {
                // Fit Width
                if (imgW > widW) {
                    // image > widget
//@                    value = imgW / widW;
                    value = widW / imgW;
                } else {
                    // image < widget
                    value = widW / imgW;
                }
                value = adjustZoomValueLower(value);

                if (value*imgH > widH) {
                    // vertical scrollbar is about to show
                    if (value*imgW > (widW - qApp->style()->pixelMetric(QStyle::PM_ScrollBarExtent))) {
                        // if (width-scrollbar) is less than zoomed width, decrease value
                        int tmp = convertZoomDoubleToIndex(value);
                        value = convertZoomIndexToDouble(tmp - 1);
                    }
                }
            } else if (fz == FitZoomHeight) {
                // Fit Height
                if (imgH > widH) {
                    // image > widget
//@                    value = imgH / widH;
                    value = widH / imgH;
                } else {
                    // image < widget
                    value = widH / imgH;
                }
                value = adjustZoomValueLower(value);

                if (value*imgW > widW) {
                    // horizontal scrollbar is about to show
                    if (value*imgH > (widH - qApp->style()->pixelMetric(QStyle::PM_ScrollBarExtent))) {
                        // if (height-scrollbar) is less than zoomed height, decrease value
                        int tmp = convertZoomDoubleToIndex(value);
                        value = convertZoomIndexToDouble(tmp - 1);
                    }
                }
            }
            return value;
        }
    }
    return -1.;
}

void QFitsSingleBuffer::resizeEvent(QResizeEvent *s) {
//dp_debug("QFitsSingleBuffer::resizeEvent() [%p, SB%s] (%d, %d)\n", this, dbg_level.toStdString().c_str(), s->size().width(), s->size().height()); fflush(stdout);
    if (widgetState != NULL) {
// TO DO: resize all Views/widgetStates!!!!
            widgetState->setGeometry(0, 0,
                                     s->size().width(), s->size().height());
    }
}

void QFitsSingleBuffer::ChangeWidgetState(dpViewMode mode) {
    bool found = false;
    int found_index = 0;
    for (int i = 0; i < widgetList.size(); i++) {
        if (found) {
            break;
        }
        if (dpData->type == typeFits) {
            switch (mode) {
            case ViewImage:
                if (Naxis(0) < 2) {
                    if (dynamic_cast<QFitsWidget1D*>(widgetList.at(i)) != NULL) {
                        found = true;
                    }
                } else {
                    if (dynamic_cast<QFitsWidget2D*>(widgetList.at(i)) != NULL) {
                        found = true;
                    }
                }
                break;
            case ViewWiregrid:
                if (dynamic_cast<QFitsWidgetWiregrid*>(widgetList.at(i)) != NULL) {
                    found = true;
                }
                break;
            case ViewContour:
                if (dynamic_cast<QFitsWidgetContour*>(widgetList.at(i)) != NULL) {
                    found = true;
                }
                break;
            case ViewTable:
                if (dynamic_cast<QFitsWidgetTable*>(widgetList.at(i)) != NULL) {
                    found = true;
                }
                break;
#ifdef HAS_VTK
            case View3D:
                if ((Naxis(0) == 3) &&
                    (dynamic_cast<QFitsWidget3D*>(widgetList.at(i)) != NULL))
                {
                    found = true;
                }
                break;
#endif
            default:
                // do nothing
                break;
            }
        } else if (dpData->type == typeStrarr) {
            if (dynamic_cast<QFitsWidgetTable*>(widgetList.at(i)) != NULL) {
                found = true;
            }
        }
        found_index = i;
    }

    if (found) {
        prevWidgetState = widgetState;
        widgetState = NULL;
        if (widgetList.size() > 0) {
            widgetState = widgetList.at(found_index);
        }
    } else {
        // widget hasn't been found, create it
        if (dpData->type == typeFits) {
            switch (mode) {
            case ViewImage:
                if (Naxis(0) < 2) {
                    widgetList.append(new QFitsWidget1D(this));
                } else {
                    widgetList.append(new QFitsWidget2D(this));
                }
                break;
            case ViewWiregrid:
                widgetList.append(new QFitsWidgetWiregrid(this));
                break;
            case ViewContour:
                widgetList.append(new QFitsWidgetContour(this));
                break;
            case ViewTable:
                widgetList.append(new QFitsWidgetTable(this));
                break;
#ifdef HAS_VTK
            case View3D:
                if (Naxis(0) == 3) {
                    widgetList.append(new QFitsWidget3D(this));
                }
                break;
#endif
            default:
                // do nothing
                break;
            }
        } else if (dpData->type == typeStrarr) {
            widgetList.append(new QFitsWidgetTable(this));

        }
        prevWidgetState = widgetState;
        widgetState = widgetList.last();
    }

    viewMode = mode;
}

void QFitsSingleBuffer::activateBuffer() {
dp_debug("QFitsSingleBuffer::activateBuffer()");
    QFitsBaseBuffer::activateBuffer();
    if (dpData->type == typeFits) {
        setCubeDisplay1D(NULL);
        if (Naxis(0) > 1) {
            if (Naxis(0) > 2) {
                createManualSpectrum();
                fitsMainWindow->spectrum->changeWorkArray();
                fitsMainWindow->spectrum->setupMarkers();
            }
            if (isMultiBufferChild() && myMultiBuffer->getHasCube()) {
                fitsMainWindow->spectrum->changeWorkArray();
            }
            enableMovie(false);
        } else {
            // update 1D spectrum plot when changing buffers
            setData();
            setXRange();
        }
        if ((dpData->fvalue->extensionType == BINTABLE) || (dpData->fvalue->extensionType == TABLE)) {
            if (fitsMainWindow->spectrum->isVisible()) {
                fitsMainWindow->spectrum->changeWorkArray();
            }
            QFitsWidgetTable *wt = dynamic_cast<QFitsWidgetTable*>(widgetState);
            if (wt != NULL) {
                wt->restoreState();
            }
        }
    }

    if (dpData->type == typeFits) {
#ifdef HAS_VTK
        if (viewMode == View3D) {
            newData3D();
        } else
#endif
        {
            showCubePlot();
            updateLinemapDialog();
        }
    }
    if (fitsMainWindow->headerVisible()) {
        fitsMainWindow->DisplayFITSHeader();
    }

    if (fitsMainWindow->markposDialog->isVisible()) {
        fitsMainWindow->markposDialog->updateMethod();
    }
    setFocus();
}

BufferAppearance QFitsSingleBuffer::updateAppearance() {
    QFitsBaseBuffer::updateAppearance();

    if (hasSpectrum && !hasImage && !hasCube) {
        bufAppearance.enableColourmapMenu = false;
        bufAppearance.enableScaleMenu = false;
        bufAppearance.enableZoomMenu = false;
    } else {
        bufAppearance.enableColourmapMenu = true;
        bufAppearance.enableScaleMenu = true;
        bufAppearance.enableZoomMenu = true;
    }

    bufAppearance.enableImredMenu = true;
    bufAppearance.enableArithmeticButtons = true;

    // show actual QFitsWidget
    for(int i = 0; i < widgetList.size(); i++){
        widgetList[i]->hide();
    }
    if (widgetState != NULL) {
        widgetState->show();
    }

    // let QFitsMultiBuffer handle by its own
    if (myMultiBuffer == NULL) {
        int dim = Naxis(0);
        // specific setup depending on kind of data (1D/2D/3D)
        // setup depending on view has already been exeuted in QFitsBaseBuffer
        switch (viewMode) {
        case ViewImage:
            if (dim == 3) {
                bufAppearance.hideSpectrum = false;
            } else {
                bufAppearance.hideSpectrum = true;
            }
            if (dim == 1) {
                bufAppearance.hideViewingtools = true;
                bufAppearance.hideViewingtoolsCutsplot = true;
                bufAppearance.hideWedge = true;
            } else {
                if (settings.showViewingTools != 0) {
                    bufAppearance.hideViewingtools = false;
                }
                bufAppearance.hideWedge = false;
            }
            if (dim == 1) {
                bufAppearance.showToolbarZoom         = false;
                bufAppearance.showToolbarOrientation  = false;
                bufAppearance.showToolbarMovie        = false;
                bufAppearance.showToolbarCube         = false;
            } else if (dim == 2) {
                bufAppearance.showToolbarZoom         = true;
                bufAppearance.showToolbarOrientation  = true;
                bufAppearance.showToolbarMovie        = false;
                bufAppearance.showToolbarCube         = false;
            } else {
                bufAppearance.showToolbarZoom         = true;
                bufAppearance.showToolbarOrientation  = true;
                bufAppearance.showToolbarMovie        = true;
                bufAppearance.showToolbarCube         = false;
            }
            break;
        case ViewTable:
            {
            QFitsWidgetTable *wt = dynamic_cast<QFitsWidgetTable*>(widgetState);
            if (wt == NULL) {
                for (int i = 0; i < widgetList.size(); i++) {
                    wt = dynamic_cast<QFitsWidgetTable*>(widgetList.at(i));
                    if (wt != NULL) {
                        break;
                    }
                }
            }
            QFitsTableView *tv = wt->getTableView();
            if (dim < 3) {
                if (tv != NULL) {
                    if (tv->doPlotData()) {
                        bufAppearance.hideSpectrum = false;
                    } else {
                        bufAppearance.hideSpectrum = true;
                    }
                } else {
                    bufAppearance.hideSpectrum = true;
                }
            } else {
                // Fits-cube displayed as table -> always show spectrum
                bufAppearance.hideSpectrum = false;
            }
            if (dim == 3) {
                bufAppearance.showToolbarMovie = true;
            } else {
                bufAppearance.showToolbarMovie = false;
            }
            }
            break;
        case ViewWiregrid:
        case ViewContour:
            bufAppearance.hideSpectrum = true;
            break;
//#ifdef HAS_VTK
        case View3D:
            if (settings.showViewingTools != 0) {
                bufAppearance.hideViewingtools = false;
            }
            if (dim == 3) {
                bufAppearance.hideSpectrum = false;
            } else {
                bufAppearance.hideSpectrum = true;
            }
            break;
//#endif
        default:
            break;
        }

        // handle focus
        if (fitsMainWindow->bufferChangedByKey) {
            setFocus();
            fitsMainWindow->bufferChangedByKey = false;
        }
        fitsMainWindow->viewActions->actions()[viewMode]->setChecked(true);
    }

    return bufAppearance;
}

dpint64 QFitsSingleBuffer::Naxis(int dim) {
    QReadLocker locker(&buffersLock);
    if (dpData->type == typeFits) {
        if (dpData->fvalue != NULL) {
            return dpData->fvalue->Naxis(dim);
        }
    } else if (dpData->type == typeStrarr) {
        switch (dim) {
            case 0:
                return 1;
            case 1:
                return dpData->arrvalue->size();
            default:
                return 0;
        }
    }
    return 0;
}

void QFitsSingleBuffer::ResetContinuum() {
    linecont1       = false;
    linecont2       = false;

    if ((dpData->type == typeFits) &&
        (dpData->fvalue != NULL) &&
        (Naxis(3) > 1))
    {
        cubeCenter[QFV::Wavelength] = Naxis(3) / 2.;
        lineWidth[QFV::Wavelength]  = 10.;
        setCubeSpecOrientation(QFV::Wavelength);
        linecont1center = -20;
        linecont1width  = 10;
        linecont2center = 20;
        linecont2width  = 10;
    } else {
        cubeCenter[getCubeSpecOrientation()] = 0;
        lineWidth[getCubeSpecOrientation()] = 0;
        linecont1center = 0;
        linecont1width  = 0;
        linecont2center = 0;
        linecont2width  = 0;
    }
}

void QFitsSingleBuffer::zoomTextChanged(double zoomValue, FitZoom fz) {
    if (fz != FitZoomNone) {
        // "Fit window", "Fit width" or "Fit height"

        // this is only needed because the widgets (hsplitter, spectrum, qtdpuser)
        // in QFitsMainWindow can't be put into a QVBoxLayout, they are in a QSplitter.
        // Furthermore spectrum is not always visible, specially at startup.
        // I think with a layout, this would be more straightforward
        int wedgeSpectrumCorrection = 0;
        if (!fitsMainWindow->zoomCorr_ComboZoomTriggered) {
            if (hasImage || hasCube) {
                wedgeSpectrumCorrection += fitsMainWindow->main_view->getWedge()->minimumHeight();
            }

            if (hasCube) {
                if (!fitsMainWindow->zoomCorr_spectrumVisible) {
                    wedgeSpectrumCorrection += fitsMainWindow->spectrum->minimumHeight()+4;
                }
            }
            if (hasImage && !hasCube) {
                if (fitsMainWindow->zoomCorr_spectrumVisible) {
                    wedgeSpectrumCorrection -= fitsMainWindow->spectrum->minimumHeight()+4;
                }
            }
        }
        fitsMainWindow->zoomCorr_ComboZoomTriggered = false;

        int w = width(),
            h = height() - wedgeSpectrumCorrection;

        zoomValue = calcZoomFitWindow(w, h, fz);
        fitsMainWindow->mytoolbar->setZoomTextCombo(zoomValue);
    }
    setZoomWidget(zoomValue);
}

QFitsWidget1D* QFitsSingleBuffer::getQFitsWidget1D() {
    QFitsWidget1D *widget1D = NULL;
    for (int i = 0; i < widgetList.size(); i++) {
        widget1D = dynamic_cast<QFitsWidget1D*>(widgetList[i]);
        if (widget1D != NULL) {
            break;
        }
    }

    if (widget1D == NULL) {
        // widget hasn't been found, create it
        widget1D = new QFitsWidget1D(this);
        widgetList.append(widget1D);
    }

    return widget1D;
}

QFitsWidget2D* QFitsSingleBuffer::getQFitsWidget2D() {
    QFitsWidget2D *widget2D = NULL;
    for (int i = 0; i < widgetList.size(); i++) {
        widget2D = dynamic_cast<QFitsWidget2D*>(widgetList[i]);
        if (widget2D != NULL) {
            break;
        }
    }

    if (widget2D == NULL) {
        // widget hasn't been found, create it
        widget2D = new QFitsWidget2D(this);
        widgetList.append(widget2D);
    }

    return widget2D;
}

#ifdef HAS_VTK
QFitsWidget3D* QFitsSingleBuffer::getQFitsWidget3D(bool create) {
    QFitsWidget3D *widget3D = NULL;
    for (int i = 0; i < widgetList.size(); i++) {
        widget3D = dynamic_cast<QFitsWidget3D*>(widgetList[i]);
        if (widget3D != NULL) {
            break;
        }
    }

    if ((widget3D == NULL) && create) {
        // widget hasn't been found, create it
        widget3D = new QFitsWidget3D(this);
        widgetList.append(widget3D);
    }

    return widget3D;
}
#endif

QFitsMarkers* QFitsSingleBuffer::getSourceMarkers() {
    QFitsWidget2D *widget = getQFitsWidget2D();
    if (widget != NULL) {
        return widget->getSourceMarkers();
    }
    return NULL;
}

void QFitsSingleBuffer::ScaleImage() {
    if ((dpData->type == typeFits) &&
        (dpData->fvalue != NULL))
    {
        QReadLocker locker(&buffersLock);

        if (getDirty()) {
            tableViewDirty = true;
            QApplication::setOverrideCursor(Qt::WaitCursor);
            if (Naxis(3) > 1) {
                switch (cubeMode) {
                case DisplayCubeAverage:
                    if (ignoreInCube) {
                        cube_avg(*(dpData->fvalue), *(cubeDisplay2D), ignoreValue);
                    } else {
                        cube_avg(*(dpData->fvalue), *(cubeDisplay2D));
                    }
                    break;
                case DisplayCubeMedian:
                    if (ignoreInCube) {
                        cubeDisplay2D->CubeMedian(*(dpData->fvalue), ignoreValue);
                    } else {
                        cubeDisplay2D->CubeMedian(*(dpData->fvalue));
                    }
                    break;
                case DisplayCubeLinemap:
                {
                    Fits cont;
                    int c1 = (int)(cubeCenter[getCubeSpecOrientation()] - lineWidth[getCubeSpecOrientation()] + 0.5),
                        c2 = (int)(cubeCenter[getCubeSpecOrientation()] + lineWidth[getCubeSpecOrientation()] + 0.5);

                    if (c1 > c2) {
                        int c = c1;
                        c1 = c2;
                        c2 = c;
                    }
                    if (c1 < 1) {
                        c1 = 1;
                    }
                    if (c2 > Naxis(3)) {
                        c2 = Naxis(3);
                    }
                    if (ignoreInCube) {
                        cube_avg(*(dpData->fvalue), *(cubeDisplay2D),
                                 c1, c2, ignoreValue);
                    } else {
                        cube_avg(*(dpData->fvalue), *(cubeDisplay2D), c1, c2);
                    }

                    if (linecont1 && linecont2) {
                        *(cubeDisplay2D) *= 2.0;
                    }
                    if (linecont1) {
                        c1 = (int)(cubeCenter[getCubeSpecOrientation()] + linecont1center - linecont1width + 0.5);
                        c2 = (int)(cubeCenter[getCubeSpecOrientation()] + linecont1center + linecont1width + 0.5);
                        if (c1 < 1) {
                            c1 = 1;
                        }
                        if (c2 > Naxis(3)) {
                            c2 = Naxis(3);
                        }
                        if (ignoreInCube) {
                            cube_avg(*(dpData->fvalue), cont, c1, c2, ignoreValue);
                        } else {
                            cube_avg(*(dpData->fvalue), cont, c1, c2);
                        }
                        *(cubeDisplay2D) -= cont;
                    }
                    if (linecont2) {
                        c1 = (int)(cubeCenter[getCubeSpecOrientation()] + linecont2center - linecont2width + 0.5);
                        c2 = (int)(cubeCenter[getCubeSpecOrientation()] + linecont2center + linecont2width + 0.5);
                        if (c1 < 1) {
                            c1 = 1;
                        }
                        if (c2 > Naxis(3)) {
                            c2 = Naxis(3);
                        }
                        if (ignoreInCube) {
                            cube_avg(*(dpData->fvalue), cont, c1, c2, ignoreValue);
                        } else {
                            cube_avg(*(dpData->fvalue), cont, c1, c2);
                        }
                        *(cubeDisplay2D) -= cont;
                    }
                }
                    break;
                default:
                    dpData->fvalue->extractRange(*cubeDisplay2D, -1, -1, -1, -1,
                                                 (int)(cubeCenter[getCubeSpecOrientation()] + 0.5), (int)(cubeCenter[getCubeSpecOrientation()] + 0.5));
                    // undo deflate() called form within extractRange(...)
                    cubeDisplay2D->setNaxis(0, 2);
                    cubeDisplay2D->setNaxis(1, dpData->fvalue->Naxis(1));
                    cubeDisplay2D->setNaxis(2, dpData->fvalue->Naxis(2));
                    break;
                }
            } else {
                cubeDisplay2D->create(1, 1);
            }
        }

        // minmax, linear etc...
        Fits work;
        if (Naxis(0) == 3) {
            // 3D
            calculateMinMax(cubeDisplay2D);
            cubeDisplay2D->normub(work, imageMinValue, imageMaxValue, imageScaleMethod);
        } else {
            // 2D
            calculateMinMax(dpData->fvalue);
            dpData->fvalue->normub(work, imageMinValue, imageMaxValue, imageScaleMethod);
        }

//#ifdef HAS_VTK
        if (viewMode != View3D)
//#endif
        {
            fitsMainWindow->mytoolbar->updateImageMinMax(this);
            fitsMainWindow->mytoolbar->updateSpecMinMaxY(this);
        }

        work.rot90(rotation);
        if (flipX) {
            work.flip(1);
        }
        if (flipY) {
            work.flip(2);
        }

        // copy i1data from Fits to myBuffer->image
        // needed to update QTotalView etc
        copyScaledData2Image(&work);

        // update colortable
        setupColours();

        setDirty(false);
        QApplication::restoreOverrideCursor();
    }
}

// copy image data from scaled Fits-Cube to QImage
// this is used in order to get the range for the colortable
void QFitsSingleBuffer::copyScaledData2Image(Fits *work) {
    if (image != NULL) {
        delete image;
    }
    if ((viewMode == ViewTable) && (work->Naxis(0) < 2)) {
        image = new QImage(1, work->Naxis(1), QImage::Format_Indexed8);
    } else {
        image = new QImage(work->Naxis(1), work->Naxis(2), QImage::Format_Indexed8);
    }
    image->setColorCount(NCOLORS);

    if (work->Naxis(0) < 3) {
        unsigned char *i1data = work->i1data;
        for (int y = image->height() - 1; y >= 0; y--) {
            uchar *p = image->scanLine(y);
            for (int x = 0; x < image->width(); x++) {
                *p++ = *(i1data++);
            }
        }
    }
}

void QFitsSingleBuffer::calculateMinMax(Fits *work) {
    switch (imageScaleRange) {
        case ScaleMinMax: {
            double tmpMin = 0.0,
                   tmpMax = 0.0;
            if (ignoreInCube) {
                work->get_minmax(&tmpMin, &tmpMax, ignoreValue);
                imageMinValue = tmpMin;
                imageMaxValue = tmpMax;
            } else {
                work->get_minmax(&tmpMin, &tmpMax);
                imageMinValue = tmpMin;
                imageMaxValue = tmpMax;
            }
        }
            break;
        case Scale999:
            imageMinValue = work->get_nth(0.001);
            imageMaxValue = work->get_nth(0.999);
            break;
        case Scale995:
            imageMinValue = work->get_nth(0.005);
            imageMaxValue = work->get_nth(0.995);
            break;
        case Scale99:
            imageMinValue = work->get_nth(0.01);
            imageMaxValue = work->get_nth(0.99);
            break;
        case Scale98:
            imageMinValue = work->get_nth(0.02);
            imageMaxValue = work->get_nth(0.98);
            break;
        case Scale95:
            imageMinValue = work->get_nth(0.05);
            imageMaxValue = work->get_nth(0.95);
            break;
        default:
            break;
    }
}

QFitsMarkers* QFitsSingleBuffer::getCursorMarkers() {
    if (isMultiBufferChild()) {
        // this belongs to a QFitsMultiBuffer
        return myMultiBuffer->getCursorMarkers();
    } else {
        // this is a plain QFitsSingleBuffer
        return cursorSpectrumMarkers;
    }
}

int QFitsSingleBuffer::getSpecChannelMinX() {
    if (isMultiBufferChild() && myMultiBuffer->hasCubeSpecHomogenSB() && !isEmptyPrimary()) {
        return myMultiBuffer->getSpecChannelMinX();
    } else {
        return specChannelMinX;
    }
}

void QFitsSingleBuffer::setSpecChannelMinX(int channelMinX) {
    if ((dpData->type == typeFits) &&
        (dpData->fvalue != NULL))
    {
        int dim         = getCubeSpecOrientation(),
            channelMaxX = getSpecChannelMaxX();
        if (channelMaxX != 0) {
            if (((dpData->fvalue->getCDELT(dim) >= 0) && (channelMinX >= channelMaxX)) ||
                ((dpData->fvalue->getCDELT(dim) < 0)  && (channelMinX < channelMaxX)))
            {
                return;
            }
        }

        QReadLocker locker(&buffersLock);
        Fits *f = getCubeDisplay1D();
        if (f == NULL) {
            f = dpData->fvalue;
            if (f->extensionType == EMPTY) {
                return;
            }
        }
        if (f->Naxis(0) != 3) {
            dim = 1;
        }

        double  crval       = f->getCRVAL(dim),
                crpix       = f->getCRPIX(dim),
                cdelt       = f->getCDELT(dim),
                physMin     = pixwave(channelMinX, crpix, crval, cdelt, f->isAxisLog(dim)); //crval + (channelMinX - crpix) * cdelt;

        specChannelMinX = channelMinX;
        specPhysMinX = physMin;

        if (isMultiBufferChild()) {
            myMultiBuffer->setSpecChannelMinX(channelMinX);
            if (cdelt >= 0) {
                myMultiBuffer->setSpecPhysMinX(physMin);
            } else {
                myMultiBuffer->setSpecPhysMaxX(physMin);
            }
        }
    } else if (dpData->type == typeStrarr) {
        specChannelMinX = channelMinX;
        specPhysMinX    = channelMinX;

        if (isMultiBufferChild()) {
            myMultiBuffer->setSpecChannelMinX(channelMinX);
            myMultiBuffer->setSpecPhysMinX(channelMinX);
        }

    }
}

int QFitsSingleBuffer::getSpecChannelMaxX() {
    if (isMultiBufferChild() && myMultiBuffer->hasCubeSpecHomogenSB() && !isEmptyPrimary()) {
        return myMultiBuffer->getSpecChannelMaxX();
    } else {
        return specChannelMaxX;
    }
}

void QFitsSingleBuffer::setSpecChannelMaxX(int channelMaxX) {
    if ((dpData->type == typeFits) &&
        (dpData->fvalue != NULL))
    {
        int dim         = getCubeSpecOrientation(),
            channelMinX = getSpecChannelMinX();
        if (channelMinX != 0) {
            if (((dpData->fvalue->getCDELT(dim) >= 0) && (channelMinX >= channelMaxX)) ||
                ((dpData->fvalue->getCDELT(dim) < 0)  && (channelMinX < channelMaxX)))
            {
                return;
            }
        }

        QReadLocker locker(&buffersLock);
        Fits *f = getCubeDisplay1D();
        if (f == NULL) {
            f = dpData->fvalue;
            if (f->extensionType == EMPTY) {
                return;
            }
        }
        if (f->Naxis(0) != 3) {
            dim = 1;
        }

        double  crval       = f->getCRVAL(dim),
                crpix       = f->getCRPIX(dim),
                cdelt       = f->getCDELT(dim),
                physMax     = pixwave(channelMaxX, crpix, crval, cdelt, f->isAxisLog(dim)); //crval + (channelMaxX - crpix) * cdelt;

        specChannelMaxX = channelMaxX;
        specPhysMaxX = physMax;

        if (isMultiBufferChild()) {
            myMultiBuffer->setSpecChannelMaxX(channelMaxX);
            if (cdelt >= 0) {
                myMultiBuffer->setSpecPhysMaxX(physMax);
            } else {
                myMultiBuffer->setSpecPhysMinX(physMax);
            }
        }
    } else if (dpData->type == typeStrarr) {
        specChannelMaxX = channelMaxX;
        specPhysMaxX    = channelMaxX;

        if (isMultiBufferChild()) {
            myMultiBuffer->setSpecChannelMaxX(channelMaxX);
            myMultiBuffer->setSpecPhysMaxX(channelMaxX);
        }

    }
}

double QFitsSingleBuffer::getSpecPhysMinX() {
    if (isMultiBufferChild() && myMultiBuffer->hasCubeSpecHomogenSB() && !isEmptyPrimary()) {
        return myMultiBuffer->getSpecPhysMinX();
    } else {
        return specPhysMinX;
    }
}

void QFitsSingleBuffer::setSpecPhysMinX(double physMinX) {
    if ((dpData->type == typeFits) &&
        (dpData->fvalue != NULL))
    {
        if ((getSpecPhysMaxX() != 0) &&
            (physMinX >= getSpecPhysMaxX()))
        {
            return;
        }

//@        QReadLocker locker(&buffersLock);
        Fits *f = getCubeDisplay1D();
        if (f != NULL) {
            double  crval       = f->getCRVAL(QFV::Horizontal),
                    crpix       = f->getCRPIX(QFV::Horizontal),
                    cdelt       = f->getCDELT(QFV::Horizontal);
//            int     channelMin  = (int)(crpix + (physMinX - crval) / cdelt + 0.5);
            int     channelMin  = wavepix(physMinX, crpix, crval, cdelt, f->isAxisLog(QFV::Horizontal));

            specPhysMinX    = physMinX;
            specChannelMinX = channelMin;

            if (isMultiBufferChild()) {
                myMultiBuffer->setSpecPhysMinX(physMinX);
                myMultiBuffer->setSpecChannelMinX(channelMin);
            }
        }
    } else if (dpData->type == typeStrarr) {
        specPhysMinX    = physMinX;
        specChannelMinX = (int)(physMinX + .5);

        if (isMultiBufferChild()) {
            myMultiBuffer->setSpecPhysMinX(physMinX);
            myMultiBuffer->setSpecChannelMinX((int)(physMinX + .5));
        }
    }
}

double QFitsSingleBuffer::getSpecPhysMaxX() {
     if (isMultiBufferChild() && myMultiBuffer->hasCubeSpecHomogenSB() && !isEmptyPrimary()) {
        return myMultiBuffer->getSpecPhysMaxX();
    } else {
        return specPhysMaxX;
    }
}

void QFitsSingleBuffer::setSpecPhysMaxX(double physMaxX) {
    if ((dpData->type == typeFits) &&
        (dpData->fvalue != NULL))
    {
        if ((getSpecPhysMinX() != 0) &&
            (getSpecPhysMinX() >= physMaxX))
        {
            return;
        }

//@        QReadLocker locker(&buffersLock);
        Fits *f = getCubeDisplay1D();
        if (f != NULL) {
            double  crval       = f->getCRVAL(QFV::Horizontal),
                    crpix       = f->getCRPIX(QFV::Horizontal),
                    cdelt       = f->getCDELT(QFV::Horizontal);
//            int     channelMax  = (int)(crpix + (physMaxX - crval) / cdelt + 0.5);
            int     channelMax  = wavepix(physMaxX, crpix, crval, cdelt, f->isAxisLog(QFV::Horizontal));

            specPhysMaxX    = physMaxX;
            specChannelMaxX = channelMax;
            if (isMultiBufferChild()) {
                myMultiBuffer->setSpecPhysMaxX(physMaxX);
                myMultiBuffer->setSpecChannelMaxX(channelMax);
            }
        }
    } else if (dpData->type == typeStrarr) {
        specPhysMaxX    = physMaxX;
        specChannelMaxX = (int)(physMaxX + .5);

        if (isMultiBufferChild()) {
            myMultiBuffer->setSpecPhysMaxX(physMaxX);
            myMultiBuffer->setSpecChannelMaxX((int)(physMaxX + .5));
        }
    }
}

double QFitsSingleBuffer::getSpecPhysMinY() {
    return specPhysMinY;
}

double QFitsSingleBuffer::getSpecPhysMaxY() {
    return specPhysMaxY;
}

void QFitsSingleBuffer::setSpecPhysRangeY(double physMinY, double physMaxY) {
    if (physMinY < physMaxY) {
        if (((dpData->type == typeFits) && (dpData->fvalue != NULL)) ||
            (dpData->type == typeStrarr))
        {
            specPhysMinY = physMinY;
            specPhysMaxY = physMaxY;
            if (isMultiBufferChild()) {
                myMultiBuffer->setSpecPhysRangeY(physMinY, physMaxY);
            }
        }
    }
}

double QFitsSingleBuffer::getImageMinValue() {
    return imageMinValue;
}

void QFitsSingleBuffer::setImageMinValue(double imageMin) {
    if ((dpData->type == typeFits) &&
        (dpData->fvalue != NULL))
    {
        if ((getImageMaxValue() != 0) &&
            (imageMin >= getImageMaxValue()))
        {
            return;
        }

        QReadLocker locker(&buffersLock);
        imageMinValue = imageMin;

        if (isMultiBufferChild()) {
            myMultiBuffer->setImageMinValue(imageMin);
        }
    }
}

double QFitsSingleBuffer::getImageMaxValue() {
    return imageMaxValue;
}

void QFitsSingleBuffer::setImageMaxValue(double imageMax) {
    if ((dpData->type == typeFits) &&
        (dpData->fvalue != NULL))
    {
        if ((getImageMinValue() != 0) &&
            (getImageMinValue() >= imageMax))
        {
            return;
        }

        imageMaxValue = imageMax;
        if (isMultiBufferChild()) {
            myMultiBuffer->setImageMaxValue(imageMax);
        }
    }
}

void QFitsSingleBuffer::setCubeSpecOrientation(QFV::Orientation o, bool allSB) {
    if ((o == Qt::Vertical) &&
        (dpData->fvalue != NULL) &&
        (dpData->fvalue->Naxis(0) == 1))
    {
        o = QFV::Horizontal;    // set here orientation to dim 1
    }

    cubeSpecOrientation = o;
    if (isMultiBufferChild() && myMultiBuffer->hasCubeSpecHomogenSB() && !isEmptyPrimary()) {
        myMultiBuffer->setCubeSpecOrientation(o, allSB);
    }
}

double QFitsSingleBuffer::getCubeCenter(QFV::Orientation o) {
    if (o == QFV::Default) {
        o = getCubeSpecOrientation();
    }
    if (isMultiBufferChild() && myMultiBuffer->hasCubeSpecHomogenSB() && !isEmptyPrimary()) {
//printf("getCubeCenter MB %g\n", myMultiBuffer->getCubeCenter(o));fflush(stdout);
        return myMultiBuffer->getCubeCenter(o);
    } else {
//printf("getCubeCenter SB %g\n", cubeCenter[o]);fflush(stdout);
        return cubeCenter[o];
    }
}

void QFitsSingleBuffer::setCubeCenter(double c, QFV::Orientation o, bool) {
    if (o == QFV::Default) {
        o = getCubeSpecOrientation();
    }
//printf("setCubeCenter SB %g\n", c);fflush(stdout);
    cubeCenter[o] = c;
    if (isMultiBufferChild() && myMultiBuffer->hasCubeSpecHomogenSB() && !isEmptyPrimary()) {
//printf("   setCubeCenter MB %g\n", c);fflush(stdout);
        myMultiBuffer->setCubeCenter(c, o);
    }
    QFitsWidget2D *widget2D = dynamic_cast<QFitsWidget2D*>(widgetState);
    QFitsWidgetTable *widgetTable = dynamic_cast<QFitsWidgetTable*>(widgetState);
    if ((widget2D != NULL) || (widgetTable != NULL)) {
        setDirty(true);
        updateScaling();
        fitsMainWindow->viewingtools->updateRegionInfo();
    }
}

double QFitsSingleBuffer::getLineWidth(QFV::Orientation o) {
    if (o == QFV::Default) {
        o = getCubeSpecOrientation();
    }
    if (isMultiBufferChild() && myMultiBuffer->hasCubeSpecHomogenSB() && !isEmptyPrimary()) {
        return myMultiBuffer->getLineWidth(o);
    } else {
        return lineWidth[o];
    }
}

void QFitsSingleBuffer::setLineWidth(double w, QFV::Orientation o, bool allSB) {
    if (o == QFV::Default) {
        o = getCubeSpecOrientation();
    }
    lineWidth[o] = w;
    if (isMultiBufferChild() && myMultiBuffer->hasCubeSpecHomogenSB() && !isEmptyPrimary()) {
        myMultiBuffer->setLineWidth(w, o, allSB);
    }
}

int QFitsSingleBuffer::getLineCont1Center() {
    if (isMultiBufferChild() && myMultiBuffer->hasCubeSpecHomogenSB() && !isEmptyPrimary()) {
        return myMultiBuffer->getLineCont1Center();
    } else {
        return linecont1center;
    }
}

void QFitsSingleBuffer::setLineCont1Center(int c) {
    linecont1center = c;
    if (isMultiBufferChild() && myMultiBuffer->hasCubeSpecHomogenSB() && !isEmptyPrimary()) {
        myMultiBuffer->setLineCont1Center(c);
    }
}

int QFitsSingleBuffer::getLineCont1Width() {
    if (isMultiBufferChild() && myMultiBuffer->hasCubeSpecHomogenSB() && !isEmptyPrimary()) {
        return myMultiBuffer->getLineCont1Width();
    } else {
        return linecont1width;
    }
}

void QFitsSingleBuffer::setLineCont1Width(int w) {
    linecont1width = w;
    if (isMultiBufferChild() && myMultiBuffer->hasCubeSpecHomogenSB() && !isEmptyPrimary()) {
        myMultiBuffer->setLineCont1Width(w);
    }
}

int QFitsSingleBuffer::getLineCont2Center() {
    if (isMultiBufferChild() && myMultiBuffer->hasCubeSpecHomogenSB() && !isEmptyPrimary()) {
        return myMultiBuffer->getLineCont2Center();
    } else {
        return linecont2center;
    }
}

void QFitsSingleBuffer::setLineCont2Center(int c) {
    linecont2center = c;
    if (isMultiBufferChild() && myMultiBuffer->hasCubeSpecHomogenSB() && !isEmptyPrimary()) {
        myMultiBuffer->setLineCont2Center(c);
    }
}

int QFitsSingleBuffer::getLineCont2Width() {
    if (isMultiBufferChild() && myMultiBuffer->hasCubeSpecHomogenSB() && !isEmptyPrimary()) {
        return myMultiBuffer->getLineCont2Width();
    } else {
        return linecont2width;
    }
}

void QFitsSingleBuffer::setLineCont2Width(int w) {
    linecont2width = w;
    if (isMultiBufferChild() && myMultiBuffer->hasCubeSpecHomogenSB() && !isEmptyPrimary()) {
        myMultiBuffer->setLineCont2Width(w);
    }
}

bool QFitsSingleBuffer::getLineCont1() {
    if (isMultiBufferChild() && myMultiBuffer->hasCubeSpecHomogenSB() && !isEmptyPrimary()) {
//printf("getLineCont1 MB %g\n", myMultiBuffer->getLineCont1());fflush(stdout);
        return myMultiBuffer->getLineCont1();
    } else {
//printf("getLineCont1 SB %g\n", linecont1);fflush(stdout);
        return linecont1;
    }
}

void QFitsSingleBuffer::setLineCont1(bool c) {
//printf("setLineCont1 SB %g\n", c);fflush(stdout);
    linecont1 = c;
    if (isMultiBufferChild() && myMultiBuffer->hasCubeSpecHomogenSB() && !isEmptyPrimary()) {
//printf("   setLineCont1 MB %g\n", c);fflush(stdout);
        myMultiBuffer->setLineCont1(c);
    }
}

bool QFitsSingleBuffer::getLineCont2() {
    if (isMultiBufferChild() && myMultiBuffer->hasCubeSpecHomogenSB() && !isEmptyPrimary()) {
//printf("getLineCont2 MB %g\n", myMultiBuffer->getLineCont2());fflush(stdout);
        return myMultiBuffer->getLineCont2();
    } else {
//printf("getLineCont2 SB %g\n", linecont2);fflush(stdout);
        return linecont2;
    }
}

void QFitsSingleBuffer::setLineCont2(bool c) {
//printf("setLineCont2 SB %g\n", c);fflush(stdout);
    linecont2 = c;
    if (isMultiBufferChild() && myMultiBuffer->hasCubeSpecHomogenSB() && !isEmptyPrimary()) {
//printf("   setLineCont2 MB %g\n", c);fflush(stdout);
        myMultiBuffer->setLineCont2(c);
    }
}

BufferAppearance QFitsSingleBuffer::getAppearance() {
    QFitsSingleBuffer *sb = fitsMainWindow->getActualSB();
    QFitsBaseBuffer   *bb = fitsMainWindow->getCurrentBuffer();
    QFitsMultiBuffer  *mb = dynamic_cast<QFitsMultiBuffer*>(bb);
    if ((sb == NULL) &&
        (mb != NULL) &&
        mb->hasCubeSpecHomogenSB())
    {
        return mb->getAppearance();
    }

    return bufAppearance;
}

void QFitsSingleBuffer::setMouseTrackingView(bool track, bool traceUp) {
    if (traceUp && isMultiBufferChild()) {
        myMultiBuffer->setMouseTrackingView(track, traceUp);
    } else {
        widgetState->setMouseTrackingView(track);
    }
}

void QFitsSingleBuffer::showCubePlot() {
    fitsMainWindow->mytoolbar->movieButton->setChecked(false);
    QReadLocker locker(&buffersLock);
    if ((dpData->type == typeFits) && (Naxis(3) >= 2)) {
        bufAppearance.hideSpectrum = false;
    }
}

void QFitsSingleBuffer::updateLinemapDialog() {
    if (dpData->type == typeFits) {
        if (!(buffersLock.tryLockForRead())) {
            return;
        }

        CubeDisplayDialog *dlg = fitsMainWindow->cubeDisplayDialog;

        dlg->setCubeDisplayOptions(dpData->fvalue->getCRPIX(3),
                                   dpData->fvalue->getCDELT(3),
                                   dpData->fvalue->getCRVAL(3),
                                   Naxis(3));
        if (Naxis(3) > 1) {
            dlg->setLinemapCenterMaxValue(Naxis(3));
            dlg->setLinemapCont1MaxValue(Naxis(3));
            dlg->setLinemapCont2MaxValue(Naxis(3));
            dlg->setLinemapSliderWavelength(Naxis(3));
            if (dlg->getLinemapCenterValue() > Naxis(3)) {
                dlg->setLinemapCenterValue(Naxis(3));
            }

            QString text1, text2, textInfo;
            dpString fstring;
            if (dpData->fvalue->getCDELT(3) != 0.0) {
                double start_wavelength = dpData->fvalue->getCRVAL(3) - dpData->fvalue->getCDELT(3) * (dpData->fvalue->getCRPIX(3) - 1.0);
                double end_wavelength = start_wavelength + dpData->fvalue->getCDELT(3) * Naxis(3);
                double width_wavelength = dpData->fvalue->getCDELT(3) * dlg->getLinemapWidthValue();

                fstring.sprintf("%8.5f", start_wavelength);
                text1 = fstring.c_str();
                fstring.sprintf("%8.5f", end_wavelength);
                text2 = fstring.c_str();
                char value[256];
                dpData->fvalue->GetStringKey("CUNIT3", value);
                fstring.sprintf("Center at %8.5f +- %8.5f %s", start_wavelength,width_wavelength, value);
                textInfo = fstring.c_str();
                dlg->setLinemapWavelengthSliderInformation(&text1, &textInfo, &text2);
            } else {
                text1 = "1";
                textInfo = "No wavelength information available";
                fstring.sprintf("%i", Naxis(3));
                text2 = fstring.c_str();
                dlg->setLinemapWavelengthSliderInformation(&text1, &textInfo, &text2);
            }
        }
        buffersLock.unlock();
    }
}

void QFitsSingleBuffer::saveImage() {
    // only for 2D FITS images for the moment
    QFitsWidget2D *widget = dynamic_cast<QFitsWidget2D*>(widgetState);
    if (widget != NULL) {
        widget->saveImage();
    }
}

void QFitsSingleBuffer::saveFits() {
    QString selectedFilter;
    QString filename = QFileDialog::getSaveFileName(this,
                                                    "QFitsView - Save as FITS",
                                                    settings.lastSavePath,
                                                    "*.fits;;*",
                                                    &selectedFilter);
    if (!filename.isNull() &&
        (dpData->type == typeFits) &&
        buffersLock.tryLockForRead())
    {
        selectedFilter.remove(0,1);
        if (!filename.endsWith(selectedFilter)) {
            filename += selectedFilter;
        }

        dpData->fvalue->WriteFITS(filename.toStdString().c_str());
        buffersLock.unlock();
        QFileInfo finfo(filename);
        settings.lastSavePath = finfo.absoluteDir().path();
        settings.lastSavePath.replace("\\", "/");
    }
}

void QFitsSingleBuffer::saveFitsImage() {
    QString selectedFilter;
    QString filename = QFileDialog::getSaveFileName(this,
                                                    "QFitsView - Save Image as FITS",
                                                    settings.lastSavePath,
                                                    "*.fits;;*",
                                                    &selectedFilter);
    if (!filename.isNull() &&
        (dpData->type == typeFits) &&
        buffersLock.tryLockForRead())
    {
        selectedFilter.remove(0,1);
        if (!filename.endsWith(selectedFilter)) {
            filename += selectedFilter;
        }

        if (Naxis(3) > 1) {
            cubeDisplay2D->WriteFITS(filename.toStdString().c_str());
        } else {
            dpData->fvalue->WriteFITS(filename.toStdString().c_str());
        }
        buffersLock.unlock();
        QFileInfo finfo(filename);
        settings.lastSavePath = finfo.absoluteDir().path();
        settings.lastSavePath.replace("\\", "/");
    }
}

void QFitsSingleBuffer::printImage() {
    // only for 2D FITS images for the moment
    QFitsWidget2D *widget = dynamic_cast<QFitsWidget2D*>(widgetState);
    if (widget != NULL) {
        widget->printImage();
    }
}

void QFitsSingleBuffer::setZoomWidget(double zoomValue) {
    if (widgetState != NULL) {
        widgetState->setZoom(zoomValue);
    }
}

void QFitsSingleBuffer::updateScaling() {
    ScaleImage();
    if ((tableViewDirty == true) && (dynamic_cast<QFitsWidgetTable*>(widgetState) == NULL)) {
        // if an update is requested and TableView is NOT active, signal it anyway that there
        // an update has to be done
        // Only ViewTable needs this, since all other Widgets work on image or cubedisplay and
        // not on fitsData directly.
        for (int i = 0; i < widgetList.size(); i++) {
            if (dynamic_cast<QFitsWidgetTable*>(widgetList.at(i)) != NULL) {
                widgetList.at(i)->updateScaling();
                break;
            }
        }
        tableViewDirty == false;
    }
    if (widgetState != NULL) {
        widgetState->updateScaling();
    }

    update();
}

void QFitsSingleBuffer::enableMovie(bool b) {
    if (Naxis(0) > 2) {
        QFitsWidget2D *widget2D = dynamic_cast<QFitsWidget2D*>(widgetState);
        QFitsWidgetTable *widgetTable = dynamic_cast<QFitsWidgetTable*>(widgetState);
        if ((widget2D != NULL) || (widgetTable != NULL)) {
            widgetState->enableMovie(b);
        }
    }
}

void QFitsSingleBuffer::setXRange() {
    if (widgetState != NULL) {
        widgetState->setXRange(getSpecPhysMinX(), getSpecPhysMaxX());
    }
}

void QFitsSingleBuffer::setYRange() {
    if (widgetState != NULL) {
        widgetState->setYRange(specPhysMinY, specPhysMaxY);
    }
}

void QFitsSingleBuffer::setYRange(const double &minval, const double &maxval) {
    if (widgetState != NULL) {
        widgetState->setYRange(minval, maxval);
    }
}

void QFitsSingleBuffer::setData() {
    if ((dpData->type == typeFits) &&
        (dpData->fvalue != NULL) &&
        (Naxis(0) == 1))
    {
        for (int i = 0; i < widgetList.size(); i++) {
            QFitsWidget1D *widget1D = dynamic_cast<QFitsWidget1D *>(widgetList.at(i));
            if (widget1D != NULL) {
                QReadLocker locker(&buffersLock);
                Fits *copy = new Fits();
                copy->copy(*(dpData->fvalue));
                setCubeDisplay1D(copy);
                widget1D->setData();

                break;
            }
        }
    }
}

void QFitsSingleBuffer::newData3D() {
    if (widgetState != NULL) {
        widgetState->newData3D();
    }
}

void QFitsSingleBuffer::setMovieSpeed(int i) {
    if (widgetState != NULL) {
        widgetState->setMovieSpeed(i);
    }
}

void QFitsSingleBuffer::setImageCenter(double x, double y) {
    if (widgetState != NULL) {
        widgetState->setImageCenter(x, y);
    }
}

void QFitsSingleBuffer::setIgnore(bool isChecked, double val) {
    ignoreInCube = isChecked;
    ignoreValue = val;
}

void QFitsSingleBuffer::copyImage(int copymode) {
    // overloaded (QBaseWidget and QFitsWidget2d)
    if (widgetState != NULL) {
        widgetState->copyImage(copymode);
    }
}

void QFitsSingleBuffer::setFocus() {
    if (!dpuser_widget->input->hasFocus()) {
        QFitsWidget1D *widget1D = dynamic_cast<QFitsWidget1D*>(widgetState);
        QFitsWidgetTable *wt = dynamic_cast<QFitsWidgetTable*>(widgetState);
        if (widget1D != NULL) {
            widget1D->plotter->setFocus();
        } else if (wt != NULL) {
            wt->getTableView()->setFocus();
        } else {
            if (widgetState != NULL) {
                widgetState->setFocus();
            }
        }
    }
}

void QFitsSingleBuffer::setLinemapOptions(bool c1, bool c2, int cen1, int wid1,
                                          int cen2, int wid2, int cen3, int wid3)
{
    QFitsWidget2D *widget = dynamic_cast<QFitsWidget2D*>(widgetState);
    if (widget != NULL) {
        setLineCont1(c1);
        setLineCont2(c2);
        setLineWidth(wid1, QFV::Wavelength);
        setLineCont1Center(cen2);
        setLineCont1Width(wid2);
        setLineCont2Center(cen3);
        setLineCont2Width(wid3);
        setCubeCenter(cen1, QFV::Wavelength);
    }
}

QFitsBaseWidget* QFitsSingleBuffer::getState() {
    if (isMultiBufferChild() &&
        (dynamic_cast<QFitsWidgetTable*>(myMultiBuffer->getState()) != NULL))
    {
        return myMultiBuffer->getTableWidget();
    }
    return widgetState;
}

void QFitsSingleBuffer::createManualSpectrum() {
    QFitsMarkers *markers = getSourceMarkers();

    QReadLocker locker(&buffersLock);
    if ((dpData->type == typeFits) &&
        (dpData->fvalue != NULL) &&
        (markers != NULL) &&
        ((markers->getSizeSource() > 0) ||
         (markers->getSizeContinuum() > 0)))
    {
        QApplication::setOverrideCursor(Qt::WaitCursor);
        manualSpectrum->spec3d(*(dpData->fvalue), 1, 1, 0, 0);
        manualSpectrum->setType(R8);
//        manualSpectrum->create(Naxis(3), 1, R8);

        int    method   = fitsMainWindow->spectrum->getStatistics();
        double *medmem  = (double*)malloc(markers->getSizeSource() * sizeof(double)),
               *medmem2 = (double*)malloc(markers->getSizeContinuum() * sizeof(double));
        for (int z = 1; z <= Naxis(3); z++) {
            long counter  = 0,
                 counter2 = 0,
                 x        = 0,
                 y        = 0;
            for (int i = 0; i < markers->getSizeSource(); i++) {
                markers->getMarkerSource(i, &x, &y);
                if ((x >= 1) && (x <= Naxis(1)) && (y >= 1) && (y <= Naxis(2))) {
                    double value = dpData->fvalue->ValueAt(dpData->fvalue->F_I(x, y, z));
                    if (gsl_finite(value)) {
                        medmem[counter] = value;
                        counter++;
                    }
                }
            }
            for (int i = 0; i < markers->getSizeContinuum(); i++) {
                markers->getMarkerContinuum(i, &x, &y);
                if ((x >= 1) && (x <= Naxis(1)) && (y >= 1) && (y <= Naxis(2))) {
                    double value = dpData->fvalue->ValueAt(dpData->fvalue->F_I(x, y, z));
                    if (gsl_finite(value)) {
                        medmem2[counter2] = value;
                        counter2++;
                    }
                }
            }
            manualSpectrum->r8data[z-1] = 0.;
            if (counter > 0) {
                if (method == 0 || method == 1) { // sum, average
                    manualSpectrum->r8data[z-1] = gsl_stats_mean(medmem, 1, counter);
                } else { // median
                    gsl_sort(medmem, 1, counter);
                    manualSpectrum->r8data[z-1] = gsl_stats_median_from_sorted_data(medmem, 1, counter);
                }
            }
            if (counter2 > 0) {
                if (method == 0 || method == 1) { // sum, average
                    manualSpectrum->r8data[z-1] -= gsl_stats_mean(medmem2, 1, counter2);
                } else { // median
                    gsl_sort(medmem2, 1, counter2);
                    manualSpectrum->r8data[z-1] -= gsl_stats_median_from_sorted_data(medmem2, 1, counter2);
                }
            }
            if (method == 0) { // sum
                manualSpectrum->r8data[z-1] *= counter;
            }
        }
        if (medmem) {
            free(medmem);
        }
        if (medmem2) {
            free(medmem2);
        }
/*
        char key[255];
        manualSpectrum->CopyHeader(*(dpData->fvalue));
        manualSpectrum->SetFloatKey("CRVAL1", dpData->fvalue->getCRVAL(3));
        manualSpectrum->SetFloatKey("CRPIX1", dpData->fvalue->getCRPIX(3));
        manualSpectrum->SetFloatKey("CDELT1", dpData->fvalue->getCDELT(3));
        if (dpData->fvalue->GetStringKey("CTYPE3", key)) {
            manualSpectrum->SetStringKey("CTYPE1", key);
        } else {
            manualSpectrum->DeleteKey("CTYPE1");
        }
        if (dpData->fvalue->GetStringKey("CUNIT3", key)) {
            manualSpectrum->SetStringKey("CUNIT1", key);
        } else {
            manualSpectrum->DeleteKey("CUNIT1");
        }

        for (int i = 1; i <= MAXNAXIS; i++) {
            for (int j = 1; j <= MAXNAXIS; j++) {
                sprintf(key, "CD%i_%i", i, j);
                manualSpectrum->DeleteKey(key);
            }
        }

        for (int i = 2; i <= MAXNAXIS; i++) {
            sprintf(key, "CUNIT%i", i);
            manualSpectrum->DeleteKey(key);
            sprintf(key, "CTYPE%i", i);
            manualSpectrum->DeleteKey(key);
            sprintf(key, "CRPIX%i", i);
            manualSpectrum->DeleteKey(key);
            sprintf(key, "CRVAL%i", i);
            manualSpectrum->DeleteKey(key);
            sprintf(key, "CDELT%i", i);
            manualSpectrum->DeleteKey(key);
        }

        manualSpectrum->DeleteKey("CROTA1");
        manualSpectrum->DeleteKey("CROTA2");
*/
        sprintf(manualSpectrum->crtype, "");
        QApplication::restoreOverrideCursor();
        hasManualSpectrum = true;
    } else {
        hasManualSpectrum = false;
    }

    fitsMainWindow->spectrum->update();
}

void QFitsSingleBuffer::setupColours() {
    QVector<QRgb> *colorVec = setActualColortable();

    if (colorVec != NULL) {
        image->setColorTable(*colorVec);
        delete colorVec;
    }

    if (widgetState != NULL) {
        widgetState->setupColours();
    }

    if (myMultiBuffer == NULL) {
        fitsMainWindow->updateViewingtools();
        fitsMainWindow->updateWedge();
    }
}

void QFitsSingleBuffer::ResetSpectrum() {
    if ((dpData->type == typeFits) &&
        (dpData->fvalue != NULL))
    {
        int dim = getCubeSpecOrientation();
//        int dim  = dpData->fvalue->Naxis(0);
//        if (dim == 2) {
//            if (getOrientation2D() == Qt::Horizontal) {
//                dim = 1;
//            } else {
//                dim = 2;
//            }
//        }

        int size = dpData->fvalue->Naxis(dim);
        if (dpData->fvalue->getCDELT(dim) >= 0.) {
            setSpecChannelMinX(1);
            setSpecChannelMaxX(size);
        } else {
            setSpecChannelMinX(size);
            setSpecChannelMaxX(1);
        }
    }
}

bool QFitsSingleBuffer::isEmptyPrimary() {
    if ((dpData->fvalue != NULL) &&
        (dpData->fvalue->extensionType == EMPTY))
    {
        return true;
    }
    return false;
}

void QFitsSingleBuffer::rotImage(bool cw) {
    QTransform trans;
    if (cw) {
        trans.rotate(-90);
    } else {
        trans.rotate(90);
    }
    QImage img = image->transformed(trans);
    delete image;
    image = new QImage(img);
}

void QFitsSingleBuffer::lockSB(bool lock, int x, int y) {
    if (viewMode != ViewTable) {
        setMouseTrackingView(!lock, true);

        if (lock) {
            // not locked --> lock please
            if (fitsMainWindow->spectrum->getSpectrumMethod() == SinglePixel) {
                getCursorMarkers()->addMarkerLock(x , y);
            }
            setLockedSingleBuffer(this);
            setMarkedSingleBuffer(this, true);
        } else {
            // locked --> unlock please
            getCursorMarkers()->deleteMarkerLock();
            setLockedSingleBuffer(NULL);
            setMarkedSingleBuffer(NULL, true);
        }
    }
}

void QFitsSingleBuffer::transCoordImage2Widget(double *x, double *y, double zoomFactor/*, bool doRotFlip*/) {
// ex QFitsSingleBuffer::imageToWidget()
    if ((dpData->type != typeFits) ||
        (dpData->fvalue == NULL))
    {
        *x = -1.;
        *y = -1.;
        return;
    }

    // dpuser starts counting at 1, widget at zero!
    // y is compensated when flipping vertically (code below)
    (*x)--;

    double  imageWidth      = dpData->fvalue->Naxis(1),
            imageHeight     = dpData->fvalue->Naxis(2),
            imageWidthRot   = imageWidth,
            imageHeightRot  = imageHeight;

    // switch width and height if rotated 90° or 270°
    if ((rotation == 90) || (rotation == 270)) {
        imageWidthRot  = imageHeight;
        imageHeightRot = imageWidth;
    }

    // flip in y (widget origin: upper left, image origin: bottom left)
    *y = imageHeight - *y;

//    if (doRotFlip) {
        int     corr            = 1;

        // apply rotation
        switch(rotation) {
            case 90: {
                double tmp = *x;
                *x = *y;
                *y = imageWidth - tmp - corr; }
                break;
            case 180:
                *x = imageWidth - *x - corr;
                *y = imageHeight - *y - corr;
                break;
            case 270: {
                double tmp = *x;
                *x = imageHeight - *y - corr;
                *y = tmp; }
                break;
            default:
                break;
        }

        // apply flip
        if (flipX) {
            *x = imageWidthRot - *x - corr;
        }
        if (flipY) {
            *y = imageHeightRot - *y - corr;
        }
//    }

    // apply zoom, now we are in widget-coordinates
    *x *= zoomFactor;
    *y *= zoomFactor;
}

void QFitsSingleBuffer::transCoordWidget2Image(double *x, double*y, double zoomFactor, int widgetHeight, bool doRotFlip) {
// used in QFitsView2D::widgetToImageNoSignal()
    if ((dpData->type != typeFits) ||
        (dpData->fvalue == NULL))
    {
        *x = -1.;
        *y = -1.;
        return;
    }

    // flip in y (widget origin: upper left, image origin: bottom left)
    *y = widgetHeight - *y;

    // apply zoom, from now on calculate in image-coordinates
    *x /= zoomFactor;
    *y /= zoomFactor;

    if (doRotFlip) {
//        int     corr            = 0; //1;
        double  imageWidth      = dpData->fvalue->Naxis(1),
                imageHeight     = dpData->fvalue->Naxis(2),
                imageWidthRot   = imageWidth,
                imageHeightRot  = imageHeight;

        // switch width and height if rotated 90° or 270°
        if ((rotation == 90) || (rotation == 270)) {
            imageWidthRot  = imageHeight;
            imageHeightRot = imageWidth;
        }

        // apply flip
        if (flipX) {
            *x = imageWidthRot - *x/* + corr*/;
        }
        if (flipY) {
            *y = imageHeightRot - *y/* + corr*/;
        }

        // apply rotation
        switch(rotation) {
            case 90: {
                double tmp = *x;
                *x = *y;
                *y = imageWidthRot - tmp/* + corr*/; }
                break;
            case 180:
                *x = imageWidthRot - *x/* + corr*/;
                *y = imageHeightRot - *y/* + corr*/;
                break;
            case 270: {
                double tmp = *x;
                *x = imageHeightRot - *y/* + corr*/;
                *y = tmp; }
                break;
            default:
                break;
        }
    }

    // dpuser starts counting at 1!
    (*x)++;
    (*y)++;
}

void QFitsSingleBuffer::setImageScaleRange(dpScaleMode mode) {
    imageScaleRange = mode;
    QFitsWidget1D *widget1D = dynamic_cast<QFitsWidget1D*>(widgetState);
    if (widget1D != NULL) {
        if (mode == ScaleManual) {
            widget1D->plotter->setAutoScale(false);
        } else {
            widget1D->plotter->setAutoScale(true);
        }
    }
}

void QFitsSingleBuffer::updateCubeMode() {
    QFitsToolBar *tb = fitsMainWindow->mytoolbar;
//    CubeDisplayDialog *dlg = fitsMainWindow->cubeDisplayDialog;

    switch(cubeMode) {
    case DisplayCubeSingle:
        break;
    case DisplayCubeAverage:
    case DisplayCubeMedian:
        tb->movieButton->setChecked(false);
        enableMovie(false);
        break;
    case DisplayCubeLinemap:
        tb->movieButton->setChecked(false);
        enableMovie(false);
//        setLinemapOptions(dlg->getCheckLinemapDoCont1Checked(),
//                          dlg->getCheckLinemapDoCont2Checked(),
//                          dlg->getLinemapCenterValue(),
//                          dlg->getLinemapWidthValue(),
//                          dlg->getLinemapCont1Value(),
//                          dlg->getLinemapWidth1Value(),
//                          dlg->getLinemapCont2Value(),
//                          dlg->getLinemapWidth2Value());
//        tb->cubeImageSlice->setValue(dlg->getLinemapCenterValue());
        break;
    default:
        dp_debug("SOMETHING IS VERY WRONG HERE 2!");
        break;
    }
    setCubeCenter(tb->cubeImageSlice->value(), QFV::Wavelength);
    fitsMainWindow->updateToolbar();
    fitsMainWindow->spectrum->update();
}

QFitsSingleBuffer* QFitsSingleBuffer::getFirstImageBuffer() {
    QFitsSingleBuffer *sb = NULL;
    if ((dpData->type == typeFits) &&
        (!isMultiBufferChild() || (dpData->fvalue->extensionType != EMPTY)))
    {
        sb = this;
    }
    return sb;
}

bool QFitsSingleBuffer::showSpectrum() {
    bool ret = false;
    if (isMultiBufferChild()) {
        ret = myMultiBuffer->showSpectrum();
    }

    if (!ret) {
        QFitsWidgetTable *wt = dynamic_cast<QFitsWidgetTable*>(widgetState);
        if (hasCube ||
            ((wt != NULL) && (wt->getTableView()->doPlotData())))
        {
            ret = true;
        } else {
            ret = false;
        }
    }
    return ret;
}

void QFitsSingleBuffer::setColormap(QAction *action) {
    colormap = action;
    setupColours();
}

void QFitsSingleBuffer::setInvertColormap(bool b) {
    invertColormap = b;
    setupColours();
}

void QFitsSingleBuffer::orientationChanged() {
    ScaleImage();
    if (widgetState != NULL) {
        widgetState->orientationChanged();
    }
}

void QFitsSingleBuffer::setFlipX(bool b) {
    flipX = b;
    if (widgetState != NULL) {
        widgetState->setFlipX(b);
    }
}

void QFitsSingleBuffer::setFlipY(bool b) {
    flipY = b;
    if (widgetState != NULL) {
        widgetState->setFlipY(b);
    }
}

void QFitsSingleBuffer::setBrightness(int b, bool sbInitialized) {
    if (b != brightness) {
        brightness = b;
        if (myMultiBuffer == NULL) {
            emit(newBrightness(b));
            setupColours();
        } else {
            if (sbInitialized) {
                // This SingleBuffer has been activated directly,
                // promote to MultiBuffer
                myMultiBuffer->setBrightness(b);
            }
        }
    }
}

void QFitsSingleBuffer::setContrast(int c, bool sbInitialized) {
    if (c != contrast) {
        contrast = c;
        if (myMultiBuffer == NULL) {
            emit(newContrast(c));
            setupColours();
        } else {
            if (sbInitialized) {
                // This SingleBuffer has been activated directly,
                // promote to MultiBuffer
                myMultiBuffer->setContrast(c);
            }
        }
    }
}

void QFitsSingleBuffer::setBrightnessContrast(int b, int c,
                                              bool sbInitialized)
{
    if ((b != brightness) || (c != contrast)) {
        brightness = b;
        contrast = c;

        if (myMultiBuffer == NULL) {
            emit(newBrightness(b));
            emit(newContrast(c));
            setupColours();
        } else {
            if (sbInitialized) {
                // This SingleBuffer has been activated directly,
                // promote to MultiBuffer
                myMultiBuffer->setBrightnessContrast(b, c);
            }
        }
    }
}

void QFitsSingleBuffer::setImageScalingMethod(int scale, bool sbInitialized) {
    if (scale != imageScaleMethod) {
        imageScaleMethod = scale;
        if (myMultiBuffer == NULL) {
            setDirty(true);
            updateScaling();
        } else {
            if (sbInitialized) {
                // This SingleBuffer has been activated directly,
                // promote to MultiBuffer
                myMultiBuffer->setImageScalingMethod(scale);
            } else {
                // This SingleBuffer has been activated through MultiBuffer,
                // do this for every SingleBuffer
                setDirty(true);
                updateScaling();
            }
        }
    }
}

void QFitsSingleBuffer::setRotation(int r) {
//    rotation = r;     // this is done below!
    if (widgetState != NULL) {
        widgetState->setRotation(r);
    }
}

void QFitsSingleBuffer::imageToWidget(int *x, int *y) {
    double  xDbl = *x,
            yDbl = *y;

    transCoordImage2Widget(&xDbl, &yDbl, getZoomFactor());

    *x = (int)xDbl;
    *y = (int)yDbl;
}

#ifdef HAS_VTK
    void QFitsSingleBuffer::new3Ddata() {
        if (getQFitsWidget3D() != NULL) {
            getQFitsWidget3D()->cubeViewer->newData();
        }
    }

    void QFitsSingleBuffer::new3Ddata2() {
        if (getQFitsWidget3D() != NULL) {
            getQFitsWidget3D()->cubeViewer->newData2();
        }
    }
#endif
