#include <QtGui>
#include <QtCore>
#include <QtOpenGL>

/* Results:

N900:

  With Raster:
  -proxywidgets                                         26 ms / frame
  -proxywidgets -no-widget-background                   22 ms / frame
  -proxywidgets -no-widget-background -no-text          13 ms / frame
  -proxywidgets -item-cache                             8.5 ms / frame

  -graphicswidgets                                      13 ms / frame
  -graphicswidgets -no-indexing -optimize-flags         11 ms / frame
  -graphicswidgets -no-text                             5.8 ms / frame
  -graphicswidgets -item-cache                          8.5 ms / frame

  -buttonview                                           10 ms / frame
  -buttonview -no-indexing -optimize-flags              9.3 ms / frame
  -buttonview -no-text                                  3.9 ms / frame
  -buttonview -item-cache                               4.6 ms / frame
  -buttonview -text-layout -no-indexing -optimize-flags 5.5 ms / frame

  With X11:
  -proxywidgets                                         57 ms
  -proxywidgets -no-widget-background                   44 ms
  -proxywidgets -no-widget-background -no-text          27 ms
  -proxywidgets -item-cache                             10 ms

  -graphicswidgets                                      22 ms
  -graphicswidgets -no-indexing -optimzie-flags         20 ms
  -graphicswidgets -no-text                             7.2 ms
  -graphicswidgets -item-cache                          8.9 ms

  -buttonview                                           18 ms
  -buttonview -no-indexing -optimize-flags              18 ms
  -buttonview -no-text                                  4.7 ms
  -buttonview -item-cache                               3.0 ms
  -buttonview -text-layout -no-indexing -optimize-flags 14 ms

  With OpenGL:
  -proxywidgets                                         60 ms
  -proxywidgets -no-widget-background                   47 ms
  -proxywidgets -no-widget-background -no-text          28 ms
  -proxywidgets -item-cache                             9.5 ms

  -graphicswidgets                                      20 ms
  -graphicswidgets -no-indexing -optimize-flags         18 ms
  -graphicswidgets -no-text                             7.8 ms
  -graphicswidgets -item-cache                          9.1 ms

  -buttonview                                           16 ms
  -buttonview -no-indexing -optimize-flags              16 ms
  -buttonview -ordered                                  13 ms
  -buttonview -no-indexing -optimize-flags -ordered     13 ms
  -buttonview -no-text                                  5.2 ms
  -buttonview -no-text -cheat                           4.2 ms
  -buttonview -item-cache                               3.9 ms
  -buttonview -cheat -text-layout -optimize-flags -no-indexing 8.1 ms
  -buttonview -text-layout -optimize-flags -no-indexing 9.1 ms

*/

static QPixmap *theButtonPixmap;
static bool no_text;


class ButtonWidget : public QWidget
{
public:
    ButtonWidget(const QString &text)
    {
        m_text = text;

        if (qApp->arguments().contains("-no-widget-background")) {
            setAttribute(Qt::WA_NoBackground);
        }

    }

    void paintEvent(QPaintEvent *e)
    {
        QPainter p(this);
        p.drawPixmap(0, 0, *theButtonPixmap);
        if (!no_text)
            p.drawText(rect(), Qt::AlignCenter, m_text);
    }

private:
    QString m_text;
};

class ProxyBased : public QGraphicsView
{
public:
    ProxyBased()
    {
        for (int i='0'; i<='Z'; ++i) {
            int x = (i - '0') % 10;
            int y = (i - '0') / 10;
            QGraphicsProxyWidget *proxy = new QGraphicsProxyWidget;
            ButtonWidget *button = new ButtonWidget(QLatin1String("") + QLatin1Char(i));
            button->resize(32, 32);
            proxy->setWidget(button);
            proxy->setGeometry(QRect(x * 32, y * 32, 32, 32));
            m_scene.addItem(proxy);
        }
        setScene(&m_scene);
    }


private:
    QGraphicsScene m_scene;
};




class ButtonItem : public QGraphicsWidget
{
public:
    ButtonItem(const QString &text) : m_text(text) { }
    QRectF boundingRect() const { return QRectF(0, 0, 32, 32); }

    void paint(QPainter *p, const QStyleOptionGraphicsItem *, QWidget *)
    {
        p->drawPixmap(0, 0, *theButtonPixmap);
        if (!no_text)
            p->drawText(boundingRect(), Qt::AlignCenter,  m_text);
    }

private:
    QString m_text;
};

class GraphicsBased : public QGraphicsView
{
public:
    GraphicsBased()
    {
        for (int i='0'; i<='Z'; ++i) {
            int x = (i - '0') % 10;
            int y = (i - '0') / 10;
            ButtonItem *item = new ButtonItem(QLatin1String("") + QLatin1Char(i));
            item->setGeometry(x * 32, y * 32, 32, 32);
            m_scene.addItem(item);
        }
        setScene(&m_scene);
    }

private:
    QGraphicsScene m_scene;
};



class ButtonView : public QGraphicsWidget
{
public:
    ButtonView() {
        QString content;
        for (int i='0'; i<='Z'; ++i) {
            int x = (i - '0') % 10;
            int y = (i - '0') / 10;
            m_texts << (QLatin1String("") + QLatin1Char(i));
            m_rects << QRect(x * 32, y * 32, 32, 32);

            content += QLatin1Char(i);
            content += QChar(QChar::LineSeparator);
        }
        m_cheating = qApp->arguments().contains("-cheat");
        m_ordered = qApp->arguments().contains("-ordered");
        m_many = qApp->arguments().contains("-many");
        m_cached_textlayout = qApp->arguments().contains("-text-layout");

        m_pixmaps.resize(m_rects.size());
        for (int i=0; i<m_rects.size(); ++i) {
            m_pixmaps[i].point = m_rects.at(i).topLeft() + QPointF(16, 16);
            m_pixmaps[i].source = theButtonPixmap->rect();
            m_pixmaps[i].scaleX = 1;
            m_pixmaps[i].scaleY = 1;
            m_pixmaps[i].rotation = 0;
            m_pixmaps[i].opacity = 1;
        }

        m_layout = new QTextLayout(content, font());
        QFontMetricsF fm(font());
        m_layout->beginLayout();
        for (int i=0; i<content.size() / 2; ++i) {
            QTextLine line = m_layout->createLine();
            line.setNumColumns(1);
            int x = (i) % 10;
            int y = (i) / 10;
            QSizeF s = fm.boundingRect(content.at(i*2)).size();
            line.setPosition(QPointF(x * 32, y * 32) + QPointF(16 + s.width() / 2, 16 + s.height() / 2));
        }
        m_layout->endLayout();
        m_layout->setCacheEnabled(true);
    }

    ~ButtonView() {
        delete m_layout;
    }

    QRectF boundingRect() const { return QRectF(0, 0, 32*10, 32*5); }

    void paint(QPainter *p, const QStyleOptionGraphicsItem *, QWidget *)
    {
        int count = m_many ? 100 : 1;
        for (int i=0; i<count; ++i) {

            if (m_ordered) {
                // Draw pixmaps and text interleaved
                for (int i=0; i<m_rects.size(); ++i) {
                    p->drawPixmap(m_rects.at(i), *theButtonPixmap);
                    if (!no_text)
                        p->drawText(m_rects.at(i), Qt::AlignCenter, m_texts.at(i));
                }
            } else {
                if (m_cheating)
                    qDrawPixmaps(p, m_pixmaps.constData(), m_rects.size(), *theButtonPixmap);
                else
                    for (int i=0; i<m_rects.size(); ++i)
                        p->drawPixmap(m_rects.at(i), *theButtonPixmap);

                if (!no_text) {
                    if (m_cached_textlayout)
                        m_layout->draw(p, QPointF(0, 0));
                    else
                        for (int i=0; i<m_rects.size(); ++i)
                          p->drawText(m_rects.at(i), Qt::AlignCenter, m_texts.at(i));
                }
            }
        }
    }

    QList<QRect> m_rects;
    QList<QString> m_texts;

    QVector<QDrawPixmaps::Data> m_pixmaps;

    QTextLayout *m_layout;

    bool m_cheating;
    bool m_many;
    bool m_ordered;
    bool m_cached_textlayout;
};


class SingleItemView : public QGraphicsView
{
public:
    SingleItemView() {
        m_scene.addItem(new ButtonView());
        setScene(&m_scene);
    }

    QGraphicsScene m_scene;
};


class FPSTracker : public QObject
{
public:
    FPSTracker()
    {
        m_time.start();
        m_frames = 0;
    }

    bool eventFilter(QObject *object, QEvent *e) {
        if (e->type() == QEvent::Paint) {
            ++m_frames;
            if (m_time.elapsed() > 2500) {
                printf("Average ms pr frame: %f, %d frames in %d ms\n", m_time.elapsed() / (qreal) m_frames, m_frames, m_time.elapsed());
                m_time.start();
                m_frames = 0;
            }

        }
        return QObject::eventFilter(object, e);

    }

private:
    QTime m_time;
    bool m_started;
    int m_frames;
};



int main(int argc, char **argv)
{
    QApplication app(argc, argv);

    theButtonPixmap = new QPixmap(32, 32);

    if (app.arguments().contains("-opaque-theme"))
        theButtonPixmap->fill(Qt::black);
    else
        theButtonPixmap->fill(Qt::transparent);

    QPainter p(theButtonPixmap);
    p.setRenderHint(QPainter::Antialiasing);
    p.setBrush(QLinearGradient(0, 50, 0, 0));
    p.drawRoundedRect(0.5, 0.5, 31, 31, 6, 6);
    p.end();

    QGraphicsView *view;

    if (app.arguments().contains("-proxywidgets")) {
        view = new ProxyBased();
    } else if (app.arguments().contains("-graphicswidgets")) {
        view = new GraphicsBased();
    } else if (app.arguments().contains("-buttonview")) {
        view = new SingleItemView();
    } else {
        printf("  -proxywidgets           Using a proxy widgets for buttons\n"
               "  -graphicswidgets        Using a items for button\n"
               "  -buttonview             Using one item to draw all buttons\n"
               "\n"
               "  -opengl                 Use an opengl viewport\n"
               "  -optimize-flags         set all optimization flags\n"
               "  -item-cache             Use ItemCoordinateCache\n"
               "  -device-cache           Use DeviceCoordinateCache\n"
               "  -no-text                Don't draw text\n"
               "  -no-widget-background   Don't draw proxywidgets background\n"
               "  -no-indexing            Disable the BSP indexing\n");
        return 0;
    }

    if (app.arguments().contains("-no-indexing"))
        view->scene()->setItemIndexMethod(QGraphicsScene::NoIndex);

    if (app.arguments().contains("-opengl")) {
        view->setViewport(new QGLWidget());
    }

    if (app.arguments().contains("-optimize-flags")) {
        view->setOptimizationFlags(QGraphicsView::DontAdjustForAntialiasing
                                   | QGraphicsView::DontClipPainter
                                   | QGraphicsView::DontSavePainterState);
    }

    if (app.arguments().contains("-item-cache")) {
        foreach (QGraphicsItem *item, view->scene()->items())
            item->setCacheMode(QGraphicsItem::ItemCoordinateCache);

    } else if (app.arguments().contains("-device-cache")) {
        foreach (QGraphicsItem *item, view->scene()->items())
            item->setCacheMode(QGraphicsItem::DeviceCoordinateCache);

    }

    if (app.arguments().contains("-no-text")) {
        no_text = true;
    }

    view->show();

    FPSTracker tracker;
    view->viewport()->installEventFilter(&tracker);

    QTimer timer;
    QObject::connect(&timer, SIGNAL(timeout()), view->viewport(), SLOT(update()));
    timer.start(0);

    int ret = app.exec();
    return ret;
}

