/**
  * mainwindow.cpp
  * Trida hlavniho okna.
  *
  * @author Michala Capkova
  */

#include "mainwindow.h"
#include "modeselctiondialog.h"
#include "region.h"
#include "capital.h"
#include "scene.h"
#include "mapitemtypes.h"
#include "town.h"
#include "mapview.h"
#include "arrowswidget.h"
#include "examwidget.h"

#include <QGraphicsScene>
#include <QGraphicsView>
#include <QPolygonF>
#include <QPointF>
#include <QFile>
#include <QJsonDocument>
#include <QJsonObject>
#include <QString>
#include <QJsonArray>
#include <QAction>
#include <QToolBar>
#include <QDockWidget>
#include <QListWidget>
#include <QStringList>
#include <QList>
#include <QGraphicsPolygonItem>
#include <QPainterPath>
#include <QSlider>
#include <QLabel>
#include <QMessageBox>

#include <set>
#include <iostream>

//cesty k datum
const QString MainWindow::BORDERS = "../podklady/Statni_hranice_generalizace500.json";
const QString MainWindow::REGIONS = "../podklady/Krajske_hranice_generalizace500.json";
const QString MainWindow::CAPITALS = "../podklady/Krajska_mesta_generalizace500.json";
const QString MainWindow::TOWNS = "../podklady/obce.json";

/**
  * konstruktor
  */
MainWindow::MainWindow(QWidget *parent): QMainWindow(parent)
{
    this->scene = new Scene(this);
    this->view = new MapView(scene, this);
    this->setCentralWidget(view);
    this->setWindowTitle(trUtf8("Zeměpis ČR"));

    QToolBar *mainToolbar = addToolBar("main toolbar");
    this->modeSelectionDialog = new ModeSelectionDialog();

    this->list = new QListWidget();

    // nastaveni dock widgetu se seznamem objektu pro vyukovy mod
    this->listDockWidget = new QDockWidget(tr("List Dock Widget"), this);
    listDockWidget->setAllowedAreas(Qt::RightDockWidgetArea);
    listDockWidget->setWidget(this->list);
    listDockWidget->setFeatures(QDockWidget::NoDockWidgetFeatures);
    listDockWidget->setTitleBarWidget(new QWidget(this));
    addDockWidget(Qt::RightDockWidgetArea, listDockWidget);
    listDockWidget->hide();

    // nastaveni dock widgetu se sipkami pro vyukovy mod
    this->arrowsDockWidget = new QDockWidget(tr("Arrows Dock Widget"), this);
    arrowsDockWidget->setAllowedAreas(Qt::RightDockWidgetArea);
    ArrowsWidget *arrowsWidget = new ArrowsWidget(this);
    arrowsDockWidget->setWidget(arrowsWidget);
    arrowsDockWidget->setFeatures(QDockWidget::NoDockWidgetFeatures);
    arrowsDockWidget->setTitleBarWidget(new QWidget(this));
    addDockWidget(Qt::RightDockWidgetArea, arrowsDockWidget);
    arrowsDockWidget->hide();
    connect(arrowsWidget, SIGNAL(leftPressed()), this, SLOT(setPrevious()));
    connect(arrowsWidget, SIGNAL(rightPressed()), this, SLOT(setNext()));

    //nastaveni dock widgetu pro testovaci mod
    this->examWidget = new ExamWidget();
    this->examDockWidget = new QDockWidget(tr("Exam Dock Widget"), this);
    examDockWidget->setAllowedAreas(Qt::RightDockWidgetArea);
    examDockWidget->setWidget(this->examWidget);
    examDockWidget->setFeatures(QDockWidget::NoDockWidgetFeatures);
    examDockWidget->setTitleBarWidget(new QWidget(this));
    addDockWidget(Qt::RightDockWidgetArea, examDockWidget);
    examDockWidget->setMinimumWidth(MIN_WIDTH);
    examDockWidget->setMaximumWidth(MAX_WIDTH);
    examDockWidget->hide();
    connect(examWidget, SIGNAL(nextQuestionPressed()), this->view, SLOT(enable()));
    connect(examWidget, SIGNAL(endExamPressed()), this, SLOT(endTest()));
    connect(examWidget, SIGNAL(correctAnswerPressed()), this, SLOT(showCorrectAnswer()));

    //akce
    this->modeSelection = mainToolbar->addAction(trUtf8("Nastavení"));
    connect(modeSelection, SIGNAL(triggered()), this, SLOT(selectMode()));
    connect(scene, SIGNAL(selectionChanged()), this, SLOT(itemWasSelected()));
    connect(list, SIGNAL(currentTextChanged(QString)), this, SLOT(textWasSelected(QString)));

    mainToolbar->addSeparator();

    //slider pro zoom
    this->zoomSlider = new QSlider(this);
    zoomSlider->setMaximum(MAX_ZOOM);
    zoomSlider->setMinimum(MIN_ZOOM);
    zoomSlider->setTickInterval(INTERVAL_ZOOM);
    zoomSlider->setOrientation(Qt::Horizontal);
    zoomSlider->setMaximumWidth(WIDTH_SLIDER);
    mainToolbar->addWidget(zoomSlider);
    connect(zoomSlider, SIGNAL(sliderMoved(int)), view, SLOT(setZoom(int)));

    //label pro zoom
    this->zoomLabel = new QLabel("");
    zoomLabel->setStyleSheet("QLabel { background-color : white;}");
    displayZoom(zoomSlider->value());
    connect(zoomSlider, SIGNAL(sliderMoved(int)),this, SLOT(displayZoom(int)));
    mainToolbar->addWidget(zoomLabel);

    mainToolbar->addSeparator();

    //akce zobrazeni informaci o programu
    QAction *aboutProgramAction = mainToolbar->addAction("O programu");
    connect(aboutProgramAction, SIGNAL(triggered()), this, SLOT(aboutProgram()));

    loadBorder();
}

/**
  * nacita a vykresluje statni hranici
  */
void MainWindow::loadBorder()
{
    QFile loadFile(BORDERS);
    if (loadFile.open(QIODevice::ReadOnly))
    {
        this->list->clearSelection();
        this->list->clear();
        this->scene->clearSelection();
        this->scene->clear();

        QByteArray saveData = loadFile.readAll();
        QJsonDocument document(QJsonDocument::fromJson(saveData));
        QJsonArray features = document.object()["features"].toArray();

        //parsovani souboru
        for (int i = 0; i < features.size(); i++)
        {
            QJsonObject polygonObject = features[i].toObject();
            QJsonObject geometry = polygonObject["geometry"].toObject();
            QJsonArray polygons = geometry["coordinates"].toArray();
            QJsonArray coordinates = polygons[0].toArray();
            QPolygonF pol;

            for (int j = 0; j < coordinates.size(); j++)
            {                
                QJsonArray point = coordinates[j].toArray();

                //transformace souradnic vykreslovaneho objektu ze souborovych na scenove
                //(kvuli velikosti absolutni hodnoty souradnic se nedarilo vykreslit objekty na scene)
                double x = point[0].toDouble()/(TRANS);
                double y = point[1].toDouble()/(-TRANS);
                pol.push_back(QPointF(x, y));
            }
            this->scene->addPolygon(pol, QPen(Qt::black, BORDER_WIDTH), QBrush(Qt::NoBrush));
        }
    }
}

/**
  * nacita kraje a vykresluje jejich geometrii
  */
void MainWindow::loadRegionsMap()
{
    QFile loadFile(REGIONS);
    if (loadFile.open(QIODevice::ReadOnly))
    {
        this->list->clearSelection();
        this->list->clear();
        this->scene->clearSelection();
        this->scene->clear();

        this->loadBorder();

        QByteArray saveData = loadFile.readAll();
        QJsonDocument document(QJsonDocument::fromJson(saveData));
        QJsonArray features = document.object()["features"].toArray();

        //parsovani souboru
        for (int i = 0; i < features.size(); i++)
        {
            QJsonObject polygonObject = features[i].toObject();
            QJsonObject geometry = polygonObject["geometry"].toObject();
            QJsonArray polygons = geometry["coordinates"].toArray();
            QJsonObject properties = polygonObject["properties"].toObject();
            QJsonArray coordinates = polygons[0].toArray();
            QPolygonF pol;
            QPainterPath path;
            QString name = properties["Nazev"].toString();

            for (int j = 0; j < coordinates.size(); j++)
            {
                QJsonArray point = coordinates[j].toArray();

                //transformace souradnic vykreslovaneho objektu ze souborovych na scenove
                //(kvuli velikosti absolutni hodnoty souradnic se nedarilo vykreslit objekty na scene)
                double x = point[0].toDouble()/(TRANS);
                double y = point[1].toDouble()/(-TRANS);
                pol.push_back(QPointF(x, y));
            }

            path.addPolygon(pol);

            //v pripade deraveho kraje jako je Stredocesky kraj
            if (polygons.size() == 2)
            {
                QPolygonF praha_je_dira;
                QPainterPath praha_path;
                for (int j = 0; j < polygons[1].toArray().size(); j++)
                {
                    QJsonArray point = polygons[1].toArray()[j].toArray();
                    //transformace souradnic vykreslovaneho objektu ze souborovych na scenove
                    //(kvuli velikosti absolutni hodnoty souradnic se nedarilo vykreslit objekty na scene)
                    double x = point[0].toDouble()/(TRANS);
                    double y = point[1].toDouble()/(-TRANS);
                    praha_je_dira.push_back(QPointF(x, y));
                }
                praha_path.addPolygon(praha_je_dira);
                path = path.subtracted(praha_path);
            }
            this->scene->addItem(new Region(path, name));
        }
    }
}

/**
  * nacita retezcovy seznam kraju
  */
void MainWindow::loadRegionsList()
{
    QFile loadFile(REGIONS);
    if (loadFile.open(QIODevice::ReadOnly))
    {
        QList<QString> stringList;

        this->list->clearSelection();
        this->list->clear();

        QByteArray saveData = loadFile.readAll();
        QJsonDocument document(QJsonDocument::fromJson(saveData));
        QJsonArray features = document.object()["features"].toArray();

        // naplni seznam prazdnymi retezci
        // pri parsovani kraju cteme i jejich poradi v seznamu a diky teto inicializaci muzeme retezec s nazvem rovnou zapsat na pozici podle poradi
        for (int i = 0; i < features.size(); i++)
        {
            stringList << "-";
        }

        //parsovani souboru
        for (int i = 0; i < features.size(); i++)
        {
            QJsonObject polygonObject = features[i].toObject();
            QJsonObject properties = polygonObject["properties"].toObject();
            QString name = properties["Nazev"].toString();
            int rank = (int)properties["Poradi"].toDouble();
            stringList.replace(rank-1, name);

        }

        for (int i = 0; i < stringList.size(); i++)
        {
            this->list->addItem(stringList.at(i));
        }
    }
}

/**
  * nacita krajska mesta (retezce i geometrii)
  */
void MainWindow::loadCapitals()
{
    QFile loadFile(CAPITALS);
    if (loadFile.open(QIODevice::ReadOnly))
    {
        this->list->clearSelection();
        this->list->clear();
        this->scene->clearSelection();
        this->scene->clear();

        this->loadBorder();
        this->loadRegionsMap();

        // nactene statni a krajske hranice zdeaktivujeme, budou tak slouzit jako podklad
        for (int i = 0; i < scene->items().size(); i++)
        {
            QGraphicsItem * item = scene->items()[i];
            if(item->data(1).toInt() == REGION)
            {
                item->setEnabled(false);
            }
        }

        QByteArray saveData = loadFile.readAll();
        QJsonDocument document(QJsonDocument::fromJson(saveData));
        QJsonArray features = document.object()["features"].toArray();
        QList<QString> stringList;

        // naplni seznam prazdnymi retezci
        // pri parsovani krajskych mest cteme i jejich poradi v seznamu a diky teto inicializaci muzeme retezec s nazvem rovnou zapsat na pozici podle poradi
        for (int i = 0; i < features.size(); i++)
        {
            stringList << "-";
        }

        //parsovani souboru
        for (int i = 0; i < features.size(); i++)
        {
            QJsonObject polygonObject = features[i].toObject();
            QJsonObject geometry = polygonObject["geometry"].toObject();
            QJsonArray polygons = geometry["coordinates"].toArray();
            QJsonObject properties = polygonObject["properties"].toObject();
            QString name = properties["Nazev"].toString();
            int rank = (int)properties["Poradi"].toDouble();
            stringList.replace(rank-1, name);

            //krajske mesto tvori jeden celek
            if (polygons.size() == 1)
            {
                QJsonArray coordinates = polygons[0].toArray();
                QPolygonF pol;
                QPainterPath path;

                for (int j = 0; j < coordinates.size(); j++)
                {
                    QJsonArray point = coordinates[j].toArray();
                    //transformace souradnic vykreslovaneho objektu ze souborovych na scenove
                    //(kvuli velikosti absolutni hodnoty souradnic se nedarilo vykreslit objekty na scene)
                    double x = point[0].toDouble()/(TRANS);
                    double y = point[1].toDouble()/(-TRANS);
                    pol.push_back(QPointF(x, y));
                }
                path.addPolygon(pol);
                this->scene->addItem(new Capital(path, name));
            }
            //krajske mesto tvori vice polygonu
            else
            {
                QPainterPath path;

                for(int j = 0; j <polygons.size(); j++)
                {
                    QJsonArray coordinates = polygons[j].toArray()[0].toArray();
                    QPolygonF pol;

                    for (int k = 0; k < coordinates.size(); k++)
                    {
                        QJsonArray point = coordinates[k].toArray();
                        //transformace souradnic vykreslovaneho objektu ze souborovych na scenove
                        //(kvuli velikosti absolutni hodnoty souradnic se nedarilo vykreslit objekty na scene)
                        double x = point[0].toDouble()/(TRANS);
                        double y = point[1].toDouble()/(-TRANS);
                        pol.push_back(QPointF(x, y));
                    }
                    //this->scene->addPolygon(pol, QPen(Qt::black, 2), QBrush(Qt::NoBrush));
                    path.addPolygon(pol);
                }
                this->scene->addItem(new Capital(path, name));
            }
        }

        for (int i = 0; i < stringList.size(); i++)
        {
            this->list->addItem(stringList.at(i));
        }
    }    
}

/**
  * nacita mesta (retezce i geometrii)
  */
void MainWindow::loadTowns()
{
    QFile loadFile(TOWNS);
    if (loadFile.open(QIODevice::ReadOnly))
    {
        QList<QString> stringList;

        this->list->clearSelection();
        this->list->clear();
        this->scene->clearSelection();
        this->scene->clear();

        this->loadBorder();
        this->loadRegionsMap();

        // nactene statni a krajske hranice zdeaktivujeme, budou tak slouzit jako podklad
        for (int i = 0; i < scene->items().size(); i++)
        {
            QGraphicsItem * item = scene->items()[i];
            if(item->data(1).toInt() == REGION)
            {
                item->setEnabled(false);
            }
        }

        QByteArray saveData = loadFile.readAll();
        QJsonDocument document(QJsonDocument::fromJson(saveData));
        QJsonArray features = document.object()["features"].toArray();

        // naplni seznam prazdnymi retezci
        // pri parsovani mest cteme i jejich poradi v seznamu a diky teto inicializaci muzeme retezec s nazvem rovnou zapsat na pozici podle poradi
        for (int i = 0; i < features.size(); i++)
        {
            stringList << "-";
        }

        //parsovani souboru
        for (int i = 0; i < features.size(); i++)
        {
            QJsonObject pointObject = features[i].toObject();
            QJsonObject geometry = pointObject["geometry"].toObject();
            QJsonArray coordinates = geometry["coordinates"].toArray();

            //transformace souradnic vykreslovaneho objektu ze souborovych na scenove
            //(kvuli velikosti absolutni hodnoty souradnic se nedarilo vykreslit objekty na scene)
            double x = coordinates[0].toDouble()/(TRANS);
            double y = coordinates[1].toDouble()/(-TRANS);
            QJsonObject properties = pointObject["properties"].toObject();

            QString name = properties["Nazev"].toString();
            Town *town = new Town(name, properties["VYMERA"].toDouble());
            town->setPos(x, y);
            this->scene->addItem(town);
            int rank = (int)properties["Poradi"].toDouble();
            stringList.replace(rank-1, name);
        }

        for (int i = 0; i < stringList.size(); i++)
        {
            this->list->addItem(stringList.at(i));
        }
    }
    std::cout << this->scene->items().size() << std::endl;
}

/**
  * vybira mezi testovacim/vyukovym modem a mezi krajskymi/obycejnymi mesty a kraji
  */
void MainWindow::selectMode()
{
    int result = this->modeSelectionDialog->exec();
    if (result == QDialog::Accepted)
    {

        if (this->modeSelectionDialog->getSelectedMap() == this->modeSelectionDialog->REGIONS)
        {
            this->loadRegionsMap();
            this->loadRegionsList();
        }
        else if (this->modeSelectionDialog->getSelectedMap() == this->modeSelectionDialog->CAPITALS)
        {
            this->loadCapitals();
        }
        else if (this->modeSelectionDialog->getSelectedMap() == this->modeSelectionDialog->TOWNS)
        {
            this->loadTowns();
        }

        if (this->modeSelectionDialog->getSelectedMode() == this->modeSelectionDialog->TEACHING)
        {
            this->listDockWidget->show();
            this->arrowsDockWidget->show();
            this->examDockWidget->hide();
        }
        else if (this->modeSelectionDialog->getSelectedMode() == this->modeSelectionDialog->TESTING)
        {
            this->listDockWidget->hide();
            this->arrowsDockWidget->hide();
            this->examDockWidget->show();
            this->modeSelection->setDisabled(true);

            QList <QString> testSet;

            for (int i = 0; i < list->count()-1; i++)
            {
                testSet << list->item(i)->text();
            }
            examWidget->setExamSet(testSet);
            examWidget->showQuestionLayout();
        }
    }
}

/**
  * reaguje na vyber objektu v mape; ve vyukovem rezimu vybere objekt v seznamu objektu, v testovacim vyhodnoti odpoved
  */
void MainWindow::itemWasSelected()
{
    if ((this->scene->selectedItems().size() > 0)&&(this->list->count() > 0))
    {
        if (this->modeSelectionDialog->getSelectedMode() == this->modeSelectionDialog->TEACHING)
        {
            QString name = this->scene->selectedItems()[0]->data(0).toString();
            QListWidgetItem *item = this->list->findItems(name, Qt::MatchExactly)[0];
            this->list->setCurrentItem(item);
        }
        else if (this->modeSelectionDialog->getSelectedMode() == this->modeSelectionDialog->TESTING)
        {
            QString name = this->scene->selectedItems()[0]->data(0).toString();

            this->examWidget->checkAnswer(name);
            view->setEnabled(false);
            //scene->clearSelection();
        }



        //std::cout << "item was seleted" << this->scene->selectedItems()[0]->data(0).toString().toStdString() << std::endl;
    }


}

/**
  * reaguje na vyber objektu v seznamu tak, ze ho vybere i v mape
  */
void MainWindow::textWasSelected(QString name)
{
    for(int i = 0; i < this->scene->items().size(); i++)
    {
        QString name_in_map = this->scene->items()[i]->data(0).toString();
        if(name.compare(name_in_map) == 0)
        {
            this->scene->items()[i]->setZValue(1);
        }
        else
        {
            this->scene->items()[i]->setZValue(0);
        }
    }
    this->scene->clearSelection();
    this->scene->items()[0]->setSelected(true);
}

/**
  * vypise aktualni uroven priblizeni
  */
void MainWindow::displayZoom(int value)
{
    this->zoomLabel->setText(" "+QString::number(value));
}

/**
  * vybere predchozi objekt v seznamu objektu
  */
void MainWindow::setPrevious()
{
    int currentRow = list->currentRow();
    //std::cout << list->currentRow() << std::endl;
    if(currentRow != -1)
    {
        // v pripade ze je vybrany prvni objekt, preskocime na konec seznamu
        if(currentRow == 0)
        {
            list->setCurrentRow(list->count() - 1);
        }
        else
        {
            list->setCurrentRow(currentRow - 1);
        }
    }
}

/**
  * vybere nasledujici objekt v seznamu objektu
  */
void MainWindow::setNext()
{
    int currentRow = list->currentRow();
    //std::cout << list->currentRow() << std::endl;
    if(currentRow != -1)
    {
        // v pripade ze je vybrany posledni objekt, preskocime na zacatek seznamu
        if(currentRow == list->count()-1)
        {
            list->setCurrentRow(0);
        }
        else
        {
            list->setCurrentRow(currentRow + 1);
        }
    }
}

/**
  * ukonci probihajici test
  */
void MainWindow::endTest()
{
    this->modeSelection->setEnabled(true);
    this->examDockWidget->hide();
    this->scene->clear();
    this->loadBorder();
    this->view->enable();

}

/**
  * zobrazi na mape spravnou odpoved
  */
void MainWindow::showCorrectAnswer()
{
    //std::cout << examWidget->getExamItem().toStdString() << std::endl;
    for (int i = 0; i < scene->items().size(); i++)
    {
        QGraphicsItem *item = scene->items().at(i);
        QString name = item->data(0).toString();
        if(name.compare(examWidget->getExamItem()) == 0)
        {
            scene->blockSignals(true);
            scene->clearSelection();
            item->setSelected(true);
            scene->blockSignals(false);
            item->setZValue(1);
        }
        else
        {
            item->setZValue(0);
        }
    }
}

/**
  * zobrazi dialog se zakladnim infem o programu
  */
void MainWindow::aboutProgram()
{
    QMessageBox::about(this, trUtf8("Zemepis ČR"), trUtf8("Aplikace Zeměpis České republiky pro Základní školu 1. máje 1 v Karlových Varech\nbyla vytvořena v rámci předmětu Projekt informatika 2 (153PIN2) 2014 na Stavební fakultě ČVUT, autor: Michala Čapková"));
}

MainWindow::~MainWindow()
{

}
