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

#undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses

#include <QtTest/QtTest>
#include <QtCharts/QChartView>
#include <QtCharts/QLegend>
#include <QtCharts/QPieSeries>
#include <QtCharts/QPieLegendMarker>
#include <QtCharts/QAreaSeries>
#include <QtCharts/QAreaLegendMarker>
#include <QtCharts/QLineSeries>
#include <QtCharts/QXYLegendMarker>
#include <QtCharts/QScatterSeries>
#include <QtCharts/QBarSeries>
#include <QtCharts/QBarSet>
#include <QtCharts/QBarLegendMarker>
#include "tst_definitions.h"

QT_USE_NAMESPACE

class tst_QLegend : public QObject
{
    Q_OBJECT

public slots:
    void initTestCase();
    void cleanupTestCase();
    void init();
    void cleanup();

private slots:
    void qlegend();
    void qpieLegendMarker();
    void qareaLegendMarker();
    void qxyLegendMarker();
    void qbarLegendMarker();
    void markers();
    void addAndRemoveSeries();
    void pieMarkerProperties();
    void barMarkerProperties();
    void areaMarkerProperties();
    void xyMarkerPropertiesLine();
    void xyMarkerPropertiesScatter();
    void markerSignals();

private:

    QChart *m_chart;
};

void tst_QLegend::initTestCase()
{
}

void tst_QLegend::cleanupTestCase()
{
    QTest::qWait(1); // Allow final deleteLaters to run
}

void tst_QLegend::init()
{
    m_chart = newQChartOrQPolarChart();
}

void tst_QLegend::cleanup()
{
    delete m_chart;
    m_chart = 0;
}

void tst_QLegend::qlegend()
{
    QVERIFY(m_chart);

    QLegend *legend = m_chart->legend();
    QVERIFY(legend);

    QList<QLegendMarker*> markers = legend->markers();
    QVERIFY(markers.size() == 0);
}

void tst_QLegend::qpieLegendMarker()
{
    SKIP_ON_POLAR();

    QVERIFY(m_chart);
    QLegend *legend = m_chart->legend();

    QPieSeries *series = new QPieSeries();
    QPieSlice *s1 = new QPieSlice(QString("s1"), 111.0);
    series->append(s1);
    m_chart->addSeries(series);

    // Should have one marker
    QList<QLegendMarker*> markers = legend->markers();
    QVERIFY(markers.size() == 1);
    QLegendMarker *m = markers.at(0);

    // Should be piemarker
    QVERIFY(m->type() == QLegendMarker::LegendMarkerTypePie);

    // Related series and slice must match
    QPieLegendMarker *pm = qobject_cast<QPieLegendMarker *> (m);
    QVERIFY(pm);
    QVERIFY(pm->series() == series);
    QVERIFY(pm->slice() == s1);

    // Add another slice
    QPieSlice *s2 = new QPieSlice(QString("s2"), 111.0);
    series->append(s2);

    markers = legend->markers();
    QVERIFY(markers.size() == 2);
    m = markers.at(1);

    QVERIFY(m->type() == QLegendMarker::LegendMarkerTypePie);

    // Related series and slice must match
    pm = qobject_cast<QPieLegendMarker *> (m);
    QVERIFY(pm);
    QVERIFY(pm->series() == series);
    QVERIFY(pm->slice() == s2);
}

void tst_QLegend::qareaLegendMarker()
{
    QVERIFY(m_chart);
    QLegend *legend = m_chart->legend();
    QAreaSeries *series = new QAreaSeries();

    QLineSeries *upper = new QLineSeries(series);
    QLineSeries *lower = new QLineSeries(series);

    upper->append(1,1);
    lower->append(1,0);

    series->setUpperSeries(upper);
    series->setLowerSeries(lower);

    m_chart->addSeries(series);

    // Should have one marker
    QList<QLegendMarker *> markers = legend->markers();
    QVERIFY(markers.size() == 1);
    QLegendMarker *m = markers.at(0);

    QVERIFY(m->series() == series);
    QVERIFY(m->type() == QLegendMarker::LegendMarkerTypeArea);

    QAreaLegendMarker *pm = qobject_cast<QAreaLegendMarker *> (m);
    QVERIFY(pm);
    QVERIFY(pm->series() == series);
}

void tst_QLegend::qxyLegendMarker()
{
    QVERIFY(m_chart);
    QLegend *legend = m_chart->legend();

    QLineSeries *series = new QLineSeries();
    m_chart->addSeries(series);

    // Should have one marker
    QList<QLegendMarker *> markers = legend->markers();
    QVERIFY(markers.size() == 1);
    QLegendMarker *m = markers.at(0);

    QVERIFY(m->series() == series);
    QVERIFY(m->type() == QLegendMarker::LegendMarkerTypeXY);

    QXYLegendMarker *pm = qobject_cast<QXYLegendMarker *> (m);
    QVERIFY(pm);
    QVERIFY(pm->series() == series);
}

void tst_QLegend::qbarLegendMarker()
{
    SKIP_ON_POLAR();

    QVERIFY(m_chart);
    QLegend *legend = m_chart->legend();

    QBarSeries *series = new QBarSeries();
    QBarSet *set0 = new QBarSet(QString("set0"));
    series->append(set0);
    m_chart->addSeries(series);

    // Should have one marker
    QList<QLegendMarker *> markers = legend->markers();
    QVERIFY(markers.size() == 1);
    QLegendMarker *m = markers.at(0);

    QVERIFY(m->series() == series);
    QVERIFY(m->type() == QLegendMarker::LegendMarkerTypeBar);

    QBarLegendMarker *pm = qobject_cast<QBarLegendMarker *> (m);
    QVERIFY(pm);
    QVERIFY(pm->series() == series);
    QVERIFY(pm->barset() == set0);

    // Add another barset
    QBarSet *set1 = new QBarSet(QString("set1"));
    series->append(set1);

    markers = legend->markers();
    QVERIFY(markers.size() == 2);
    m = markers.at(1);

    QVERIFY(m->series() == series);
    QVERIFY(m->type() == QLegendMarker::LegendMarkerTypeBar);

    pm = qobject_cast<QBarLegendMarker *> (m);
    QVERIFY(pm);
    QVERIFY(pm->series() == series);
    QVERIFY(pm->barset() == set1);
}

void tst_QLegend::markers()
{
    SKIP_ON_POLAR();

    QVERIFY(m_chart);
    QLegend *legend = m_chart->legend();

    QPieSeries *pie = new QPieSeries();
    pie->append(QString("slice1"), 1);
    pie->append(QString("slice2"), 2);
    pie->append(QString("slice3"), 3);
    m_chart->addSeries(pie);
    QList<QPieSlice *> slices = pie->slices();

    QBarSeries *bar = new QBarSeries();
    QList<QBarSet *> sets;
    sets.append(new QBarSet(QString("set0")));
    sets.append(new QBarSet(QString("set1")));
    bar->append(sets);
    m_chart->addSeries(bar);

    QLineSeries *line = new QLineSeries();
    line->setName(QString("line1"));
    m_chart->addSeries(line);

    QAreaSeries *area = new QAreaSeries();
    QLineSeries *upper = new QLineSeries(area);
    QLineSeries *lower = new QLineSeries(area);
    upper->append(1,1);
    lower->append(1,0);
    area->setUpperSeries(upper);
    area->setLowerSeries(lower);

    m_chart->addSeries(area);

    QVERIFY(legend->markers(pie).size() == 3);
    QVERIFY(legend->markers(bar).size() == 2);
    QVERIFY(legend->markers(line).size() == 1);
    QVERIFY(legend->markers(area).size() == 1);
    QVERIFY(legend->markers().size() == 3+2+1+1);
}

void tst_QLegend::addAndRemoveSeries()
{
    SKIP_ON_POLAR();

    QVERIFY(m_chart);
    QLegend *legend = m_chart->legend();

    QPieSeries *pie = new QPieSeries();
    pie->append(QString("slice1"), 1);
    pie->append(QString("slice2"), 2);
    pie->append(QString("slice3"), 3);

    m_chart->addSeries(pie);

    QList<QLegendMarker *> markers = legend->markers();
    QVERIFY(markers.size() == 3);

    m_chart->removeSeries(pie);

    markers = legend->markers();
    QVERIFY(markers.size() == 0);

    delete pie;
}

void tst_QLegend::pieMarkerProperties()
{
    SKIP_ON_POLAR();

    QVERIFY(m_chart);
    QLegend *legend = m_chart->legend();

    QPieSeries *pie = new QPieSeries();
    QPieSlice *slice = pie->append(QString("Slice1"), 1);
    m_chart->addSeries(pie);

    QLegendMarker *m = legend->markers(pie).at(0);
    QPieLegendMarker *pm = qobject_cast<QPieLegendMarker *> (m);
    QVERIFY(pm);

    QBrush b1(QColor(12,34,56,78));
    QBrush b2(QColor(34,56,78,90));

    // Change brush
    slice->setBrush(b1);
    QVERIFY(pm->brush() == b1);
    slice->setBrush(b2);
    QVERIFY(pm->brush() == b2);

    // Change label
    QVERIFY(pm->label().compare(QString("Slice1")) == 0);
    slice->setLabel(QString("foo"));
    QVERIFY(pm->label().compare(QString("foo")) == 0);
    slice->setLabel(QString("bar"));
    QVERIFY(pm->label().compare(QString("bar")) == 0);

    // Change visibility
    pie->setVisible(false);
    TRY_COMPARE(pm->isVisible(), false);
    pie->setVisible(true);
    TRY_COMPARE(pm->isVisible(), true);
}

void tst_QLegend::barMarkerProperties()
{
    SKIP_ON_POLAR();

    QVERIFY(m_chart);
    QLegend *legend = m_chart->legend();

    QBarSeries *bar = new QBarSeries();
    QBarSet *barSet = new QBarSet(QString("Set0"));
    *barSet << 1;
    bar->append(barSet);
    m_chart->addSeries(bar);

    QLegendMarker *m = legend->markers(bar).at(0);
    QBarLegendMarker *pm = qobject_cast<QBarLegendMarker *> (m);
    QVERIFY(pm);

    QBrush b1(QColor(12,34,56,78));
    QBrush b2(QColor(34,56,78,90));

    // Change brush
    barSet->setBrush(b1);
    QVERIFY(pm->brush() == b1);
    barSet->setBrush(b2);
    QVERIFY(pm->brush() == b2);

    // Change label
    QVERIFY(pm->label().compare(QString("Set0")) == 0);
    barSet->setLabel(QString("foo"));
    QVERIFY(pm->label().compare(QString("foo")) == 0);
    barSet->setLabel(QString("bar"));
    QVERIFY(pm->label().compare(QString("bar")) == 0);

    // Change visibility
    bar->setVisible(false);
    TRY_COMPARE(pm->isVisible(), false);
    bar->setVisible(true);
    TRY_COMPARE(pm->isVisible(), true);
}

void tst_QLegend::areaMarkerProperties()
{
    QVERIFY(m_chart);
    QLegend *legend = m_chart->legend();

    QAreaSeries *area = new QAreaSeries();
    area->setName(QString("Area"));
    QLineSeries *upper = new QLineSeries(area);
    QLineSeries *lower = new QLineSeries(area);
    upper->append(1,1);
    lower->append(1,0);
    area->setUpperSeries(upper);
    area->setLowerSeries(lower);
    m_chart->addSeries(area);

    QLegendMarker *m = legend->markers(area).at(0);
    QAreaLegendMarker *pm = qobject_cast<QAreaLegendMarker *> (m);
    QVERIFY(pm);

    QBrush b1(QColor(12,34,56,78));
    QBrush b2(QColor(34,56,78,90));

    // Change brush
    area->setBrush(b1);
    QVERIFY(pm->brush() == b1);
    area->setBrush(b2);
    QVERIFY(pm->brush() == b2);

    // Change name
    QVERIFY(pm->label().compare(QString("Area")) == 0);
    area->setName(QString("foo"));
    QVERIFY(pm->label().compare(QString("foo")) == 0);
    area->setName(QString("bar"));
    QVERIFY(pm->label().compare(QString("bar")) == 0);

    // Change visibility
    area->setVisible(false);
    TRY_COMPARE(pm->isVisible(), false);
    area->setVisible(true);
    TRY_COMPARE(pm->isVisible(), true);
}

void tst_QLegend::xyMarkerPropertiesLine()
{
    QVERIFY(m_chart);
    QLegend *legend = m_chart->legend();

    QLineSeries *line = new QLineSeries();
    line->setName(QString("Line"));
    line->append(1,1);
    m_chart->addSeries(line);

    QLegendMarker *m = legend->markers(line).at(0);
    QXYLegendMarker *pm = qobject_cast<QXYLegendMarker *> (m);
    QVERIFY(pm);

    // With line series, the marker is colored after pen, not brush
    QPen b1(QColor(12,34,56,78));
    QPen b2(QColor(34,56,78,90));

    // Change brush
    line->setPen(b1);
    QVERIFY(pm->brush().color() == b1.color());
    line->setPen(b2);
    QVERIFY(pm->brush().color() == b2.color());

    // Change name
    QVERIFY(pm->label().compare(QString("Line")) == 0);
    line->setName(QString("foo"));
    QVERIFY(pm->label().compare(QString("foo")) == 0);
    line->setName(QString("bar"));
    QVERIFY(pm->label().compare(QString("bar")) == 0);

    // Change visibility
    line->setVisible(false);
    TRY_COMPARE(pm->isVisible(), false);
    line->setVisible(true);
    TRY_COMPARE(pm->isVisible(), true);
}

void tst_QLegend::xyMarkerPropertiesScatter()
{
    QVERIFY(m_chart);
    QLegend *legend = m_chart->legend();

    QScatterSeries *scatter = new QScatterSeries();
    scatter->setName(QString("Scatter"));
    scatter->append(1,1);
    m_chart->addSeries(scatter);

    QLegendMarker *m = legend->markers(scatter).at(0);
    QXYLegendMarker *pm = qobject_cast<QXYLegendMarker *> (m);
    QVERIFY(pm);

    QBrush b1(QColor(12,34,56,78));
    QBrush b2(QColor(34,56,78,90));

    // Change brush
    scatter->setBrush(b1);
    QVERIFY(pm->brush() == b1);
    scatter->setBrush(b2);
    QVERIFY(pm->brush() == b2);

    // Change name
    QVERIFY(pm->label().compare(QString("Scatter")) == 0);
    scatter->setName(QString("foo"));
    QVERIFY(pm->label().compare(QString("foo")) == 0);
    scatter->setName(QString("bar"));
    QVERIFY(pm->label().compare(QString("bar")) == 0);

    // Change visibility
    scatter->setVisible(false);
    TRY_COMPARE(pm->isVisible(), false);
    scatter->setVisible(true);
    TRY_COMPARE(pm->isVisible(), true);
}

void tst_QLegend::markerSignals()
{
    SKIP_ON_POLAR();
    SKIP_IF_CANNOT_TEST_MOUSE_EVENTS();
    SKIP_IF_FLAKY_MOUSE_MOVE();

    QChart *chart = newQChartOrQPolarChart();
    QLegend *legend = chart->legend();

    QBarSeries *bar = new QBarSeries();
    QBarSet *set1 = new QBarSet(QString("set 1"));
    *set1 << 10 << 10 << 10;
    bar->append(set1);
    QBarSet *set2 = new QBarSet(QString("set 2"));
    *set2 << 10 << 10 << 10;
    bar->append(set2);
    chart->addSeries(bar);

    QPieSeries *pie = new QPieSeries();
    pie->append(QString("slice1"), 1);
    pie->append(QString("slice2"), 2);
    pie->append(QString("slice3"), 3);
    chart->addSeries(pie);
    legend->setAlignment(Qt::AlignRight);

    QLineSeries *line = new QLineSeries();
    line->setName(QString("Line 1"));
    line->append(1,1);
    chart->addSeries(line);

    QAreaSeries *area = new QAreaSeries();
    area->setName(QString("Area 1"));
    QLineSeries *upper = new QLineSeries(area);
    QLineSeries *lower = new QLineSeries(area);
    upper->append(2,2);
    lower->append(1,1);
    area->setUpperSeries(upper);
    area->setLowerSeries(lower);
    chart->addSeries(area);

    QScatterSeries *scatter = new QScatterSeries();
    scatter->setName(QString("Scatter"));
    scatter->append(3,3);
    chart->addSeries(scatter);

    QList<QSignalSpy *> spies;
    foreach(QLegendMarker *m, legend->markers()) {
        QSignalSpy *spy = new QSignalSpy(m, SIGNAL(hovered(bool)));
        spies.append(spy);
    }

    QChartView view(chart);
    view.resize(400, 400);
    view.show();
    QVERIFY(QTest::qWaitForWindowExposed(&view));

    // Sweep mouse over all legend items
    for (int i = 0; i < 400; i++)
        QTest::mouseMove(view.viewport(), QPoint(333, i));

    foreach (QSignalSpy *spy, spies)
        TRY_COMPARE(spy->size(), 2);

    qDeleteAll(spies);
}

QTEST_MAIN(tst_QLegend)

#include "tst_qlegend.moc"

