/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/

#ifndef WINDOW_H
#define WINDOW_H


#include <QGraphicsScene>
#include <QGraphicsWidget>
#include <QGraphicsLinearLayout>
#include <QGraphicsView>
#include <QSpinBox>
#include <QCheckBox>
#include <QPushButton>
#include <QLabel>
#include <QHBoxLayout>
#include <QPainter>
#include <QApplication>
#include <QThread>
#include <QMap>
#include <QTime>
#include <QDebug>

struct Statistics {
    Statistics() : setGeometryCount(0), sleepMsecs(0), output(0),
        currentBenchmarkIteration(0), relayoutClicked(false)
    {
    }
    QMap<QGraphicsWidget*, int> setGeometryTracker;
    QTime time;
    int setGeometryCount;
    int sleepMsecs;
    QLabel *output;
    void sleep()
    {
#if QT_VERSION >= 0x050000
        QThread::msleep(sleepMsecs);
#else
        qWarning("%s unimplemented", Q_FUNC_INFO);
#endif
    }
    int currentBenchmarkIteration;
    bool relayoutClicked;

};


class Window;

class SlowWidget : public QGraphicsWidget {
public:
    SlowWidget(QGraphicsWidget *w = 0, Qt::WindowFlags wFlags = 0) : QGraphicsWidget(w, wFlags)
    {
        m_window = 0;
    }

    void setStats(Statistics *stats)
    {
        m_stats = stats;
    }

    void setWindow(Window *window)
    {
        m_window = window;
    }

    void setGeometry(const QRectF &rect);

    bool event(QEvent *e)
    {
        if (e->type() == QEvent::LayoutRequest) {
            if (m_stats->sleepMsecs > 0) {
                m_stats->sleep();
                qDebug("sleep %d ms\n", m_stats->sleepMsecs);
            }
        }
        return QGraphicsWidget::event(e);
    }

    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
    {
        Q_UNUSED(option);
        Q_UNUSED(widget);
        painter->setBrush(m_brush);
        painter->drawRoundRect(rect());
        painter->drawLine(rect().topLeft(), rect().bottomRight());
        painter->drawLine(rect().bottomLeft(), rect().topRight());
    }

    void setBrush(const QBrush &brush)
    {
        m_brush = brush;
    }
private:
    QBrush m_brush;
    Statistics *m_stats;
    Window *m_window;
};

class Window : public QWidget {
    Q_OBJECT
public:
    Window() : QWidget()
    {
        QGraphicsView *m_view = new QGraphicsView(&scene);

        m_window = 0;
        m_leaf = 0;

        m_button = new QPushButton(tr("Relayout"));
        m_button->setObjectName("button");

        m_sleepLabel = new QLabel(tr("Sleep:"));
        m_sleepSpinBox = new QSpinBox;
        m_sleepSpinBox->setRange(0, 1000);
        m_sleepSpinBox->setSingleStep(10);

        m_depthLabel = new QLabel(tr("Depth:"));
        m_depthSpinBox = new QSpinBox;
        m_depthSpinBox->setObjectName("depthSpinBox");
        m_depthSpinBox->setRange(1, 200);
        m_depthSpinBox->setSingleStep(5);

        m_benchmarkIterationsLabel = new QLabel(tr("Benchmark iterations"));
        m_benchmarkIterationsSpinBox = new QSpinBox;
        m_benchmarkIterationsSpinBox->setObjectName("benchmarkIterationsSpinBox");
        m_benchmarkIterationsSpinBox->setRange(1, 1000);
        m_benchmarkIterationsSpinBox->setValue(41);
        m_benchmarkIterationsSpinBox->setSingleStep(10);

        m_instantCheckBox = new QCheckBox(tr("Instant propagation"));
        m_instantCheckBox->setObjectName("instantPropagationCheckbox");
        QGraphicsLayout::setInstantInvalidatePropagation(true);
        m_instantCheckBox->setChecked(QGraphicsLayout::instantInvalidatePropagation());

        m_resultLabel = new QLabel(tr("Press relayout to start test"));

        QHBoxLayout *hbox = new QHBoxLayout;
        hbox->addWidget(m_sleepLabel);
        hbox->addWidget(m_sleepSpinBox);
        hbox->addWidget(m_depthLabel);
        hbox->addWidget(m_depthSpinBox);
        hbox->addWidget(m_benchmarkIterationsLabel);
        hbox->addWidget(m_benchmarkIterationsSpinBox);
        hbox->addWidget(m_instantCheckBox);
        hbox->addWidget(m_resultLabel);
        hbox->addStretch();
        hbox->addWidget(m_button);

        QVBoxLayout *vbox = new QVBoxLayout;
        vbox->addWidget(m_view);
        vbox->addLayout(hbox);
        setLayout(vbox);

        metaObject()->connectSlotsByName(this);

        m_depthSpinBox->setValue(20);   // triggers purposedly on_depthSpinBox_valueChanged
    }

private slots:
    void on_depthSpinBox_valueChanged(int value)
    {
        m_stats.relayoutClicked = false;
        if (m_window) {
            QApplication::processEvents();
            delete m_window;
        }
        m_window = new SlowWidget(0, Qt::Window);
        m_window->setStats(&m_stats);
        m_window->setWindow(this);
        QColor col(Qt::black);
        m_window->setBrush(col);
        scene.addItem(m_window);
        m_leaf = 0;
        const int depth = value;
        SlowWidget *parent = m_window;
        for (int i = 1; i < depth; ++i) {
            QGraphicsLinearLayout *l = new QGraphicsLinearLayout(parent);
            l->setContentsMargins(2,2,2,2);
            SlowWidget *child = new SlowWidget;
            QColor col;
            col.setHsl(0, 0, 255*i/(depth - 1));
            child->setBrush(col);
            child->setStats(&m_stats);
            child->setWindow(this);
            l->addItem(child);
            parent = child;
        }
        m_leaf = parent;
    }

    void on_button_clicked(bool /*check = false*/)
    {
        m_stats.relayoutClicked = true;
        if (m_leaf) {
            QSizeF sz = m_leaf->size();
            int w = int(sz.width());
            w^=16;
            sz = QSizeF(w,w);
            m_stats.output = m_resultLabel;
            m_stats.output->setText(QString("wait..."));
            m_stats.setGeometryCount = 0;
            m_stats.setGeometryTracker.clear();
            m_stats.sleepMsecs = m_sleepSpinBox->value();
            m_stats.time.start();
            m_stats.currentBenchmarkIteration = 0;
            m_leaf->setMinimumSize(sz);
            m_leaf->setMaximumSize(sz);
        }
    }

    void on_instantPropagationCheckbox_toggled(bool checked)
    {
        QGraphicsLayout::setInstantInvalidatePropagation(checked);
    }

public slots:
    void doAgain()
    {
        if (m_leaf) {
            QSizeF sz = m_leaf->size();
            int w = int(sz.width());
            w^=16;
            sz = QSizeF(w,w);
            m_leaf->setMinimumSize(sz);
            m_leaf->setMaximumSize(sz);
        }
    }

private:
public:
    QGraphicsScene scene;
    QGraphicsView *m_view;
    QPushButton *m_button;
    QLabel *m_sleepLabel;
    QSpinBox *m_sleepSpinBox;
    QLabel *m_depthLabel;
    QSpinBox *m_depthSpinBox;
    QLabel *m_benchmarkIterationsLabel;
    QSpinBox *m_benchmarkIterationsSpinBox;
    QCheckBox *m_instantCheckBox;
    QLabel *m_resultLabel;
    QGraphicsWidget *m_leaf;
    SlowWidget *m_window;
    Statistics m_stats;


};


#endif //WINDOW_H
