// 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 <QtCharts/QBarCategoryAxis>
#include <QtCharts/QCandlestickSeries>
#include <QtCharts/QCandlestickSet>
#include <QtCharts/QDateTimeAxis>
#include <QtCharts/QHCandlestickModelMapper>
#include <QtCharts/QValueAxis>
#include <QtCore/QDateTime>
#include <QtCore/QDebug>
#include <QtCore/QRandomGenerator>
#include <QtWidgets/QCheckBox>
#include <QtWidgets/QComboBox>
#include <QtWidgets/QDoubleSpinBox>
#include <QtWidgets/QGridLayout>
#include <QtWidgets/QHeaderView>
#include <QtWidgets/QLabel>
#include <QtWidgets/QPushButton>
#include <QtWidgets/QTableView>
#include "customtablemodel.h"
#include "mainwidget.h"

QT_USE_NAMESPACE

MainWidget::MainWidget(QWidget *parent)
    : QWidget(parent),
      m_chart(new QChart()),
      m_chartView(new QChartView(m_chart, this)),
      m_axisX(nullptr),
      m_axisY(nullptr),
      m_maximumColumnWidth(-1.0),
      m_minimumColumnWidth(5.0),
      m_bodyOutlineVisible(true),
      m_capsVisible(false),
      m_bodyWidth(0.5),
      m_capsWidth(0.5),
      m_customIncreasingColor(false),
      m_customDecreasingColor(false),
      m_hModelMapper(new QHCandlestickModelMapper(this))
{
    m_chartView->setRenderHint(QPainter::Antialiasing, false);

    m_hModelMapper->setModel(new CustomTableModel(this));
    m_hModelMapper->setTimestampColumn(0);
    m_hModelMapper->setOpenColumn(1);
    m_hModelMapper->setHighColumn(2);
    m_hModelMapper->setLowColumn(3);
    m_hModelMapper->setCloseColumn(4);

    QGridLayout *mainLayout = new QGridLayout();
    mainLayout->addLayout(createSeriesControlsLayout(), 0, 0);
    mainLayout->addLayout(createSetsControlsLayout(), 1, 0);
    mainLayout->addLayout(createCandlestickControlsLayout(), 2, 0);
    mainLayout->addLayout(createMiscellaneousControlsLayout(), 3, 0);
    mainLayout->addWidget(m_chartView, 0, 1, mainLayout->rowCount() + 1, 1);
    mainLayout->addLayout(createModelMapperControlsLayout(), 0, 2, mainLayout->rowCount(), 1);
    setLayout(mainLayout);

    addSeries();
}

MainWidget::~MainWidget()
{
}

QGridLayout *MainWidget::createSeriesControlsLayout()
{
    QGridLayout *layout = new QGridLayout();
    int row = 0;

    layout->addWidget(new QLabel(QStringLiteral("Series controls:")), row, 0, Qt::AlignLeft);

    QPushButton *addSeriesButton = new QPushButton(QStringLiteral("Add a series"));
    connect(addSeriesButton, SIGNAL(clicked(bool)), this, SLOT(addSeries()));
    layout->addWidget(addSeriesButton, row++, 1, Qt::AlignLeft);

    QPushButton *removeSeriesButton = new QPushButton(QStringLiteral("Remove a series"));
    connect(removeSeriesButton, SIGNAL(clicked(bool)), this, SLOT(removeSeries()));
    layout->addWidget(removeSeriesButton, row++, 1, Qt::AlignLeft);

    QPushButton *removeAllSeriesButton = new QPushButton(QStringLiteral("Remove all series"));
    connect(removeAllSeriesButton, SIGNAL(clicked(bool)), this, SLOT(removeAllSeries()));
    layout->addWidget(removeAllSeriesButton, row++, 1, Qt::AlignLeft);

    return layout;
}

QGridLayout *MainWidget::createSetsControlsLayout()
{
    QGridLayout *layout = new QGridLayout();
    int row = 0;

    layout->addWidget(new QLabel(QStringLiteral("Sets controls:")), row, 0, Qt::AlignLeft);

    QPushButton *addSetButton = new QPushButton(QStringLiteral("Add a set"));
    connect(addSetButton, SIGNAL(clicked(bool)), this, SLOT(addSet()));
    layout->addWidget(addSetButton, row++, 1, Qt::AlignLeft);

    QPushButton *insertSetButton = new QPushButton(QStringLiteral("Insert a set"));
    connect(insertSetButton, SIGNAL(clicked(bool)), this, SLOT(insertSet()));
    layout->addWidget(insertSetButton, row++, 1, Qt::AlignLeft);

    QPushButton *removeSetButton = new QPushButton(QStringLiteral("Remove a set"));
    connect(removeSetButton, SIGNAL(clicked(bool)), this, SLOT(removeSet()));
    layout->addWidget(removeSetButton, row++, 1, Qt::AlignLeft);

    QPushButton *removeAllSetsButton = new QPushButton(QStringLiteral("Remove all sets"));
    connect(removeAllSetsButton, SIGNAL(clicked(bool)), this, SLOT(removeAllSets()));
    layout->addWidget(removeAllSetsButton, row++, 1, Qt::AlignLeft);

    return layout;
}

QGridLayout *MainWidget::createCandlestickControlsLayout()
{
    QGridLayout *layout = new QGridLayout();
    int row = 0;

    layout->addWidget(new QLabel(QStringLiteral("Maximum column width:")), row, 0, Qt::AlignLeft);
    QDoubleSpinBox *maximumColumnWidthSpinBox = new QDoubleSpinBox();
    maximumColumnWidthSpinBox->setRange(-1.0, 1024.0);
    maximumColumnWidthSpinBox->setDecimals(0);
    maximumColumnWidthSpinBox->setValue(m_maximumColumnWidth);
    maximumColumnWidthSpinBox->setSingleStep(1.0);
    connect(maximumColumnWidthSpinBox, SIGNAL(valueChanged(double)),
            this, SLOT(changeMaximumColumnWidth(double)));
    layout->addWidget(maximumColumnWidthSpinBox, row++, 1, Qt::AlignLeft);

    layout->addWidget(new QLabel(QStringLiteral("Minimum column width:")), row, 0, Qt::AlignLeft);
    QDoubleSpinBox *minimumColumnWidthSpinBox = new QDoubleSpinBox();
    minimumColumnWidthSpinBox->setRange(-1.0, 1024.0);
    minimumColumnWidthSpinBox->setDecimals(0);
    minimumColumnWidthSpinBox->setValue(m_minimumColumnWidth);
    minimumColumnWidthSpinBox->setSingleStep(1.0);
    connect(minimumColumnWidthSpinBox, SIGNAL(valueChanged(double)),
            this, SLOT(changeMinimumColumnWidth(double)));
    layout->addWidget(minimumColumnWidthSpinBox, row++, 1, Qt::AlignLeft);

    QCheckBox *bodyOutlineVisible = new QCheckBox(QStringLiteral("Body outline visible"));
    connect(bodyOutlineVisible, SIGNAL(toggled(bool)), this, SLOT(bodyOutlineVisibleToggled(bool)));
    bodyOutlineVisible->setChecked(m_bodyOutlineVisible);
    layout->addWidget(bodyOutlineVisible, row++, 0, Qt::AlignLeft);

    QCheckBox *capsVisible = new QCheckBox(QStringLiteral("Caps visible"));
    connect(capsVisible, SIGNAL(toggled(bool)), this, SLOT(capsVisibleToggled(bool)));
    capsVisible->setChecked(m_capsVisible);
    layout->addWidget(capsVisible, row++, 0, Qt::AlignLeft);

    layout->addWidget(new QLabel(QStringLiteral("Candlestick body width:")), row, 0, Qt::AlignLeft);
    QDoubleSpinBox *bodyWidthSpinBox = new QDoubleSpinBox();
    bodyWidthSpinBox->setRange(-1.0, 2.0);
    bodyWidthSpinBox->setValue(m_bodyWidth);
    bodyWidthSpinBox->setSingleStep(0.1);
    connect(bodyWidthSpinBox, SIGNAL(valueChanged(double)), this, SLOT(changeBodyWidth(double)));
    layout->addWidget(bodyWidthSpinBox, row++, 1, Qt::AlignLeft);

    layout->addWidget(new QLabel(QStringLiteral("Candlestick caps width:")), row, 0, Qt::AlignLeft);
    QDoubleSpinBox *capsWidthSpinBox = new QDoubleSpinBox();
    capsWidthSpinBox->setRange(-1.0, 2.0);
    capsWidthSpinBox->setValue(m_capsWidth);
    capsWidthSpinBox->setSingleStep(0.1);
    connect(capsWidthSpinBox, SIGNAL(valueChanged(double)), this, SLOT(changeCapsWidth(double)));
    layout->addWidget(capsWidthSpinBox, row++, 1, Qt::AlignLeft);

    QCheckBox *increasingColor = new QCheckBox(QStringLiteral("Custom increasing color (only S1)"));
    connect(increasingColor, SIGNAL(toggled(bool)), this, SLOT(customIncreasingColorToggled(bool)));
    increasingColor->setChecked(m_customIncreasingColor);
    layout->addWidget(increasingColor, row++, 0, 1, 2, Qt::AlignLeft);

    QCheckBox *decreasingColor = new QCheckBox(QStringLiteral("Custom decreasing color (only S1)"));
    connect(decreasingColor, SIGNAL(toggled(bool)), this, SLOT(customDecreasingColorToggled(bool)));
    decreasingColor->setChecked(m_customDecreasingColor);
    layout->addWidget(decreasingColor, row++, 0, 1, 2, Qt::AlignLeft);

    return layout;
}

QGridLayout *MainWidget::createMiscellaneousControlsLayout()
{
    QGridLayout *layout = new QGridLayout();
    int row = 0;

    layout->addWidget(new QLabel(QStringLiteral("Miscellaneous:")), row, 0, Qt::AlignLeft);

    QCheckBox *antialiasingCheckBox = new QCheckBox(QStringLiteral("Antialiasing"));
    connect(antialiasingCheckBox, SIGNAL(toggled(bool)), this, SLOT(antialiasingToggled(bool)));
    antialiasingCheckBox->setChecked(false);
    layout->addWidget(antialiasingCheckBox, row++, 1, Qt::AlignLeft);

    QCheckBox *animationCheckBox = new QCheckBox(QStringLiteral("Animation"));
    connect(animationCheckBox, SIGNAL(toggled(bool)), this, SLOT(animationToggled(bool)));
    animationCheckBox->setChecked(false);
    layout->addWidget(animationCheckBox, row++, 1, Qt::AlignLeft);

    QCheckBox *legendCheckBox = new QCheckBox(QStringLiteral("Legend"));
    connect(legendCheckBox, SIGNAL(toggled(bool)), this, SLOT(legendToggled(bool)));
    legendCheckBox->setChecked(true);
    layout->addWidget(legendCheckBox, row++, 1, Qt::AlignLeft);

    QCheckBox *titleCheckBox = new QCheckBox(QStringLiteral("Title"));
    connect(titleCheckBox, SIGNAL(toggled(bool)), this, SLOT(titleToggled(bool)));
    titleCheckBox->setChecked(true);
    layout->addWidget(titleCheckBox, row++, 1, Qt::AlignLeft);

    layout->addWidget(new QLabel(QStringLiteral("Chart theme:")), row, 0, Qt::AlignLeft);
    QComboBox *chartThemeComboBox = new QComboBox();
    chartThemeComboBox->addItem(QStringLiteral("Light"));
    chartThemeComboBox->addItem(QStringLiteral("Blue Cerulean"));
    chartThemeComboBox->addItem(QStringLiteral("Dark"));
    chartThemeComboBox->addItem(QStringLiteral("Brown Sand"));
    chartThemeComboBox->addItem(QStringLiteral("Blue Ncs"));
    chartThemeComboBox->addItem(QStringLiteral("High Contrast"));
    chartThemeComboBox->addItem(QStringLiteral("Blue Icy"));
    chartThemeComboBox->addItem(QStringLiteral("Qt"));
    connect(chartThemeComboBox,SIGNAL(currentIndexChanged(int)),this,SLOT(changeChartTheme(int)));
    layout->addWidget(chartThemeComboBox, row++, 1, Qt::AlignLeft);

    layout->addWidget(new QLabel(QStringLiteral("Axis X:")), row, 0, Qt::AlignLeft);
    QComboBox *axisXComboBox = new QComboBox();
    axisXComboBox->addItem(QStringLiteral("BarCategory"));
    axisXComboBox->addItem(QStringLiteral("DateTime"));
    axisXComboBox->addItem(QStringLiteral("Value"));
    connect(axisXComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(changeAxisX(int)));
    layout->addWidget(axisXComboBox, row++, 1, Qt::AlignLeft);

    return layout;
}

QGridLayout *MainWidget::createModelMapperControlsLayout()
{
    QGridLayout *layout = new QGridLayout();
    int row = 0;

    layout->addWidget(new QLabel(QStringLiteral("First series:")), row, 0, Qt::AlignLeft);

    QPushButton *attachModelMapperButton = new QPushButton(QStringLiteral("Attach model mapper"));
    connect(attachModelMapperButton, SIGNAL(clicked(bool)), this, SLOT(attachModelMapper()));
    layout->addWidget(attachModelMapperButton, row++, 1, Qt::AlignLeft);

    QPushButton *detachModelMappeButton = new QPushButton(QStringLiteral("Detach model mapper"));
    connect(detachModelMappeButton, SIGNAL(clicked(bool)), this, SLOT(detachModelMapper()));
    layout->addWidget(detachModelMappeButton, row++, 1, Qt::AlignLeft);

    QTableView *tableView = new QTableView();
    tableView->setMinimumSize(320, 480);
    tableView->setMaximumSize(320, 480);
    tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
    tableView->verticalHeader()->setSectionResizeMode(QHeaderView::Stretch);
    Q_ASSERT_X(m_hModelMapper->model(), Q_FUNC_INFO, "Model is not initialized");
    tableView->setModel(m_hModelMapper->model());
    layout->addWidget(tableView, row++, 0, 1, 2, Qt::AlignLeft);

    layout->addItem(new QSpacerItem(1, 1, QSizePolicy::Fixed, QSizePolicy::Expanding), row++, 0);

    return layout;
}

qreal MainWidget::randomValue(int min, int max) const
{
    if (min > max)
        qSwap(min, max);
    return QRandomGenerator::global()->bounded(min, max);
}

QCandlestickSet *MainWidget::randomSet(qreal timestamp)
{
    QCandlestickSet *set = new QCandlestickSet(timestamp);
    set->setOpen(randomValue(4, 11));
    set->setHigh(randomValue(12, 15));
    set->setLow(randomValue(0, 3));
    set->setClose(randomValue(4, 11));

    return set;
}

void MainWidget::updateAxes()
{
    if (m_chart->axes().isEmpty())
        m_chart->createDefaultAxes();

    QCandlestickSeries *series;
    if (!m_chart->series().isEmpty())
        series = qobject_cast<QCandlestickSeries *>(m_chart->series().at(0));
    else
        series = nullptr;

    m_axisX = m_chart->axes(Qt::Horizontal).first();
    if (series && !series->sets().isEmpty()) {
        if (m_axisX->type() == QAbstractAxis::AxisTypeBarCategory) {
            QBarCategoryAxis *axisX = qobject_cast<QBarCategoryAxis *>(m_axisX);
            QStringList categories;
            for (int i = 0; i < series->sets().count(); ++i)
                categories.append(QString::number(i));
            axisX->setCategories(categories);
        } else { // QAbstractAxis::AxisTypeDateTime || QAbstractAxis::AxisTypeValue
            qreal msInMonth = 31.0 * 24.0 * 60.0 * 60.0 * 1000.0;
            qreal min = series->sets().first()->timestamp() - msInMonth;
            qreal max = series->sets().last()->timestamp() + msInMonth;
            QDateTime minDateTime = QDateTime::fromMSecsSinceEpoch(min);
            QDateTime maxDateTime = QDateTime::fromMSecsSinceEpoch(max);

            if (m_axisX->type() == QAbstractAxis::AxisTypeDateTime)
                m_axisX->setRange(minDateTime, maxDateTime);
            else
                m_axisX->setRange(min, max);
        }
    }

    m_axisY = m_chart->axes(Qt::Vertical).first();
    m_axisY->setMax(15);
    m_axisY->setMin(0);
}

void MainWidget::addSeries()
{
    if (m_chart->series().count() > 9) {
        qDebug() << "Maximum series count is 10";
        return;
    }

    QCandlestickSeries *series = new QCandlestickSeries();
    series->setName(QStringLiteral("S%1").arg(m_chart->series().count() + 1));
    series->setMaximumColumnWidth(m_maximumColumnWidth);
    series->setMinimumColumnWidth(m_minimumColumnWidth);
    series->setBodyOutlineVisible(m_bodyOutlineVisible);
    series->setBodyWidth(m_bodyWidth);
    series->setCapsVisible(m_capsVisible);
    series->setCapsWidth(m_capsWidth);

    if (m_chart->series().isEmpty()) {
        if (m_customIncreasingColor)
            series->setIncreasingColor(QColor(Qt::green));
        if (m_customDecreasingColor)
            series->setDecreasingColor(QColor(Qt::red));

        for (int month = 1; month <= 12; ++month) {
            QDateTime dateTime;
            dateTime.setDate(QDate(QDateTime::currentDateTime().date().year(), month, 1));
            dateTime.setTime(QTime(12, 34, 56, 789));

            QCandlestickSet *set = randomSet(dateTime.toMSecsSinceEpoch());
            series->append(set);
        }
    } else {
        QCandlestickSeries *s = qobject_cast<QCandlestickSeries *>(m_chart->series().at(0));
        for (int i = 0; i < s->sets().count(); ++i) {
            QCandlestickSet *set = randomSet(s->sets().at(i)->timestamp());
            series->append(set);
        }
    }

    m_chart->addSeries(series);

    updateAxes();
    if (!series->attachedAxes().contains(m_axisX))
        series->attachAxis(m_axisX);
    if (!series->attachedAxes().contains(m_axisY))
        series->attachAxis(m_axisY);
}

void MainWidget::removeSeries()
{
    if (m_chart->series().isEmpty()) {
        qDebug() << "Create a series first";
        return;
    }

    if (m_chart->series().count() == 1)
        detachModelMapper();

    QCandlestickSeries *series = qobject_cast<QCandlestickSeries *>(m_chart->series().last());
    m_chart->removeSeries(series);
    delete series;
    series = nullptr;
}

void MainWidget::removeAllSeries()
{
    if (m_chart->series().isEmpty()) {
        qDebug() << "Create a series first";
        return;
    }

    detachModelMapper();

    m_chart->removeAllSeries();
}

void MainWidget::addSet()
{
    if (m_chart->series().isEmpty()) {
        qDebug() << "Create a series first";
        return;
    }

    foreach (QAbstractSeries *s, m_chart->series()) {
        QCandlestickSeries *series = qobject_cast<QCandlestickSeries *>(s);

        QDateTime dateTime;
        if (series->count()) {
            dateTime.setMSecsSinceEpoch(series->sets().last()->timestamp());
            dateTime = dateTime.addMonths(1);
        } else {
            dateTime.setDate(QDate(QDateTime::currentDateTime().date().year(), 1, 1));
            dateTime.setTime(QTime(12, 34, 56, 789));
        }

        QCandlestickSet *set = randomSet(dateTime.toMSecsSinceEpoch());
        series->append(set);
    }

    updateAxes();
}

void MainWidget::insertSet()
{
    if (m_chart->series().isEmpty()) {
        qDebug() << "Create a series first";
        return;
    }

    foreach (QAbstractSeries *s, m_chart->series()) {
        QCandlestickSeries *series = qobject_cast<QCandlestickSeries *>(s);

        QDateTime dateTime;
        if (series->count()) {
            dateTime.setMSecsSinceEpoch(series->sets().first()->timestamp());
            dateTime = dateTime.addMonths(-1);
        } else {
            dateTime.setDate(QDate(QDateTime::currentDateTime().date().year(), 1, 1));
            dateTime.setTime(QTime(12, 34, 56, 789));
        }

        QCandlestickSet *set = randomSet(dateTime.toMSecsSinceEpoch());
        series->insert(0, set);
    }

    updateAxes();
}

void MainWidget::removeSet()
{
    if (m_chart->series().isEmpty()) {
        qDebug() << "Create a series first";
        return;
    }

    foreach (QAbstractSeries *s, m_chart->series()) {
        QCandlestickSeries *series = qobject_cast<QCandlestickSeries *>(s);
        if (series->sets().isEmpty())
            qDebug() << "Create a set first";
        else
            series->remove(series->sets().last());
    }

    updateAxes();
}

void MainWidget::removeAllSets()
{
    if (m_chart->series().isEmpty()) {
        qDebug() << "Create a series first";
        return;
    }

    foreach (QAbstractSeries *s, m_chart->series()) {
        QCandlestickSeries *series = qobject_cast<QCandlestickSeries *>(s);
        if (series->sets().isEmpty())
            qDebug() << "Create a set first";
        else
            series->clear();
    }

    updateAxes();
}

void MainWidget::changeMaximumColumnWidth(double width)
{
    m_maximumColumnWidth = width;
    foreach (QAbstractSeries *s, m_chart->series()) {
        QCandlestickSeries *series = qobject_cast<QCandlestickSeries *>(s);
        series->setMaximumColumnWidth(m_maximumColumnWidth);
    }
}

void MainWidget::changeMinimumColumnWidth(double width)
{
    m_minimumColumnWidth = width;
    foreach (QAbstractSeries *s, m_chart->series()) {
        QCandlestickSeries *series = qobject_cast<QCandlestickSeries *>(s);
        series->setMinimumColumnWidth(m_minimumColumnWidth);
    }
}

void MainWidget::bodyOutlineVisibleToggled(bool visible)
{
    m_bodyOutlineVisible = visible;
    foreach (QAbstractSeries *s, m_chart->series()) {
        QCandlestickSeries *series = qobject_cast<QCandlestickSeries *>(s);
        series->setBodyOutlineVisible(m_bodyOutlineVisible);
    }
}

void MainWidget::capsVisibleToggled(bool visible)
{
    m_capsVisible = visible;
    foreach (QAbstractSeries *s, m_chart->series()) {
        QCandlestickSeries *series = qobject_cast<QCandlestickSeries *>(s);
        series->setCapsVisible(m_capsVisible);
    }
}

void MainWidget::changeBodyWidth(double width)
{
    m_bodyWidth = width;
    foreach (QAbstractSeries *s, m_chart->series()) {
        QCandlestickSeries *series = qobject_cast<QCandlestickSeries *>(s);
        series->setBodyWidth(m_bodyWidth);
    }
}

void MainWidget::changeCapsWidth(double width)
{
    m_capsWidth = width;
    foreach (QAbstractSeries *s, m_chart->series()) {
        QCandlestickSeries *series = qobject_cast<QCandlestickSeries *>(s);
        series->setCapsWidth(m_capsWidth);
    }
}

void MainWidget::customIncreasingColorToggled(bool custom)
{
    m_customIncreasingColor = custom;

    if (m_chart->series().isEmpty())
        return;

    QCandlestickSeries *series = qobject_cast<QCandlestickSeries *>(m_chart->series().at(0));
    if (series) {
        QColor color = m_customIncreasingColor ? QColor(Qt::green) : QColor();
        series->setIncreasingColor(color);
    }
}

void MainWidget::customDecreasingColorToggled(bool custom)
{
    m_customDecreasingColor = custom;

    if (m_chart->series().isEmpty())
        return;

    QCandlestickSeries *series = qobject_cast<QCandlestickSeries *>(m_chart->series().at(0));
    if (series) {
        QColor color = m_customDecreasingColor ? QColor(Qt::red) : QColor();
        series->setDecreasingColor(color);
    }
}

void MainWidget::antialiasingToggled(bool enabled)
{
    m_chartView->setRenderHint(QPainter::Antialiasing, enabled);
}

void MainWidget::animationToggled(bool enabled)
{
    if (enabled)
        m_chart->setAnimationOptions(QChart::SeriesAnimations);
    else
        m_chart->setAnimationOptions(QChart::NoAnimation);
}

void MainWidget::legendToggled(bool visible)
{
    m_chart->legend()->setVisible(visible);
    if (visible)
        m_chart->legend()->setAlignment(Qt::AlignBottom);
}

void MainWidget::titleToggled(bool visible)
{
    if (visible)
        m_chart->setTitle(QStringLiteral("Candlestick Chart"));
    else
        m_chart->setTitle(QString());
}

void MainWidget::changeChartTheme(int themeIndex)
{
    if (themeIndex < QChart::ChartThemeLight || themeIndex > QChart::ChartThemeQt) {
        qDebug() << "Invalid chart theme index:" << themeIndex;
        return;
    }

    m_chart->setTheme((QChart::ChartTheme)(themeIndex));
}

void MainWidget::changeAxisX(int axisXIndex)
{
    if (m_axisX) {
        m_chart->removeAxis(m_axisX);
        delete m_axisX;
    }

    switch (axisXIndex) {
    case 0:
        m_axisX = new QBarCategoryAxis();
        break;
    case 1:
        m_axisX = new QDateTimeAxis();
        break;
    case 2:
        m_axisX = new QValueAxis();
        break;
    default:
        qDebug() << "Invalid axis x index:" << axisXIndex;
        return;
    }

    m_chart->addAxis(m_axisX, Qt::AlignBottom);

    updateAxes();

    foreach (QAbstractSeries *series, m_chart->series())
        series->attachAxis(m_axisX);
}

void MainWidget::attachModelMapper()
{
    if (m_hModelMapper->series()) {
        qDebug() << "Model mapper is already attached";
        return;
    }

    if (m_chart->series().isEmpty())
        addSeries();

    QCandlestickSeries *series = qobject_cast<QCandlestickSeries *>(m_chart->series().at(0));
    Q_ASSERT(series);
    series->setName(QStringLiteral("SWMM")); // Series With Model Mapper

    CustomTableModel *model = qobject_cast<CustomTableModel *>(m_hModelMapper->model());
    foreach (QCandlestickSet *set, series->sets())
        model->addRow(set);

    m_hModelMapper->setFirstSetRow(0);
    m_hModelMapper->setLastSetRow(model->rowCount() - 1);
    m_hModelMapper->setSeries(series);
}

void MainWidget::detachModelMapper()
{
    if (!m_hModelMapper->series())
        return;

    QCandlestickSeries *series = qobject_cast<QCandlestickSeries *>(m_hModelMapper->series());
    Q_ASSERT(series);
    series->setName(QStringLiteral("S1"));

    m_hModelMapper->setSeries(nullptr);
    m_hModelMapper->setFirstSetRow(-1);
    m_hModelMapper->setLastSetRow(-1);

    CustomTableModel *model = qobject_cast<CustomTableModel *>(m_hModelMapper->model());
    model->clearRows();
}
