#include <QPainter>
#include <QMouseEvent>
#include <QMessageBox>

#include <gsl/gsl_math.h>

#include "QFitsMainWindow.h"
#include "QFitsMainView.h"
#include "QFitsBaseBuffer.h"
#include "QFitsSingleBuffer.h"
#include "QFitsMultiBuffer.h"
#include "QFitsWidget1D.h"
#include "QFitsView1D.h"
#include "QFitsWidgetTable.h"
#include "QFitsCubeSpectrum.h"
#include "qtdpuser.h"
#include "fits.h"

//------------------------------------------------------------------------------
//         QFitsView1D
//------------------------------------------------------------------------------
QFitsView1D::QFitsView1D(QWidget *parent) : QFitsBaseView(parent) {
//    m.setMatrix(1, 0, 0, 1, 0, 0);
    m.reset();
    plotwidth = width();
    plotheight = height();
    margin = 10;
    xdata = NULL;
    ydata = NULL;
    x2data = NULL;
    y2data = NULL;
    ndata = 0;
    n2data = 0;
    logwave = false;
    autoScaleSpectrum = true;
    selectstart = QPoint(-1, -1);
    selectend = QPoint(-1, -1);

    rightPopupMenu = new QMenu(this);
    createPopupMenu();

    setMouseTracking(true);
    setCursor(Qt::CrossCursor);
    setFocusPolicy(Qt::StrongFocus);
//    setAttribute(Qt::WA_OpaquePaintEvent);
//    setAutoFillBackground(true);
}

QFitsView1D::~QFitsView1D() {
    if (xdata) {
        free(xdata);
        xdata = NULL;
    }
    if (ydata) {
        free(ydata);
        ydata = NULL;
    }
    if (x2data) {
        free(x2data);
        x2data = NULL;
    }
    if (y2data) {
        free(y2data);
        y2data = NULL;
    }
    if (rightPopupMenu) {
        delete rightPopupMenu;
        rightPopupMenu = NULL;
    }
}

void QFitsView1D::createPopupMenu() {
    // create actions
    zoomOutAct = new QAction("Zoom out", this);
    connect(zoomOutAct, SIGNAL(triggered()), this, SLOT(zoomOut()));

    plotStyleLinesAct = new QAction("Plot Style: Lines", this);
    connect(plotStyleLinesAct, SIGNAL(triggered()), this, SLOT(setPlotStyleLines()));

    plotStyleHistogramAct = new QAction("Plot Style: Histogram", this);
    connect(plotStyleHistogramAct, SIGNAL(triggered()), this, SLOT(setPlotStyleHistogram()));

    exportSpecAsciiAct = new QAction("Save spectrum as ASCII...", this);
    connect(exportSpecAsciiAct, SIGNAL(triggered()), this, SLOT(exportSpecAsciiSlot()));

    exportSpecFitsAct = new QAction("Save spectrum as FITS...", this);
    connect(exportSpecFitsAct, SIGNAL(triggered()), this, SLOT(exportSpecFitsSlot()));

    saveMBbufferAct = new QAction("Save extension as FITS...", this);
    saveMBbufferAct->setStatusTip(tr("Saves this extension without any modifications"));
    connect(saveMBbufferAct, SIGNAL(triggered()), this, SLOT(saveMBbufferSlot()));

    copyMBbufferAct = new QAction("Copy extension to new buffer", this);
    copyMBbufferAct->setStatusTip(tr("Copies this extension to a new buffer"));
    connect(copyMBbufferAct, SIGNAL(triggered()), this, SLOT(copyMBbufferSlot()));

#ifdef HAS_PLOTTING
    plotSpecAct = new QAction("Send spectrum to plotting tool", this);
    connect(plotSpecAct, SIGNAL(triggered()), this, SLOT(sendToPlotterSlot()));
#endif /* HAS_PLOTTING */

    // fill popup menu
    rightPopupMenu->addAction(zoomOutAct);
    rightPopupMenu->addAction(plotStyleLinesAct);
    rightPopupMenu->addAction(plotStyleHistogramAct);
    rightPopupMenu->addSeparator();
    rightPopupMenu->addAction(exportSpecAsciiAct);
    rightPopupMenu->addAction(exportSpecFitsAct);
    rightPopupMenu->addSeparator();
    rightPopupMenu->addAction(saveMBbufferAct);
    rightPopupMenu->addAction(copyMBbufferAct);
#ifdef HAS_PLOTTING
    rightPopupMenu->addSeparator();
    rightPopupMenu->addAction(plotSpecAct);
#endif /* HAS_PLOTTING */
}

int QFitsView1D::calcPos(QPoint pos) {
    int posInt = 0;

    if (selectstart != QPoint(-1, -1)) {
        selectend = pos;
    }

    QFitsSingleBuffer *sb = getMyBuffer();
    if (sb != NULL) {
        dpString fstring;
        double  value   = 0.0,
                xmin    = sb->getSpecPhysMinX(),
                xmax    = sb->getSpecPhysMaxX(),
                xscale  = (double)(plotwidth) / (xmax - xmin),
                xpos    = (pos.x() - fw - margin) / xscale + xmin;

        posInt = wavepix(xpos, crpix, crval, cdelt, logwave); // (int)(crpix + (xpos - crval) / cdelt + 0.5);

        if ((posInt > 0) && (posInt <= ndata)) {
            if (cdelt >= 0) {
                value = ydata[posInt-1];
            } else {
                value = ydata[ndata - posInt];
            }
        }

        if ((posInt > 0) && (posInt <= ndata)) {
            fstring.sprintf("#%i (%g, %g)", posInt, xpos, value);
        } else {
            fstring.sprintf("    (%g, -)", xpos);
        }
        posinfo = fstring.c_str();
    }

    return posInt;
}

void QFitsView1D::setData() {
    Fits *spectrum = NULL;
    QFitsSingleBuffer *sb = getMyBuffer();

    if (sb != NULL) {
        spectrum = sb->getCubeDisplay1D();
    } else {
        // invalidate CubeSpectrum
        ndata = 0;
        return;
    }

    int axis;
    unsigned long i, j;

    if (spectrum != NULL) {
        if (spectrum->Naxis(1) > 1) {
            axis = 1;
        } else if (spectrum->Naxis(2) > 1) {
            axis = 2;
        } else if (spectrum->Naxis(3) > 1) {
            axis = 3;
        } else {
            axis = 1;
        }

        ndata = spectrum->Nelements();
        xdata = (double *)realloc(xdata, ndata * sizeof(double));
        ydata = (double *)realloc(ydata, ndata * sizeof(double));
        crval = spectrum->getCRVAL(axis);
        crpix = spectrum->getCRPIX(axis);
        cdelt = spectrum->getCDELT(axis);
        logwave = spectrum->isAxisLog(axis);

        if (cdelt > 0.0) {
            for (i = 0; i < ndata; i++) {
                xdata[i] = pixwave(i+1, crpix, crval, cdelt, logwave);
/*
                if (logwave) {
                    xdata[i] = crval * exp(((double)(i+1) - crpix) * cdelt / crval); // following Greisen et al, 2006, EQ 4 & 5
                } else {
                    xdata[i] = crval + ((double)(i+1) - crpix) * cdelt;
                }
*/
                ydata[i] = spectrum->ValueAt(i);
            }
        } else {
            for (i = 0, j = ndata-1; i < ndata; i++, j--) {
                xdata[j] = pixwave(i+1, crpix, crval, cdelt, logwave);
/*
                if (logwave) {
                    xdata[i] = crval * exp(((double)(i+1) - crpix) * cdelt / crval); // following Greisen et al, 2006, EQ 4 & 5
                } else {
                    xdata[i] = crval + ((double)(i+1) - crpix) * cdelt;
                }
*/
                ydata[j] = spectrum->ValueAt(i);
            }
        }
    } else {
        // invalidate CubeSpectrum
        ndata = 0;
    }
}

void QFitsView1D::setData2(const double *nx2data,
                           const double *ny2data,
                           const unsigned long n)
{
    x2data = (double *)malloc(n * sizeof(double));
    y2data = (double *)malloc(n * sizeof(double));
    memcpy(x2data, nx2data, n * sizeof(double));
    memcpy(y2data, ny2data, n * sizeof(double));
    n2data = n;
}

void QFitsView1D::removeData2(void) {
    if (x2data) free(x2data);
    if (y2data) free(y2data);
    x2data = NULL;
    y2data = NULL;
    n2data = 0;
    update();
}

void QFitsView1D::setYRange(const double &yMin, const double &yMax) {
    double  tmp_ymin = yMin,
            tmp_ymax = yMax;

    if (yMin > yMax) {
        tmp_ymin = yMax;
        tmp_ymax = yMin;
    }

    QFitsSingleBuffer *sb = getMyBuffer();
    if (sb != NULL) {
        sb->setSpecPhysRangeY(tmp_ymin, tmp_ymax);
        QFitsWidget1D *widget1D = dynamic_cast<QFitsWidget1D *>(parentWidget());
        if (widget1D != NULL) {
            fitsMainWindow->mytoolbar->updateSpecMinMaxY(sb);
        }
    }

    update();
}

void QFitsView1D::setXRange(const double &xMin, const double &xMax) {
    double  tmp_xmin = xMin,
            tmp_xmax = xMax,
            tmp_ymin = -1.,
            tmp_ymax = 1.;

    if (xMin > xMax) {
        tmp_xmin = xMax;
        tmp_xmax = xMin;
    }
    if (tmp_xmax - tmp_xmin < cdelt)
        tmp_xmax = tmp_xmin + cdelt * 1.1;

    if ((ndata != 0) && autoScaleSpectrum) {
        unsigned long   i        = 0;
        while (xdata[i] < tmp_xmin && i < ndata - 1) {
            i++;
        }
        while ((!gsl_finite(ydata[i])) && (i < ndata - 1)) {
            i++;
        }
        if (gsl_finite(ydata[i])) {
            tmp_ymin = tmp_ymax = ydata[i];
        }
        while ((i < ndata) && (xdata[i] <= tmp_xmax)) {
            if ((ydata[i] > tmp_ymax) && gsl_finite(ydata[i])) {
                tmp_ymax = ydata[i];
            }
            if ((ydata[i] < tmp_ymin) && gsl_finite(ydata[i])) {
                tmp_ymin = ydata[i];
            }
            i++;
        }
        setYRange(tmp_ymin, tmp_ymax);
    }

    QFitsSingleBuffer *sb = getMyBuffer();
    if (sb == NULL) {
        QFitsMultiBuffer *mb = dynamic_cast<QFitsMultiBuffer*>(fitsMainWindow->getCurrentBuffer());
        if (mb != NULL) {
            sb = mb->getFirstImageBuffer();
        }
    }

    if (sb != NULL) {
        sb->setSpecPhysMinX(tmp_xmin);
        sb->setSpecPhysMaxX(tmp_xmax);
//@        fitsMainWindow->updateToolbar();
        if (sb->isMultiBufferChild() && sb->getMyMultiBuffer()->hasCubeSpecHomogenSB()) {
            sb->getMyMultiBuffer()->update();
        } else {
            update();
        }
    } else {
        update();
    }

}

void QFitsView1D::setAutoScale(bool scale) {
    autoScaleSpectrum = scale;
    QFitsSingleBuffer *sb = getMyBuffer();
    if (sb != NULL) {
        setXRange(sb->getSpecPhysMinX(), sb->getSpecPhysMaxX());
    }
}

void QFitsView1D::updateScaling() {
    QFitsSingleBuffer *sb = getMyBuffer();
    if (sb != NULL) {
        setXRange(sb->getSpecPhysMinX(), sb->getSpecPhysMaxX());
    }
}

// used in paintEvent
int QFitsView1D::calcPolyline(int n, double *x, double *y, QPolygon &points,
                              double ymin, double ymax, bool close)
{
    int j = 0;

    QFitsSingleBuffer *sb = getMyBuffer();
    if (sb != NULL) {
        double  xmin    = sb->getSpecPhysMinX(),
                xmax    = sb->getSpecPhysMaxX(),
                xscale  = (double)(plotwidth) / (xmax - xmin),
                yscale  = (double)(plotheight-1) / (ymax - ymin);

        if (close) {
            j = 1;
        }

        if (settings.plotstyle == 0) {
            points.resize(n + j * 2);
            for (int i = 0; i < n; i++) {
                if (gsl_finite(y[i]) &&
                    (x[i] >= xmin-fabs(cdelt) && x[i] <= xmax+fabs(cdelt)))
                {
                    points.setPoint(j, (int)((x[i]-xmin) * xscale + 0.5), (int)((y[i] - ymin) * yscale + 0.5));
                    j++;
                }
            }
        } else if (n > 2) {
            points.resize(n * 2 + j * 2);
    //        double firstx = x[0];
            if (gsl_finite(y[0])) {
                if (x[0] >= xmin-fabs(cdelt) && x[0] <= xmax+fabs(cdelt)) {
                    points.setPoint(j, (int)(((x[0]-(x[1]-x[0])/2.)-xmin) * xscale + 0.5), (int)((y[0] - ymin) * yscale + 0.5));
                    j++;
                    points.setPoint(j, (int)(((x[0]+x[1])/2.-xmin) * xscale + 0.5), (int)((y[0] - ymin) * yscale + 0.5));
                    j++;
                }
            }
            for (int i = 1; i < n-1; i++) {
                if (gsl_finite(y[i]) &&
                    (x[i] >= xmin-fabs(cdelt) && x[i] <= xmax+fabs(cdelt)))
                {
                    points.setPoint(j, (int)(((x[i-1]+x[i])/2.-xmin) * xscale + 0.5), (int)((y[i] - ymin) * yscale + 0.5));
                    j++;
                    points.setPoint(j, (int)(((x[i]+x[i+1])/2.-xmin) * xscale + 0.5), (int)((y[i] - ymin) * yscale + 0.5));
                    j++;
                }
            }
            if (gsl_finite(y[n-1]) &&
                (x[n-1] >= xmin-fabs(cdelt) && x[n-1] <= xmax+fabs(cdelt)))
            {
                points.setPoint(j, (int)(((x[n-2]+x[n-1])/2.-xmin) * xscale + 0.5), (int)((y[n-1] - ymin) * yscale + 0.5));
                j++;
                points.setPoint(j, (int)(((x[n-1]+(x[n-1]-x[n-2])/2.)-xmin) * xscale + 0.5), (int)((y[n-1] - ymin) * yscale + 0.5));
                j++;
            }
        }
        if (close) {
            j++;
            points.setPoint(0, points.point(1).x(), 0);
            points.setPoint(j-1, points.point(j-2).x(), 0);
        }
    }
    return j;
}

void QFitsView1D::paintEvent(QPaintEvent *e) {
    if (ndata == 0) {
        return;
    }

    QFitsSingleBuffer *sb = getMyBuffer();
    if (sb != NULL) {
        double  xmin    = sb->getSpecPhysMinX(),
                xmax    = sb->getSpecPhysMaxX(),
                ymin    = sb->getSpecPhysMinY(),
                ymax    = sb->getSpecPhysMaxY();

        QPainter pa;
        pa.begin(this);
        pa.setPen(Qt::lightGray);

        // ugly hack to ensure that background is cleared with background color....
        // better would be following line, but makes everything white here
        // QColor c = QWidget::palette().color(QWidget::backgroundRole());
        int r           = 0,
            g           = 0,
            b           = 0;
        QApplication::palette().window().color().getRgb(&r, &g, &b);
        pa.setBrush(QColor(r, g, b));

        QFitsWidget1D *widget1D = dynamic_cast<QFitsWidget1D *>(parentWidget());
        if (widget1D != NULL) {
            if (sb->isMultiBufferChild()) {
                QFitsSingleBuffer *sb_marked = sb->getMyMultiBuffer()->getMarkedSingleBuffer();
                if ((sb_marked != NULL) && (sb == sb_marked)) {
                    // buffer is marked, set background to grey
                    QPen pen(Qt::cyan, 3);
                    pa.setPen(pen);
//                    pa.setBrush(Qt::lightGray);
                }
            }
        }

        // draw gray bounding box
        pa.drawRect(0, 0, width()-1, height()-1);
//        pa.setPen(Qt::black);
        pa.setPen(QWidget::palette().color(QWidget::foregroundRole()));

        if (xmin == xmax) {
            xmin -= 1.0;
            xmax += 1.0;
        }
        if (ymin == ymax) {
            ymin -= 1.0;
            ymax += 1.0;
        }

        pa.drawText(QRect(0, 0, width() - margin, fh), Qt::AlignRight|Qt::AlignBottom, posinfo);
        drawAxis(&pa, ymin, ymax);
        pa.setWorldTransform(m);

        QPolygon array;
        int j = calcPolyline(ndata, xdata, ydata, array, ymin, ymax);
        pa.setClipRect(0, -1, plotwidth, plotheight+1);
        pa.drawPolyline(array.constData(), j);

        if (n2data > 0) {
            j = calcPolyline(n2data, x2data, y2data, array, ymin, ymax);
            pa.setPen(Qt::green);
            pa.drawPolyline(array.constData(), j);
//            pa.setPen(Qt::black);
            pa.setPen(QWidget::palette().color(QWidget::foregroundRole()));
        }

        drawSelection(&pa);
        pa.end();
    }
}

void QFitsView1D::keyPressEvent(QKeyEvent *e) {
    QFitsWidget1D *widget1D = dynamic_cast<QFitsWidget1D *>(parentWidget());
    if (widget1D != NULL) {
        widget1D->plotterKeyPressEvent(e);
    }
}

void QFitsView1D::drawAxis(QPainter *pa, double ymin, double ymax) {
    QFitsSingleBuffer *sb = getMyBuffer();
    if (sb != NULL) {
        int     n       = 0;
        double  start   = 0.,
                delta   = 0.;
        double  xmin    = sb->getSpecPhysMinX(),
                xmax    = sb->getSpecPhysMaxX(),
                xscale  = (double)(plotwidth) / (xmax - xmin),
                yscale  = (double)(plotheight-1) / (ymax - ymin);
        QString text;
        QRect   textrect,
                limits(fw, height() - fh, plotwidth + 2*margin, fh);

        if ((fabs(xmin-xmax) > 1e-100) && (xmax > xmin)){
            calcAxisScale(xmin, xmax, &n, &start, &delta);

            pa->resetTransform();
            pa->drawLine(fw, margin, fw, height() - fh);
            pa->drawLine(fw, height() - fh, width() - margin, height() - fh);

            for (int i = -15; i < 15; i++) {
                if ((fabs(start + i*delta) >= 1e6) || (fabs(start + i*delta) <= 1e-3)) {
                    text = QString::number(start + i*delta);
                } else {
                    if (n <= 0) {
                        text = QString::number(start + i*delta, 'f', labs(n == 0 ? 1 : n));
                        while (text.right(1) == "0") {
                            text.remove(text.length() - 1, 1);
                        }
                        if (text.right(1) == ".") {
                            text.remove(text.length() - 1, 1);
                        }
                    } else {
                        text = QString::number((int)(start + i*delta));
                    }
                }

                textrect = QRect((int)((start + i*delta-xmin) * xscale + 0.5) - 100 + fw + margin, height() - fh + 2, 201, fh);
                if (limits.contains(pa->boundingRect(textrect, Qt::AlignHCenter|Qt::AlignTop, text))) {
                    pa->drawText(textrect, Qt::AlignHCenter|Qt::AlignTop, text);
                    pa->drawLine((int)((start + i*delta-xmin) * xscale + 0.5) + fw + margin, height() - fh, (int)((start + i*delta-xmin) * xscale + 0.5) + fw + margin, height() - fh - 5);
                }
            }
            // label x-axis
            Fits *f = sb->getCubeDisplay1D();
            if (f != NULL) {
                char value[81];
                text.clear();
                if (f->GetStringKey("CTYPE1", value)) {
                    dpString type = value;
                    type = type.left(4);
                    if (type == "WAVE") text = QChar(955);
                    else if (type.left(4) == "FREQ") text = QChar(957);
                    else if (type.left(4) == "ENER") text = "E";
                    else if (type.left(4) == "WAVN") text = QChar(954);
                    else if (type.left(4) == "VRAD") text = "V";
                    else if (type.left(4) == "VOPT") text = "Z";
                    else if (type.left(4) == "ZOPT") text = "z";
                    else if (type.left(4) == "AWAV") { text = QChar(955); text += "a"; }
                    else if (type.left(4) == "VELO") text = "v";
                    else if (type.left(4) == "BETA") text = QChar(946);
                    else text = type.c_str();
                }
                if (f->GetStringKey("CUNIT1", value)) {
                    if (!QString(value).trimmed().isEmpty()) {
                        text += " [";
                        text += QString(value).trimmed();
                        text += "]";
                    }
                }
                textrect = limits;
                textrect.translate(0, -2);
                pa->fillRect(pa->boundingRect(textrect, Qt::AlignHCenter|Qt::AlignBottom, text), QApplication::palette().window().color());
                pa->drawText(textrect, Qt::AlignHCenter|Qt::AlignBottom, text);
            }

            calcAxisScale(ymin, ymax, &n, &start, &delta);
            limits = QRect(0, 0, fw, plotheight + 2*margin);

            for (int i = -15; i < 15; i++) {
                if ((fabs(start + i*delta) >= 1e6) || (fabs(start + i*delta) <= 1e-3)) {
                    text = QString::number(start + i*delta);
                } else {
                    if (n <= 0) {
                        text = QString::number(start + i*delta, 'f', labs(n == 0 ? 1 : n));
                        while (text.right(1) == "0") {
                            text.remove(text.length() - 1, 1);
                        }
                        if (text.right(1) == ".") {
                            text.remove(text.length() - 1, 1);
                        }
                    } else {
                        text = QString::number((int)(start + i*delta));
                    }
                }

                textrect = QRect(0, height() - fh - (int)((start + i*delta-ymin) * yscale + 0.5) - fh - margin, fw-5, 2*fh);
                if (limits.contains(pa->boundingRect(textrect, Qt::AlignRight|Qt::AlignVCenter, text))) {
                    pa->drawText(textrect, Qt::AlignRight|Qt::AlignVCenter, text);
                    pa->drawLine(fw, height() - fh - margin - (int)((start + i*delta-ymin) * yscale + 0.5), fw + 5, height() - fh - margin - (int)((start + i*delta-ymin) * yscale + 0.5));
                }
            }
            if (f != NULL) {
                char value[81];
                if (f->GetStringKey("BUNIT", value)) {
                    text = QString(value).trimmed();
                    pa->rotate(-90.);
                    textrect = QRect(0, 0, -height() + fh, fw);
                    if (textrect.contains(pa->boundingRect(textrect, Qt::AlignHCenter|Qt::AlignTop, text))) {
                        pa->fillRect(pa->boundingRect(textrect, Qt::AlignHCenter|Qt::AlignTop, text), QApplication::palette().window().color());
                        pa->drawText(textrect, Qt::AlignHCenter|Qt::AlignTop, text);
                    }
                    pa->rotate(90.);
                }
            }
        }
    }
}

void QFitsView1D::drawSelection(QPainter *p) {
    if ((selectstart != QPoint(-1, -1)) && (selectend != QPoint(-1, -1))) {
        p->setBrush(QColor(0, 0, 0, 100));
        p->resetTransform();
        p->drawRect(QRect(selectstart, selectend));
    }
}

void QFitsView1D::mousePressEvent(QMouseEvent *m) {
    QFitsSingleBuffer *sb = getMyBuffer();
    QFitsMultiBuffer  *mb = dynamic_cast<QFitsMultiBuffer*>(fitsMainWindow->getCurrentBuffer());
    if ((sb != NULL) || (mb != NULL)) {
        QFitsWidget1D *widget1D = dynamic_cast<QFitsWidget1D*>(parentWidget());
        if (m->button() == Qt::LeftButton) {
            bool handled = false;
            if ((sb != NULL) && (widget1D != NULL)) {
                handled = widget1D->handleMousePressEvent(m);
                if (sb->isMultiBufferChild()) {
                    setMousePressedPos(m->pos().x(), m->pos().y());
                } else {
                    if (fitsMainWindow->main_view->getCurrentBuffer() != sb)
                        fitsMainWindow->main_view->setCurrentBufferFromWidget(sb);
                }
            }

            if (!handled) {
                selectstart = m->pos();
                selectend = QPoint(-1, -1);
            }
        } else if (m->button() == Qt::RightButton) {
            if ((sb != NULL) && (widget1D != NULL)) {
                exportSpecAsciiAct->setVisible(true);
                exportSpecFitsAct->setVisible(true);

                if (sb->isMultiBufferChild()) {
                    // MultiBuffer
                    saveMBbufferAct->setVisible(true);
                    copyMBbufferAct->setVisible(true);
                } else {
                    // SingleBuffer
                    saveMBbufferAct->setVisible(false);
                    copyMBbufferAct->setVisible(false);
                }
            } else {
                // CubeSpectrumViewer
                exportSpecAsciiAct->setVisible(false);
                exportSpecFitsAct->setVisible(false);
                saveMBbufferAct->setVisible(false);
                copyMBbufferAct->setVisible(false);
            }

            // show for widget1D and CubeSpectrumViewer
            rightPopupMenu->exec(m->globalPos());
        }
    }
}

void QFitsView1D::mouseMoveEvent(QMouseEvent *e) {
    if (!hasFocus()) {
        setFocus();
    }

    QFitsWidget1D* widget1D = dynamic_cast<QFitsWidget1D*>(parentWidget());
    if (widget1D != NULL) {
        widget1D->plotterMouseMoveEvent(e);
    }

    int pos = calcPos(e->pos());

    QFitsSingleBuffer *sb = getMyBuffer();
    if (sb != NULL) {
        if (sb->getImage()->width() < sb->getImage()->height()) {
            // if user has switched from ViewTable to ViewImage, image has to be rotated
            sb->rotImage(false);
        }
        fitsMainWindow->updateTools(pos, 1, pos, 1);
    }
    update();
}

void QFitsView1D::mouseMovedInSubwindow(const QPoint &globalPos) {
    QMouseEvent e(QEvent::MouseMove, mapFromGlobal(globalPos),
                  Qt::NoButton, Qt::NoButton, Qt::NoModifier);
    mouseMoveEvent(&e);
}

QFitsSingleBuffer* QFitsView1D::getMyBuffer() {
    QFitsWidget1D           *widget1D = dynamic_cast<QFitsWidget1D *>(parentWidget());
    QFitsCubeSpectrumViewer *csViewer = dynamic_cast<QFitsCubeSpectrumViewer*>(this);

    if (widget1D != NULL) {
        return widget1D->getMyBuffer();
    } else if (csViewer != NULL) {
        return fitsMainWindow->getActualSB();
    }

    return NULL;
}

void QFitsView1D::mouseReleaseEvent(QMouseEvent *m) {
    QFitsSingleBuffer *sb = getMyBuffer();
    QFitsMultiBuffer  *mb = dynamic_cast<QFitsMultiBuffer*>(fitsMainWindow->getCurrentBuffer());
    if ((sb != NULL) || (mb != NULL)) {
        if ((selectstart != QPoint(-1, -1)) && (selectend != QPoint(-1, -1))) {
            // CubeSpectrum
            if (sb != NULL) {
                // do this only if the SB has been marked
                double  xmin    = sb->getSpecPhysMinX(),
                        xmax    = sb->getSpecPhysMaxX(),
                        xscale  = (double)(plotwidth) / (xmax - xmin),
                        start   = (selectstart.x() - fw - margin) / xscale + xmin,
                        end     = (selectend.x() - fw - margin) / xscale + xmin;

                if (start > end) {
                    double tmp = start;
                    start = end;
                    end = tmp;
                }

                if ((mb != NULL) && mb->hasCubeSpecHomogenSB()) {
                    // if we have a homogen MB, just pick the first valid Fits
                    // otherwise we really don't care anymore...
                    sb = mb->getFirstImageBuffer();
                }

                if (sb != NULL) {
                    int dim = sb->getDpData()->fvalue->Naxis(0);
                    if ((dim > 0) &&
                        (fabs(sb->getDpData()->fvalue->getCRPIX(dim) - 1) < 1e-6) &&
                        (fabs(sb->getDpData()->fvalue->getCRVAL(dim) - 1) < 1e-6) &&
                        (fabs(fabs(sb->getDpData()->fvalue->getCDELT(dim)) - 1) < 1e-6))
                    {
                        start = round(start);
                        end = round(end);
                    }
                    emit rangeChanged(start, end);
                    setXRange(start, end);
                    if ((mb != NULL) && mb->hasCubeSpecHomogenSB()) {
                        mb->updateHomogenSB();
                    }
                }
            }
        } else {
            // QFitsWidget1D
            if ((m->button() == Qt::LeftButton) &&
                (sb != NULL) &&
                sb->isMultiBufferChild())
            {
                int x = 0, y = 0;
                getMousePressedPos(&x, &y);
                if ((m->pos().x() == x) && (m->pos().y() == y)) {
                    sb->setMarkedSingleBuffer(sb);
                }
            }
        }
    }

    selectstart = QPoint(-1, -1);
    selectend = QPoint(-1, -1);
}

void QFitsView1D::resizeEvent(QResizeEvent *e) {
//dp_debug("    QFitsView1D::resizeEvent()\n"); fflush(stdout);
    fh = (int)(fontMetrics().height() * 2.5);
    fh = (int)(fontMetrics().height() * 1.5);
    fw = fontMetrics().boundingRect("000000000").width();

    m = QTransform(1, 0, 0, -1, fw + margin, e->size().height()-fh-margin);
    plotwidth = e->size().width() - fw - 2*margin;
    plotheight = e->size().height() - fh - 2*margin;
}

void QFitsView1D::enterEvent(QEvent*) {
    QFitsWidget1D* widget1D = dynamic_cast<QFitsWidget1D*>(parentWidget());
    if (widget1D != NULL) {
        setFocus();
        widget1D->enterBuffer();
        QFitsSingleBuffer *sb = widget1D->getMyBuffer();
        if ((sb != NULL) && (sb->isMultiBufferChild())) {
            if (sb->isEmptyPrimary()) {
//@                fitsMainWindow->mytoolbar->enableControlsView1D(false);
            } else {
//@                fitsMainWindow->mytoolbar->enableControlsView1D(true);
                if (getMyBuffer()->isMultiBufferChild() &&
                    (getMyBuffer()->getMyMultiBuffer()->getMarkedSingleBuffer() != NULL)) {
                    // do nothing
                } else {
//@                    fitsMainWindow->mytoolbar->setRangeValues();
                }
            }
        } else {
            // no need to do anything here, we have just SB, this one is always up-to-date
        }
    }
}

void QFitsView1D::leaveEvent (QEvent *e) {
    QFitsWidget1D* widget1D = dynamic_cast<QFitsWidget1D*>(parentWidget());
    if (widget1D != NULL) {
        widget1D->leaveBuffer();
        QFitsSingleBuffer *sb = widget1D->getMyBuffer();
        if ((sb != NULL) && (sb->isMultiBufferChild())) {
            // this belongs to a QFitsMultiBuffer
            if (sb->getMyMultiBuffer()->getMarkedSingleBuffer() == NULL) {
//@                fitsMainWindow->mytoolbar->blankRangeValues();
            }
        } else {
            // this belongs to a "real" QFitsSingleBuffer
        }
    }
    posinfo = " ";
    update();
}

void QFitsView1D::zoomOut() {
    if (ndata != 0) {
        autoScaleSpectrum = true;
        emit rangeChanged(xdata[0], xdata[ndata-1]);
        setXRange(xdata[0], xdata[ndata-1]);
        QFitsMultiBuffer  *mb = dynamic_cast<QFitsMultiBuffer*>(fitsMainWindow->getCurrentBuffer());
        if ((mb != NULL) && mb->hasCubeSpecHomogenSB()) {
            mb->updateHomogenSB();
        }
    }
}

void QFitsView1D::setPlotStyleLines() {
    settings.plotstyle = 0;
    update();
}

void QFitsView1D::setPlotStyleHistogram() {
    settings.plotstyle = 1;
    update();
}

void QFitsView1D::exportSpecAsciiSlot() {
    QFitsWidget1D* widget1D = dynamic_cast<QFitsWidget1D*>(parentWidget());
    if (widget1D != NULL) {
        setMouseTracking(false);

        QReadLocker locker(&buffersLock);
        QFitsSingleBuffer *sb = widget1D->getMyBuffer();
        if ((sb != NULL) &&
            (sb->getDpData()->type == typeFits) &&
            (sb->getDpData()->fvalue != NULL))
        {
            QString fname = QFileDialog::getSaveFileName(this,
                                                         "QFitsView - Save as ASCII",
                                                         settings.lastSavePath);
            if (fname.isNull()) {
                return;
            }
            QFileInfo finfo(fname);
            settings.lastSavePath = finfo.absoluteDir().path();
            settings.lastSavePath.replace("\\", "/");

            FILE    *fd = fopen(fname.toStdString().c_str(), "w+b");
            if (fd == NULL) {
                fname = "Could not open file " + fname + " for writing";
                QMessageBox::warning(this, "QFitsView", fname);
                return;
            }

            Fits    *f      = sb->getDpData()->fvalue;
            double  start   = 0.,
                    delta   = 0.;
            delta = f->getCDELT(1);
            if (delta == 0.0)  {
                delta = 1.0;
            }
            start = f->getCRVAL(1) - delta * (f->getCRPIX(1) - 1);
            if (start == 0.0) {
                start = 1.0;
            }

            for (int i = 0; i < f->Nelements(); i++) {
                if (fprintf(fd, "%20.10f %20.10f\n", pixwave(i+1, f->getCRPIX(1), f->getCRVAL(1), f->getCDELT(1), logwave), f->ValueAt(i)) < 0) {
                    QMessageBox::warning(this, "QFitsView", "Could not write data");
                    fclose(fd);
                    return;
                }
            }
            fclose(fd);
        }

        setMouseTracking(true);
    }
}

void QFitsView1D::exportSpecFitsSlot() {
    QFitsWidget1D* widget1D = dynamic_cast<QFitsWidget1D*>(parentWidget());
    if (widget1D != NULL) {
        setMouseTracking(false);
        QFitsSingleBuffer *sb = widget1D->getMyBuffer();
        if (sb != NULL) {
            sb->saveFits();
        }
        setMouseTracking(true);
    }
}

void QFitsView1D::saveMBbufferSlot() {
    QFitsWidget1D* widget1D = dynamic_cast<QFitsWidget1D*>(parentWidget());
    if (widget1D != NULL) {
        setMouseTracking(false);
        QFitsSingleBuffer *sb = widget1D->getMyBuffer();
        if (sb != NULL) {
            sb->saveFits();
        }
        setMouseTracking(true);
    }
}

void QFitsView1D::copyMBbufferSlot() {
    QFitsWidget1D* widget1D = dynamic_cast<QFitsWidget1D*>(parentWidget());
    if (widget1D != NULL) {
        QFitsSingleBuffer *sb = widget1D->getMyBuffer();
        if (sb != NULL) {
            setMouseTracking(false);
            QString cmd = QString(freeBufferName().c_str()) + " = " +
                          QString(fitsMainWindow->main_view->getCurrentBufferIndex().c_str()) +
                          sb->getExtensionIndicesString();
            dpuser_widget->executeCommand(cmd);
        }
        setMouseTracking(true);
    }
}

void QFitsView1D::sendToPlotterSlot() {
#ifdef HAS_PLOTTING
    fitsMainWindow->plplotWindow->addBufferData(QString(fitsMainWindow->main_view->getCurrentBufferIndex().c_str()), -1, -1);
#endif /* HAS_PLOTTING */
}

void QFitsView1D::calcAxisScale(const double min, const double max, int *n, double *start, double *delta) {
    int     increment   = 0,
            nn          = 0;
    double  nstart      = 0.,
            ndelta      = 0.,
            range       = max - min;

    if (fabs(range) > 1e-100) {
        if (range < 1.0) {
            while (range < 1.0) {
                range *= 10.;
                nn--;
            }
        } else if (range > 10.0) {
            while (range > 10.0) {
                range /= 10.;
                nn++;
            }
        }

        increment = (int)(range + 0.5);
        switch (increment) {
            case 1: ndelta = .2; break;
            case 2:
            case 3: ndelta = .5; break;
            case 4:
            case 5:
            case 6:
            case 7: ndelta  = 1.; break;
            case 8:
            case 9:
            case 10: ndelta = 2.; break;
            default: ndelta = 1.; break;
        }
        ndelta = pow(10., nn) * ndelta;
        nstart = (int)(pow(10., -nn) * min);
        nstart *= pow(10., nn);

        if ((nn < 0) && (increment < 4)) {
            nn--;
        }

        if ((nstart < 0.0) && (nstart / ndelta != (int)(nstart / ndelta))) {
            nstart -= ndelta / 2.0;
        }

        *n = nn;
        *delta = ndelta;
        *start = nstart;
    }
}

double QFitsView1D::pixelToWavelength(int number) {
    QFitsSingleBuffer *sb = getMyBuffer();
    if (sb != NULL) {
        double  xmin    = sb->getSpecPhysMinX(),
                xmax    = sb->getSpecPhysMaxX();

        return number / ((double)(plotwidth) / (xmax - xmin)) + xmin;
    }
    return 0.;
}

double QFitsView1D::pixelToValue(int number) {
    QFitsSingleBuffer *sb = getMyBuffer();
    if (sb != NULL) {
        double  ymin    = sb->getSpecPhysMinY(),
                ymax    = sb->getSpecPhysMaxY();

        return ymin + (ymax - ymin) * (double)(height() - fh - margin - number) / (double)plotheight;
    }
    return 0.;
}

int QFitsView1D::wavelengthToPixel(double wavelength) {
    QFitsSingleBuffer *sb = getMyBuffer();
    if (sb != NULL) {
        double  xmin    = sb->getSpecPhysMinX(),
                xmax    = sb->getSpecPhysMaxX();

        return (int)((wavelength - xmin) * ((double)(plotwidth) / (xmax - xmin)) + 0.5);
    }
    return 0.;
}

int QFitsView1D::valueToPixel(double value) {
    QFitsSingleBuffer *sb = getMyBuffer();
    if (sb != NULL) {
        double  ymin    = sb->getSpecPhysMinY(),
                ymax    = sb->getSpecPhysMaxY();
        return (int)-((double)(value - ymin) / (double)(ymax - ymin) * (double)plotheight - height() + fh + margin);
    }
    return 0.;
}
