// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only

#include <private/boxwhiskers_p.h>
#include <QtGui/QPainter>
#include <QtWidgets/QWidget>
#include <QtWidgets/QGraphicsSceneMouseEvent>

QT_BEGIN_NAMESPACE

BoxWhiskers::BoxWhiskers(QBoxSet *set, AbstractDomain *domain, QGraphicsObject *parent) :
    QGraphicsObject(parent),
    m_boxSet(set),
    m_domain(domain),
    m_mousePressed(false)
{
    setAcceptHoverEvents(true);
    setAcceptedMouseButtons(Qt::MouseButtonMask);
    setFlag(QGraphicsObject::ItemIsSelectable);
}

BoxWhiskers::~BoxWhiskers()
{
}

void BoxWhiskers::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    Q_UNUSED(event);
    emit pressed(m_boxSet);
    m_mousePressed = true;
}

void BoxWhiskers::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
{
    Q_UNUSED(event);
    emit hovered(true, m_boxSet);
}

void BoxWhiskers::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
{
    Q_UNUSED(event);
    emit hovered(false, m_boxSet);
}

void BoxWhiskers::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
    Q_UNUSED(event);
    emit released(m_boxSet);
    if (m_mousePressed)
        emit clicked(m_boxSet);
}

void BoxWhiskers::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
{
    Q_UNUSED(event);
    // For Box a press signal needs to be explicitly fired for mouseDoubleClickEvent
    emit pressed(m_boxSet);
    emit doubleClicked(m_boxSet);
}

void BoxWhiskers::setBrush(const QBrush &brush)
{
    m_brush = brush;
    m_outlinePen.setColor(m_brush.color());
    update();
}

void BoxWhiskers::setPen(const QPen &pen)
{
    qreal widthDiff = pen.widthF() - m_pen.widthF();
    m_boundingRect.adjust(-widthDiff, -widthDiff, widthDiff, widthDiff);

    m_pen = pen;
    m_medianPen = pen;
    m_medianPen.setCapStyle(Qt::FlatCap);
    m_outlinePen = pen;
    m_outlinePen.setStyle(Qt::SolidLine);
    m_outlinePen.setColor(m_brush.color());

    update();
}

void BoxWhiskers::setBoxWidth(const qreal width)
{
    m_boxWidth = width;
}

void BoxWhiskers::setLayout(const BoxWhiskersData &data)
{
    m_data = data;

    updateGeometry(m_domain);
    update();
}

QSizeF BoxWhiskers::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
{
    Q_UNUSED(which);
    Q_UNUSED(constraint);

    return QSizeF();
}

void BoxWhiskers::setGeometry(const QRectF &rect)
{
    Q_UNUSED(rect);
}

QRectF BoxWhiskers::boundingRect() const
{
    return m_boundingRect;
}

void BoxWhiskers::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    Q_UNUSED(option);
    Q_UNUSED(widget);

    painter->save();
    painter->setBrush(m_brush);
    painter->setClipRect(parentItem()->boundingRect());
    painter->setPen(m_pen);
    painter->drawPath(m_boxPath);
    if (!m_boxOutlined)
        painter->setPen(m_outlinePen);
    painter->drawRect(m_middleBox);
    painter->setPen(m_medianPen);
    qreal halfLine = m_pen.widthF() / 2.0;
    painter->drawLine(QLineF(m_geometryLeft - halfLine, m_geometryMedian,
                             m_geometryRight + halfLine, m_geometryMedian));
    painter->restore();
}

void BoxWhiskers::updateGeometry(AbstractDomain *domain)
{
    m_domain = domain;

    prepareGeometryChange();

    QPainterPath path;
    m_boxPath = path;
    m_boundingRect = m_boxPath.boundingRect();

    qreal columnWidth = 1.0 / m_data.m_seriesCount;
    qreal left = ((1.0 - m_boxWidth) / 2.0) * columnWidth + columnWidth * m_data.m_seriesIndex + m_data.m_index - 0.5;
    qreal barWidth = m_boxWidth * columnWidth;

    QPointF geometryPoint = m_domain->calculateGeometryPoint(QPointF(left, m_data.m_upperExtreme), m_validData);
    if (!m_validData)
        return;
    m_geometryLeft = geometryPoint.x();
    qreal geometryUpperExtreme = geometryPoint.y();
    geometryPoint = m_domain->calculateGeometryPoint(QPointF(left + barWidth, m_data.m_upperQuartile), m_validData);
    if (!m_validData)
        return;
    m_geometryRight = geometryPoint.x();
    qreal geometryUpperQuartile = geometryPoint.y();
    geometryPoint = m_domain->calculateGeometryPoint(QPointF(left, m_data.m_lowerQuartile), m_validData);
    if (!m_validData)
        return;
    qreal geometryLowerQuartile = geometryPoint.y();
    geometryPoint = m_domain->calculateGeometryPoint(QPointF(left, m_data.m_lowerExtreme), m_validData);
    if (!m_validData)
        return;
    qreal geometryLowerExtreme = geometryPoint.y();
    geometryPoint = m_domain->calculateGeometryPoint(QPointF(left, m_data.m_median), m_validData);
    if (!m_validData)
        return;
    m_geometryMedian = geometryPoint.y();

    // Upper whisker
    path.moveTo(m_geometryLeft, geometryUpperExtreme);
    path.lineTo(m_geometryRight, geometryUpperExtreme);
    path.moveTo((m_geometryLeft + m_geometryRight) / 2.0, geometryUpperExtreme);
    path.lineTo((m_geometryLeft + m_geometryRight) / 2.0, geometryUpperQuartile);

    // Middle Box
    m_middleBox.setCoords(m_geometryLeft, geometryUpperQuartile, m_geometryRight, geometryLowerQuartile);

    // Lower whisker
    path.moveTo(m_geometryLeft, geometryLowerExtreme);
    path.lineTo(m_geometryRight, geometryLowerExtreme);
    path.moveTo((m_geometryLeft + m_geometryRight) / 2.0, geometryLowerQuartile);
    path.lineTo((m_geometryLeft + m_geometryRight) / 2.0, geometryLowerExtreme);

    path.closeSubpath();

    m_boxPath = path;
    m_boundingRect = m_boxPath.boundingRect();

    qreal extra = m_pen.widthF();
    m_boundingRect.adjust(-extra, -extra, extra, extra);
}

QT_END_NAMESPACE

#include "moc_boxwhiskers_p.cpp"
