/***************************** LICENSE START ***********************************

 Copyright 2015 ECMWF and INPE. This software is distributed under the terms
 of the Apache License version 2.0. In applying this license, ECMWF does not
 waive the privileges and immunities granted to it by virtue of its status as
 an Intergovernmental Organization or submit itself to any jurisdiction.

 ***************************** LICENSE END *************************************/

#include "MvQProfileView.h"

#include <QtGlobal>
#include <QDebug>
#include <QGraphicsRectItem>
#include <QGraphicsScene>
#include <QGraphicsSceneMouseEvent>
#include <QMouseEvent>
#include <QPainter>

#include "MvProfileData.h"
#include "MvQMethods.h"
#include "MvQTheme.h"

#include <cmath>

//====================================
//  MvQNodetem
//====================================

MvQNodeItem::MvQNodeItem(MvQProfileItem* prof, bool editable, float yPos) :
    prof_(prof),
    editable_(editable),
    yPos_(yPos)
{
    setFlag(ItemSendsGeometryChanges);
    setAcceptHoverEvents(true);
    setZValue(-1);

    if (editable_) {
        idleBrush_ = MvQTheme::brush("scmview", "editable_curve");
        hoverBrush_ = MvQTheme::brush("scmview", "editable_curve_hover");
        moveBrush_ = hoverBrush_;
        brush_ = idleBrush_;

        idlePen_ = QPen(Qt::NoPen);
        hoverPen_ = pen_;
        movePen_ = pen_;
        pen_ = idlePen_;
    }
    else {
        idleBrush_ = MvQTheme::brush("scmview", "curve");
        hoverBrush_ = MvQTheme::brush("scmview", "curve_hover");
        moveBrush_ = hoverBrush_;
        brush_ = idleBrush_;

        idlePen_ = QPen(Qt::NoPen);
        hoverPen_ = pen_;
        movePen_ = pen_;
        pen_ = idlePen_;
    }

    idleSize_ = 6;
    moveSize_ = 10;
    hoverSize_ = 10;
    size_ = idleSize_;
}

MvQNodeItem::~MvQNodeItem() = default;

void MvQNodeItem::addEdge(MvQEdgeItem* edge)
{
    edges_ << edge;
    edge->adjust();
}

QRectF MvQNodeItem::boundingRect() const
{
    qreal adjust = 2;
    return {-size_ / 2. - adjust, -size_ / 2. - adjust,
            size_ + adjust, size_ + adjust};
}

void MvQNodeItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* /*option*/, QWidget*)
{
    painter->save();

    painter->setPen(pen_);
    painter->setBrush(brush_);
    painter->drawEllipse(-size_ / 2., -size_ / 2., size_, size_);

    painter->restore();
}

QVariant MvQNodeItem::itemChange(GraphicsItemChange change, const QVariant& value)
{
    switch (change) {
        case ItemPositionHasChanged:
            foreach (MvQEdgeItem* edge, edges_)
                edge->adjust();
            // prof_->nodeMoved(this);
            break;
        default:
            break;
    }

    return QGraphicsItem::itemChange(change, value);
}

void MvQNodeItem::mousePressEvent(QGraphicsSceneMouseEvent* /*event*/)
{
    if (editable_) {
        pen_ = movePen_;
        brush_ = moveBrush_;

        prepareGeometryChange();
        size_ = moveSize_;
    }

    // QGraphicsItem::mousePressEvent(event);
}

void MvQNodeItem::mouseReleaseEvent(QGraphicsSceneMouseEvent* /*event*/)
{
    if (editable_) {
        prof_->nodeMoved(this);
        pen_ = idlePen_;
        brush_ = idleBrush_;

        prepareGeometryChange();
        size_ = idleSize_;
    }
    // QGraphicsItem::mouseReleaseEvent(event);
}

void MvQNodeItem::mouseMoveEvent(QGraphicsSceneMouseEvent* event)
{
    if (editable_) {
        // We keep the node on a fixed level!
        QPointF pp = mapToParent(event->pos());
        setPos(QPointF(pp.x(), yPos_));
        pen_ = movePen_;
        brush_ = moveBrush_;
        update();
    }
    // QGraphicsItem::mouseMoveEvent(event);
}

void MvQNodeItem::hoverEnterEvent(QGraphicsSceneHoverEvent* /*event*/)
{
    pen_ = hoverPen_;
    brush_ = hoverBrush_;

    prepareGeometryChange();
    size_ = hoverSize_;
}

void MvQNodeItem::hoverLeaveEvent(QGraphicsSceneHoverEvent* /*event*/)
{
    pen_ = idlePen_;
    brush_ = idleBrush_;

    prepareGeometryChange();
    size_ = idleSize_;
}

//====================================
//  MvQEdgeItem
//====================================

MvQEdgeItem::MvQEdgeItem(MvQNodeItem* fromNode, MvQNodeItem* toNode) :
    fromNode_(fromNode),
    toNode_(toNode)
{
    if (fromNode->editable()) {
        pen_ = QPen(MvQTheme::colour("scmview", "editable_curve"), 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
    }
    else {
        pen_ = QPen(MvQTheme::colour("scmview", "curve"), 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
    }
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
    setAcceptedMouseButtons(Qt::MouseButtons());
#else
    setAcceptedMouseButtons(0);
#endif
    fromNode_->addEdge(this);
    toNode_->addEdge(this);
    adjust();
}

void MvQEdgeItem::adjust()
{
    if (!fromNode_ || !toNode_)
        return;

    QLineF line(mapFromItem(fromNode_, 0, 0), mapFromItem(toNode_, 0, 0));

    qreal length = line.length();

    prepareGeometryChange();

    fromPoint_ = toPoint_ = line.p1();

    if (length > qreal(6.)) {
        fromPoint_ = line.p1();
        toPoint_ = line.p2();
    }
    else {
        fromPoint_ = toPoint_ = line.p1();
    }
}

QRectF MvQEdgeItem::boundingRect() const
{
    if (!fromNode_ || !toNode_)
        return {};

    qreal penWidth = 1;
    qreal extra = penWidth;

    return QRectF(fromPoint_, QSizeF(toPoint_.x() - fromPoint_.x(),
                                     toPoint_.y() - fromPoint_.y()))
        .normalized()
        .adjusted(-extra, -extra, extra, extra);
}

void MvQEdgeItem::paint(QPainter* painter, const QStyleOptionGraphicsItem*, QWidget*)
{
    QLineF line(fromPoint_, toPoint_);
    if (qFuzzyCompare(line.length(), qreal(0.)))
        return;

    painter->save();
    painter->setPen(pen_);
    painter->drawLine(line);
    painter->restore();
}


//====================================
//  MvQProfileItem
//====================================

MvQProfileItem::MvQProfileItem(MvQProfileView* view, MvProfileData* data, bool editable, int editRadius,
                               int firstIndex, int lastIndex, QSize size,
                               bool setMinMax, float valMin, float valMax) :
    view_(view),
    data_(data),
    zeroLine_(false),
    minXSize_(300),
    minYSize_(500),
    editable_(editable),
    editRadius_(editRadius),
    firstIndex_(firstIndex),
    lastIndex_(lastIndex),
    subPlot_(false),
    presetMinMax_(setMinMax),
    presetMin_(valMin),
    presetMax_(valMax),
    pen_(MvQTheme::colour("scmview", "pen")),
    gridPen_(MvQTheme::colour("scmview", "grid")),
    outsideBrush_(MvQTheme::colour("scmview", "outside")),
    sceneBgBrush_(MvQTheme::colour("scmview", "scenebg")),
    editableColour_(MvQTheme::colour("scmview", "editable_curve")),
    standardColour_(MvQTheme::colour("scmview", "curve"))
{
    if (!data_) {
        setEnabled(false);
        return;
    }

    if (firstIndex_ < 0 || firstIndex_ >= data_->count())
        firstIndex_ = 0;

    if (firstIndex_ >= lastIndex_ || lastIndex_ >= data_->count())
        lastIndex_ = data_->count() - 1;

    initFrame(size);
    adjustFrame();

    for (int i = 0; i < data_->count(); i++) {
        float val = data_->value(i);
        float yp = (i - firstIndex_) * yRatio_;
        auto* node = new MvQNodeItem(this, editable, yp);
        node->setParentItem(this);
        node->setPos((val - frameXMin_) * xRatio_, yp);
        if (editable) {
            node->setFlag(ItemIsMovable);
        }

        nodes_ << node;
    }

    for (int i = 0; i < data_->count() - 1; i++) {
        auto* edge = new MvQEdgeItem(nodes_[i], nodes_[i + 1]);
        edge->setParentItem(this);
        edges_ << edge;
    }


    view->scene()->addItem(this);

    adjustData();
}

MvQProfileItem::~MvQProfileItem()
{
    if (data_)
        delete data_;
}

void MvQProfileItem::setStep(int step)
{
    step_ = step;

    if (!isEnabled())
        return;

    adjustFrame();
    adjustData();
    update();
}

void MvQProfileItem::adjustFrame()
{
    // Min max
    float valMin, valMax;

    if (presetMinMax_) {
        valMin = presetMin_;
        valMax = presetMax_;
    }
    else {
        if (data_->isRangeSet()) {
            valMin = data_->rangeMin();
            valMax = data_->rangeMax();
        }
        else
            data_->valueRange(firstIndex_, lastIndex_, valMin, valMax);
    }

    // Find a nice plot range
    float valRange = valMax - valMin;

    if (valMin < 0 && valMax > 0) {
        zeroLine_ = true;
    }

    frameXMin_ = 0.;
    frameXMax_ = 0.;

    QFont font;
    QFontMetrics fm(font);
    int twMax = MvQ::textWidth(fm, QString::number(valMax));
    int twMin = MvQ::textWidth(fm, QString::number(valMin));
    int textWidth = (twMin < twMax) ? twMax : twMin;

    if (valRange > 0) {
        int digits = static_cast<int>(log10(valRange));
        tickXStep_ = pow(10., digits);
        if (valRange / tickXStep_ < 3) {
            tickXStep_ = pow(10., digits - 1);
        }
    }
    else if (fabs(valMin) > 0.0000000001 && fabs(valMin) < 1000000000.) {
        int digits = static_cast<int>(log10(fabs(valMin)));
        tickXStep_ = pow(10., digits);

        if (fabs(valMin / tickXStep_) < 3) {
            tickXStep_ = pow(10., digits - 1);
        }
    }
    else {
        tickXStep_ = 1;
    }

    // We need to ensure that tickXStep_ is greater than 0!!!
    if (tickXStep_ <= 0)
        tickXStep_ = 1.;

    tickXLabelStep_ = tickXStep_;
    tickXLabelStepOffset_ = 0;

    // Because tickXStep_ alwasy greater than 0 these two values will always be different!!!
    frameXMin_ = static_cast<int>((valMin - tickXStep_ - tickXStep_ / 1000.) / tickXStep_) * tickXStep_;
    frameXMax_ = static_cast<int>((valMax + tickXStep_ + tickXStep_ / 1000.) / tickXStep_) * tickXStep_;

    // So this division can always be made!!!
    xRatio_ = frameRect_.width() / (frameXMax_ - frameXMin_);

    // Adjustment
    if (xRatio_ * tickXStep_ < textWidth) {
        tickXLabelStep_ *= 2;
        int labelFactor = static_cast<int>(round(frameXMin_ / tickXStep_));
        if (labelFactor % 2 != 0)
            tickXLabelStepOffset_ = 1;
    }

    if (lastIndex_ != firstIndex_)
        yRatio_ = frameRect_.height() / static_cast<float>(lastIndex_ - firstIndex_);
    else
        yRatio_ = frameRect_.height();
}


QRectF MvQProfileItem::boundingRect() const
{
    return bRect_;
}

void MvQProfileItem::paint(QPainter* painter, const QStyleOptionGraphicsItem*, QWidget*)
{
    painter->save();

    painter->fillRect(bRect_, sceneBgBrush_);

    QFont font;
    QFontMetrics fm(font);

    painter->setPen(pen_);

    // Title
    if (!subPlot_) {
        QRectF titleRect(frameRect_.width() / 2. - 150, frameRect_.top() - 70, 300, 40);
        QString editableText = (editable_ == true) ? "Editable" : "";
        painter->drawText(titleRect, Qt::AlignCenter,
                          "Parameter: " + QString::fromStdString(data_->name()) +
                              " [" + QString::fromStdString(data_->units()) + "]\n" +
                              " Step: " + QString::fromStdString(data_->stepString()) +
                              "\n" + editableText);
    }
    /*else
    {
        QRectF titleRect(frameRect_.width()/2.-150,frameRect_.top()-50,300,40);
        painter->drawText(titleRect,Qt::AlignCenter,
              "Parameter: " + QString::fromStdString(data_->name()) +
                     " [" + QString::fromStdString(data_->units()) + "]");
    } */


    // Render out of normal range
    if (data_->isRangeSet()) {
        float valMin = data_->rangeMin();
        float valMax = data_->rangeMax();

        if (frameXMin_ < valMin) {
            float xp = (valMin - frameXMin_) * xRatio_;
            painter->fillRect(0, frameRect_.top(), xp, frameRect_.bottom(), outsideBrush_);
        }
        if (frameXMax_ > valMax) {
            float xp = (valMax - frameXMin_) * xRatio_;
            float xp1 = (frameXMax_ - frameXMin_) * xRatio_;
            painter->fillRect(xp, frameRect_.top(), xp1 - xp, frameRect_.bottom(), outsideBrush_);
        }
    }

    // Dash pattern for the grid
    // QVector<qreal> dashes;
    // dashes << 1 << 8;
    // gridPen.setDashPattern(dashes);

    // Lines
    // X axis - top + bottom
    for (float xv = frameXMin_; xv <= frameXMax_; xv += tickXStep_) {
        float xp = (xv - frameXMin_) * xRatio_;
        painter->drawLine(xp, frameRect_.top(), xp, frameRect_.top() - 6.);
        painter->drawLine(xp, frameRect_.bottom(), xp, frameRect_.bottom() + 6.);

        // grid
        if (firstIndex_ != lastIndex_ && xv != frameXMin_ && xv != frameXMax_) {
            painter->setPen(gridPen_);
            painter->drawLine(xp, frameRect_.top(), xp, frameRect_.bottom());
            painter->setPen(pen_);
        }

        // text
        // int xpText=xp-fm.width(QString::number(xv))/2;
        // painter->drawText(QPointF(xpText,frameRect_.bottom()+10+fm.ascent()),QString::number(xv));

        // if(!subPlot_)
        //	painter->drawText(QPointF(xpText,frameRect_.top()-10-fm.descent()),QString::number(xv));
    }

    // Text
    // X axis - top + bottom
    for (float xv = frameXMin_ + tickXLabelStepOffset_ * tickXStep_; xv <= frameXMax_; xv += tickXLabelStep_) {
        float xp = (xv - frameXMin_) * xRatio_;

        // text
        int xpText = xp - MvQ::textWidth(fm, QString::number(xv)) / 2;
        painter->drawText(QPointF(xpText, frameRect_.bottom() + 10 + fm.ascent()), QString::number(xv));

        if (!subPlot_)
            painter->drawText(QPointF(xpText, frameRect_.top() - 10 - fm.descent()), QString::number(xv));
    }


    float dy = frameRect_.height() / (1 + lastIndex_ - firstIndex_);
    int stepY = 10;
    if (dy > 15)
        stepY = 1;
    else if (dy * 2 > 15)
        stepY = 2;
    else if (dy * 5 > 15)
        stepY = 5;
    else
        stepY = 10;

    if (lastIndex_ - firstIndex_ + 1 < 6)
        stepY = 1;

    // Right axis
    if (data_->auxLevelDefined()) {
        for (int i = lastIndex_; i >= firstIndex_; i -= stepY) {
            auto xv = static_cast<float>(floor(data_->auxLevel(i) / 100.));

            float xp = frameRect_.width();
            float yp = frameRect_.top() + (i - firstIndex_) * yRatio_;
            painter->drawLine(xp, yp, xp + 6., yp);

            // Label
            int ypText = yp + fm.ascent() / 2;
            painter->drawText(QPointF(xp + 10, ypText), QString::number(xv));
        }

        // Right axis title
        painter->save();
        painter->translate(frameRect_.width() + 10 + MvQ::textWidth(fm, "1000") + 10 + fm.ascent(), frameRect_.center().y());
        painter->rotate(-90);
        QString auxTitle = QString::fromStdString(data_->auxLevelName()) + " (" + QString::fromStdString(data_->auxLevelUnits()) + ")";
        painter->drawText(-MvQ::textWidth(fm, auxTitle) / 2, 0, auxTitle);

        painter->restore();
    }

    if (lastIndex_ != firstIndex_) {
        // Left axis
        for (int i = lastIndex_; i >= firstIndex_; i -= stepY) {
            float xv = data_->level(i);
            float xp = 0;
            float yp = frameRect_.top() + (i - firstIndex_) * yRatio_;
            painter->drawLine(xp, yp, xp - 6., yp);

            // grid
            painter->setPen(gridPen_);
            painter->drawLine(xp, yp, xp + frameRect_.width(), yp);
            painter->setPen(pen_);


            int ypText = yp + fm.ascent() / 2;
            int xpText = xp - 10 - MvQ::textWidth(fm, QString::number(xv));
            painter->drawText(QPointF(xpText, ypText), QString::number(xv));
        }
    }

    // Left axis title
    painter->save();
    painter->translate(-10 - MvQ::textWidth(fm, "100") - 10, frameRect_.center().y());
    painter->rotate(-90);
    QString levTitle;
    if (!data_->levelUnits().empty()) {
        levTitle = QString::fromStdString(data_->levelName()) + " (" + QString::fromStdString(data_->levelUnits()) + ")";
    }
    else {
        levTitle = QString::fromStdString(data_->levelName());
    }
    painter->drawText(-MvQ::textWidth(fm, levTitle) / 2, 0, levTitle);
    painter->restore();

    painter->setRenderHint(QPainter::Antialiasing, false);

    // Zero line
    if (zeroLine_) {
        float xp = (0. - frameXMin_) * xRatio_;
        painter->drawLine(xp, frameRect_.top(), xp, frameRect_.bottom());
    }

    // Frame

    if (firstIndex_ != lastIndex_)
        painter->drawRect(frameRect_);
    else
        painter->drawLine(0, frameRect_.top(), frameRect_.width(), frameRect_.top());


    painter->restore();
}

bool MvQProfileItem::updateValue(const MvProfileChange& ch)
{
    if (data_->acceptChange(ch)) {
        int index = ch.level();
        float val = data_->value(index);
        nodes_.at(index)->setPos((val - frameXMin_) * xRatio_, frameRect_.top() + (index - firstIndex_) * yRatio_);

        // See if the coordinate system needs to be adjusted to the data value range
        if (val < frameXMin_ || val > frameXMax_)
            view_->adjustValueRange();
        else
            checkValueRange();

        return true;
    }
    return false;
}

void MvQProfileItem::reload()
{
    prepareGeometryChange();
    adjustFrame();
    adjustData();
    update();
}

void MvQProfileItem::nodeMoved(MvQNodeItem* node)
{
    int index = nodes_.indexOf(node);
    if (index != -1) {
        float val = data_->value(index);
        if (fabs((val - frameXMin_) * xRatio_ - node->pos().x()) > 0.1) {
            float f = frameXMin_ + node->pos().x() / xRatio_;
            // if(!data_->fitToRange(f))
            //{
            //	data_->setValue(index,f);
            //	nodes_.at(index)->setPos((f-frameXMin_)*xRatio_,(index-firstIndex_)*yRatio_);
            // }
            // else
            //{
            data_->setValue(index, f);
            //}

            if (f < frameXMin_ || f > frameXMax_) {
                view_->adjustValueRange();
            }
        }
    }
}

void MvQProfileItem::initFrame(QSize size)
{
    float w = size.width();
    if (w < minXSize_)
        w = minXSize_;

    float h = size.height();
    if (h < minYSize_)
        h = minYSize_;

    frameRect_ = QRectF(0, 0, w - 150, h - 120);
    bRect_ = frameRect_.adjusted(-60, -80, 90, 30);
}

void MvQProfileItem::initFrame(QRectF r)
{
    frameRect_ = r;
    if (subPlot_)
        bRect_ = frameRect_.adjusted(-60, -30, 90, 30);
    else
        bRect_ = frameRect_.adjusted(-60, -80, 90, 30);
}


void MvQProfileItem::adjustData()
{
    if (data_) {
        for (int i = 0; i < data_->count(); i++) {
            float val = data_->value(i);
            float yp = frameRect_.top() + (i - firstIndex_) * yRatio_;

            if (i < firstIndex_) {
                if (nodes_.at(i)->scene() != nullptr) {
                    scene()->removeItem(nodes_.at(i));
                    nodes_.at(i)->setVisible(false);
                    // qDebug() << "remove node" << i << nodes_.at(i)->scene();
                }

                if (i < edges_.count() && edges_.at(i)->scene()) {
                    scene()->removeItem(edges_.at(i));
                    edges_.at(i)->setVisible(false);
                    // qDebug() << "  remove edge " << i << edges_.at(i)->scene();
                }

                if (i > 0 && edges_.at(i - 1)->scene()) {
                    scene()->removeItem(edges_.at(i - 1));
                    edges_.at(i - 1)->setVisible(false);
                    // qDebug() << "  remove edge " << i-1 << edges_.at(i-1)->scene();
                }
            }
            else if (i > lastIndex_) {
                if (nodes_.at(i)->scene() != nullptr) {
                    scene()->removeItem(nodes_.at(i));
                    nodes_.at(i)->setVisible(false);
                    // qDebug() << "remove node" << i << nodes_.at(i)->scene();
                }

                if (i > 0 && edges_.at(i - 1)->scene()) {
                    scene()->removeItem(edges_.at(i - 1));
                    edges_.at(i - 1)->setVisible(false);
                    // qDebug() << "  remove edge " << i-1 << edges_.at(i-1)->scene();
                }

                /*if(i < edges_.count() && edges_.at(i)->scene())
                {
                    scene()->removeItem(edges_.at(i));
                    edges_.at(i)->setVisible(false);
                    qDebug() << "  remove edge " << i << edges_.at(i)->scene();
                }*/
            }
            else if (nodes_.at(i)->scene() == nullptr) {
                // qDebug() << "add node" << i << nodes_.at(i)->scene();;

                nodes_.at(i)->setParentItem(this);
                nodes_.at(i)->setVisible(true);

                if (i < edges_.count() && !edges_.at(i)->scene() && nodes_.at(i + 1)->scene() != nullptr) {
                    edges_.at(i)->setParentItem(this);
                    edges_.at(i)->setVisible(true);
                    // qDebug() << "    add edge" << i << edges_.at(i)->scene();;
                }

                if (i > 1 && !edges_.at(i - 1)->scene() && nodes_.at(i - 1)->scene() != nullptr) {
                    edges_.at(i - 1)->setParentItem(this);
                    edges_.at(i - 1)->setVisible(true);
                    // qDebug() << "    add edge" << i << edges_.at(i-1)->scene();;
                }
            }

            nodes_.at(i)->setPos((val - frameXMin_) * xRatio_, yp);
            nodes_.at(i)->setYPos(yp);
        }
    }
}

void MvQProfileItem::adjustSize(QSize size)
{
    prepareGeometryChange();

    initFrame(size);
    adjustFrame();
    adjustData();
}

void MvQProfileItem::adjustSize(QRectF r)
{
    prepareGeometryChange();

    initFrame(r);
    adjustFrame();
    adjustData();
}


void MvQProfileItem::setLevelRange(int firstIndex, int lastIndex)
{
    firstIndex_ = firstIndex;
    lastIndex_ = lastIndex;

    if (firstIndex_ < 0 || firstIndex_ >= data_->count())
        firstIndex_ = 0;

    if (firstIndex_ >= lastIndex_ || lastIndex_ >= data_->count())
        lastIndex_ = data_->count() - 1;

    prepareGeometryChange();
    adjustFrame();
    adjustData();
    update();
}


void MvQProfileItem::setLevelRange(int firstIndex, int lastIndex, float valMin, float valMax)
{
    if (presetMinMax_) {
        presetMin_ = valMin;
        presetMax_ = valMax;
    }

    setLevelRange(firstIndex, lastIndex);
}


void MvQProfileItem::setValueRange(float valMin, float valMax)
{
    if (presetMinMax_) {
        presetMin_ = valMin;
        presetMax_ = valMax;

        prepareGeometryChange();
        adjustFrame();
        adjustData();
        update();
    }
}

void MvQProfileItem::setEditRadius(int rad)
{
    editRadius_ = rad;
}

void MvQProfileItem::valueRange(int firstIndex, int lastIndex, float& valMin, float& valMax)
{
    if (data_->isRangeSet()) {
        valMin = data_->rangeMin();
        valMax = data_->rangeMax();
    }
    else {
        data_->valueRange(firstIndex, lastIndex, valMin, valMax);
    }
}


void MvQProfileItem::valueRange(float& valMin, float& valMax)
{
    if (data_->isRangeSet()) {
        valMin = data_->rangeMin();
        valMax = data_->rangeMax();
    }
    else {
        data_->valueRange(valMin, valMax);
    }
}

void MvQProfileItem::checkValueRange()
{
    // See if the coordinate system needs to be adjusted to the data value range
    if (!presetMinMax_ && !data_->isRangeSet()) {
        float valMin, valMax;
        data_->valueRange(firstIndex_, lastIndex_, valMin, valMax);
        valMax = (fabs(valMax) > fabs(valMin)) ? fabs(valMax) : fabs(valMin);
        float frameMax = (fabs(frameXMax_) > fabs(frameXMin_)) ? fabs(frameXMax_) : fabs(frameXMin_);

        if (valMax > 0.0000000001 && valMax < frameMax / 100.)
            view_->adjustValueRange();
    }
}

bool MvQProfileItem::cursorData(QPointF scenePos, QString& txt)
{
    QPointF pos = mapFromScene(scenePos);

    float xv = frameXMin_ + pos.x() / xRatio_;
    float yv = (pos.y() - frameRect_.top()) / yRatio_ + firstIndex_ + 1.5;

    if (xv >= frameXMin_ && xv <= frameXMax_ &&
        yv >= firstIndex_ && yv <= lastIndex_ + 1.5) {
        txt = " <b>Level:</b> " + QString::number(static_cast<int>(yv)) + "  <b>Value:</b> " + QString::number(xv);

        int lev = static_cast<int>(yv) - 1;
        if (lev >= 0 && lev < data_->count()) {
            float rv = data_->value(lev);
            float rp = (rv - frameXMin_) * xRatio_;
            if (fabs(rp - pos.x()) < 10) {
                if (editable_)
                    txt += " <b>Data value:</b> <font color=\"" + editableColour_.name() + "\">" + QString::number(rv) + "</font>";
                else
                    txt += " <b>Data value:</b> <font color=\"" + standardColour_.name() + "\">" + QString::number(rv) + "</font>";
            }
        }

        return true;
    }

    return false;
}

//==============================================
// MvQProfileView
//==============================================

MvQProfileView::MvQProfileView(QWidget* parent) :
    QGraphicsView(parent)
{
    setRenderHints(QPainter::Antialiasing);
    setBackgroundBrush(MvQTheme::brush("scmview", "viewbg"));
    setAlignment(Qt::AlignCenter);

    scene_ = new QGraphicsScene(this);
    setScene(scene_);
}

void MvQProfileView::setProfile(QList<MvProfileData*> profData, bool editable, int startIndex, int endIndex)
{
    QList<MvQProfileItem*> itemsToBeDeleted;

    foreach (MvQProfileItem* item, prof_) {
        scene_->removeItem(item);
        itemsToBeDeleted << item;
    }
    prof_.clear();
    // profWeight_.clear();

    QSize size = maximumViewportSize();
    size -= QSize(10, 10);

    /// Min max
    bool setMinMax = false;
    float valMin = 999999, valMax = -9999999;
    if (profData.count() > 1) {
        setMinMax = true;
        for (int i = 0; i < profData.count(); i++) {
            if (profData[i]->isRangeSet()) {
                valMin = profData[i]->rangeMin();
                valMax = profData[i]->rangeMax();
                break;
            }
            else {
                float vmin, vmax;
                profData[i]->valueRange(0, profData[i]->count() - 1, vmin, vmax);
                if (vmin < valMin)
                    valMin = vmin;
                if (vmax > valMax)
                    valMax = vmax;
            }
        }
    }

    // Create plot items
    if (profData.count() > 0) {
        prof_ << new MvQProfileItem(this, profData[0], editable, 0, startIndex, endIndex, size,
                                    setMinMax, valMin, valMax);

        for (int i = 1; i < profData.count(); i++) {
            prof_ << new MvQProfileItem(this, profData[i], editable, 0, 0, profData[i]->count() - 1, size,
                                        setMinMax, valMin, valMax);
            prof_.back()->setSubPlot(true);
        }
    }

    adjustSize();

    foreach (MvQProfileItem* item, itemsToBeDeleted) {
        delete item;
    }
}

void MvQProfileView::update(const MvProfileChange& item)
{
    foreach (MvQProfileItem* p, prof_) {
        if (p && p->updateValue(item))
            return;
    }
}

void MvQProfileView::reload()
{
    foreach (MvQProfileItem* item, prof_) {
        if (item)
            item->reload();
    }
}

void MvQProfileView::mouseMoveEvent(QMouseEvent* event)
{
    QGraphicsView::mouseMoveEvent(event);

    QString txt = tr(" Cursor out of plot!");

    foreach (MvQProfileItem* p, prof_)
        if (p->cursorData(mapToScene(event->pos()), txt))
            break;

    emit cursorData(txt);
}

void MvQProfileView::resizeEvent(QResizeEvent* event)
{
    QGraphicsView::resizeEvent(event);
    adjustSize();
}

void MvQProfileView::adjustSize()
{
    QSize size = maximumViewportSize();
    size -= QSize(10, 10);

    // Needs to be improved
    if (prof_.count() > 1) {
        int h = 0;
        for (int i = 1; i < prof_.count(); i++) {
            if (prof_[i]->levelNum() == 1) {
                h += 60 + 0;
            }
            else if (prof_[i]->levelNum() < 6) {
                h += 60 + 60;
            }
            else
                h += 60 + 100;
        }

        QSize firstSize(size.width(), size.height() - h);
        prof_[0]->adjustSize(firstSize);

        QRectF r = prof_[0]->frameRect();
        for (int i = 1; i < prof_.count(); i++) {
            if (prof_[i]->levelNum() == 1) {
                r = QRectF(0, r.bottom() + 60, r.width(), 0);
            }
            else if (prof_[i]->levelNum() < 6) {
                r = QRectF(0, r.bottom() + 60, r.width(), 60);
            }
            else if (prof_[i]->levelNum() < 6) {
                r = QRectF(0, r.bottom() + 60, r.width(), 100);
            }
            prof_[i]->adjustSize(r);
        }
    }
    else if (prof_.count() > 0 && prof_[0]) {
        prof_[0]->adjustSize(size);
    }

    // Set the scenerect
    if (prof_.count() > 0) {
        QRectF bb = prof_[0]->boundingRect();
        for (int i = 1; i < prof_.count(); i++) {
            bb = bb.united(prof_[i]->boundingRect());
        }
        scene_->setSceneRect(bb);
    }
}

void MvQProfileView::adjustValueRange()
{
    if (prof_.count() > 0 && prof_[0]) {
        float valMin = 999999, valMax = -9999999;
        for (int i = 0; i < prof_.count(); i++) {
            float vmin, vmax;
            if (i == 0)
                prof_[i]->valueRange(prof_[i]->firstIndex(), prof_[i]->lastIndex(), vmin, vmax);
            else
                prof_[i]->valueRange(vmin, vmax);

            if (vmin < valMin)
                valMin = vmin;
            if (vmax > valMax)
                valMax = vmax;
        }

        if (prof_.count() == 1) {
            prof_[0]->reload();
        }
        else {
            for (int i = 0; i < prof_.count(); i++) {
                prof_[i]->setValueRange(valMin, valMax);
            }
        }
    }
}

void MvQProfileView::setLevelRange(int startIndex, int endIndex)
{
    if (prof_.count() > 0 && prof_[0]) {
        if (prof_.count() > 1) {
            float valMin = 999999, valMax = -9999999;
            for (int i = 0; i < prof_.count(); i++) {
                float vmin, vmax;
                if (i == 0)
                    prof_[i]->valueRange(startIndex, endIndex, vmin, vmax);
                else
                    prof_[i]->valueRange(vmin, vmax);


                if (vmin < valMin)
                    valMin = vmin;
                if (vmax > valMax)
                    valMax = vmax;
            }

            prof_[0]->setLevelRange(startIndex, endIndex, valMin, valMax);
            for (int i = 1; i < prof_.count(); i++) {
                prof_[i]->setValueRange(valMin, valMax);
            }
        }
        else {
            prof_[0]->setLevelRange(startIndex, endIndex);
        }
    }
}

void MvQProfileView::setEditRadius(int rad)
{
    foreach (MvQProfileItem* item, prof_) {
        if (item)
            item->setEditRadius(rad);
    }
}
