#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "clworker.h"
#include "debugprinter.h"

#include <QElapsedTimer>
#include <QDir>

/**
 * @brief MainWindow::MainWindow
 * @param parent
 */

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent),ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    // make gui dest

    resolution resolution(1440,896);
    m_dw = new displayWidget(parent,resolution);
    if(m_dw)
    {
        m_dw->setMinimumHeight(10);
        m_dw->setMinimumWidth(10);
        ui->horizontalLayout->insertWidget(0,m_dw);
    }

    // clear one time

    uint32_t *odata = m_dw->getPixels();
    for(uint32_t y=0;y<(uint32_t)resolution.height;y++)
    {
        for(uint32_t x=0;x<(uint32_t)resolution.width;x++)
        {
            odata[x + (y*resolution.width)] = 0xff0000ff;
        }
    }

    // cl compile

    m_clWorker = new clWorker(resolution,true);

    clProgram pong("Pong","Use F/V Keys for left, K/M Keys right paddle");
    pong.add(clKernelConfig("pong",resolution.width,resolution.height));

    clProgram snake("Snake","Use WASD Keys for direction");
    snake.add(clKernelConfig("snake_pass1",resolution.width,resolution.height));
    snake.add(clKernelConfig("snake_pass2",resolution.width,resolution.height));

    m_programs[pong.getName()]=pong;
    m_programs[snake.getName()]=snake;

    // add to selector

    for(QMapIterator<QString, clProgram> it(m_programs);it.hasNext();)
    {
        it.next();
        ui->comboBoxKernel->addItem(it.key());
    }

    // init all kernelConfigs

    QList<clKernelConfig> kernelConfigs;
    for(QMapIterator<QString, clProgram> it(m_programs);it.hasNext();)
    {
        it.next();
        clProgram prg = it.value();
        QList<clKernelConfig> &subProgramms = prg.getAll();
        foreach(const clKernelConfig &subKernelConfig, subProgramms)
        {
            kernelConfigs.append(subKernelConfig);
        }
    }

    uint32_t workBuffer[64] = {0}; // little buffer for work

    if(!m_clWorker->init(workBuffer,64,kernelConfigs))
    {
        delete m_clWorker;
        m_clWorker = NULL;
    }

    // others

    installEventFilter(this);
    connect(ui->comboBoxKernel,SIGNAL(currentIndexChanged(int)),this,SLOT(kernelIndexChanged(int)));

    printProgrammInfo();
    ui->debugTextEdit->setFocus();

    // gfx update timer

    m_timer = new QTimer(this);
    connect(m_timer, SIGNAL(timeout()), this, SLOT(draw()));
    m_timer->start(10); // ~100Hz
}

/**
 * @brief MainWindow::~MainWindow
 */

MainWindow::~MainWindow()
{
    if(m_clWorker)
    {
        m_clWorker->cleanup();
        delete m_clWorker;
    }

    if(m_timer)
    {
        delete m_timer;
    }

    if(m_dw)
    {
        delete m_dw;
    }
    delete ui;
}

/**
 * @brief MainWindow::eventFilter
 * @param obj
 * @param event
 * @return
 */

bool MainWindow::eventFilter(QObject */*obj*/, QEvent *event)
{
 if(event->type() == QEvent::KeyPress)
 {
    QKeyEvent *keyEvent = (QKeyEvent*)event;

    int s1 = ui->horizontalSliderX->value();
    int s2 = ui->horizontalSliderY->value();
    int updateValue = 2;

    switch(keyEvent->key())
    {
        case Qt::Key_F: s1-=updateValue; break;
        case Qt::Key_V: s1+=updateValue; break;
        case Qt::Key_K: s2-=updateValue; break;
        case Qt::Key_M: s2+=updateValue; break;

        case Qt::Key_A:  s1=1;break;
        case Qt::Key_D:  s1=0;break;
        case Qt::Key_W:  s1=3;break;
        case Qt::Key_S:  s1=2;break;
    }

    ui->horizontalSliderX->setValue(s1);
    ui->horizontalSliderY->setValue(s2);

 }
 return false;
}

/**
 * @brief MainWindow::draw
 */

void MainWindow::draw()
{
    unsigned int sliderX = ui->horizontalSliderX->value();
    unsigned int sliderY = ui->horizontalSliderY->value();
    unsigned int sliderZ = ui->horizontalSliderZ->value();
    QString programmName = ui->comboBoxKernel->currentText();

    if(m_clWorker)
    {
        QElapsedTimer timer;
        timer.start();

        if(m_programs.contains(programmName))
        {
            QList<clKernelConfig> &subProgramms = m_programs[programmName].getAll();
            foreach(const clKernelConfig &subKernelConfig, subProgramms)
            {
                m_clWorker->run(sliderX,sliderY,sliderZ,subKernelConfig.getName());
            }
        }

        double eS = timer.nsecsElapsed()/1e+9;

        m_clWorker->copyBack(m_dw->getPixels());

        statusBar()->showMessage( QString("need %1 s (%2 fps) to execute (%3 MPixel/s))").arg(eS,8).arg(1.0/eS).arg((m_dw->getResolution().width*m_dw->getResolution().height)/eS/1e+6));
    }

    // text
    QString text = TextPrinter::getInstance()->popAll();
    if(text.length())
    {         
        ui->debugTextEdit->appendPlainText(text);
    }
    // redraw
    m_dw->repaint();
}

/**
 * @brief MainWindow::kernelIndexChanged
 */

void MainWindow::kernelIndexChanged(int)
{
    printProgrammInfo();

    if(m_clWorker)
    {
        uint32_t workBuffer[64] = {0};
        m_clWorker->writeWorkBuffer(workBuffer,64);
        m_clWorker->clearOutputBuffer();
    }

    ui->debugTextEdit->setFocus();
}

/**
 * @brief MainWindow::printProgrammInfo
 */

void MainWindow::printProgrammInfo()
{
    QString text =  ui->comboBoxKernel->currentText();
    if(m_programs.contains(text))
    {
        QString info = QString("Info from '%1' -> %2").arg(m_programs[text].getName()).arg(m_programs[text].getInfoText());
        TextPrinter::getInstance()->addText(info);
    }
}
