#include "main_window.hh"
#include "ui_main_window.h"
#include "../TradeCore/functions.hh"
#include "../TradeCore/exceptions.hh"
#include <QMessageBox>
#include <QDebug>

namespace{

constexpr char connectionName[] = "TradeDiary";
constexpr char appDirName[] = ".TradeDiary";
constexpr char settingsName[] = "database.xml";
constexpr char dateTimeFormat[] = "dd.MM.yyyy HH:mm";

} //namespace

MainWindow::MainWindow(QWidget* parent) :
    QMainWindow(parent), ui_(new Ui::MainWindow),
    settings_(this), db_(connectionName), scene_(this)
{
    ui_->setupUi(this);
    ui_->orderView->setScene(&scene_);

    connect(
        ui_->actionSettings, &QAction::triggered,
        this, &MainWindow::onSettingsDialog);

    connect(
        ui_->actionConnect, &QAction::triggered,
        this, &MainWindow::connectDatabase);

    connect(
        ui_->actionDisconnect, &QAction::triggered,
        this, &MainWindow::disconnectDatabase);

    connect(
        ui_->actionExit, &QAction::triggered,
        this, &MainWindow::close);

    connect(
        ui_->searchButton, &QAbstractButton::pressed,
        this, &MainWindow::onSearchPressed);

    connect(
        ui_->orderTable, &QTableWidget::currentCellChanged,
        this, &MainWindow::onOrderSelected);

    connect(
        ui_->actionZoomIn, &QAction::triggered,
        this, &MainWindow::onZoomIn);

    connect(
        ui_->actionZoomOut, &QAction::triggered,
        this, &MainWindow::onZoomOut);

    connect(
        ui_->actionResetZoom, &QAction::triggered,
        this, &MainWindow::onResetZoom);

    connect(
        ui_->actionShowLine, &QAction::toggled,
        this, &MainWindow::onShowLine);

    connect(
        &scene_, &ChartScene::candleChanged,
        this, &MainWindow::onCandleChanged);

    try {
        createApplicationDirectory(appDirName);
    } catch (StorageException& ex) {
        QMessageBox::critical(
            this, tr("Can not create application directory"), ex.what());
        exit(EXIT_FAILURE);
    }

    settings_.setSettingsFile(absoluteFileName(settingsName, appDirName));

    connectDatabase();
    fillSearchCombos();
}

MainWindow::~MainWindow()
{
    delete ui_;
}

void MainWindow::connectDatabase()
{
    bool result = db_.connect(
        settings_.hostname(), settings_.database(),
        settings_.username(), settings_.password());

    if (!result) {
        QMessageBox::warning(
            this, tr("Database connection"), tr("Can't connect to database ")
            + settings_.database() + tr(" (") + db_.lastError() + tr(")"));
        ui_->actionConnect->setEnabled(true);
        ui_->actionDisconnect->setEnabled(false);
    } else {
        ui_->actionConnect->setEnabled(false);
        ui_->actionDisconnect->setEnabled(true);
    }
}

void MainWindow::disconnectDatabase()
{
    db_.disconnect();
    ui_->actionConnect->setEnabled(true);
    ui_->actionDisconnect->setEnabled(false);
}

void MainWindow::onSettingsDialog()
{
    settings_.show();
}

void MainWindow::fillSearchCombos()
{
    ui_->strategyBox->addItems(db_.strategyList());

    ui_->currencyBox->addItem(tr("All"), 0);
    auto currencies = db_.enumeration("Currency");
    for (auto it = currencies.cbegin(); it != currencies.cend(); it++)
        ui_->currencyBox->addItem(it.value(), it.key());

    ui_->timeFrameBox->addItem(tr("All"), 0);
    auto timeFrames = db_.enumeration("TimeFrame");
    for (auto it = timeFrames.cbegin(); it != timeFrames.cend(); it++)
        ui_->timeFrameBox->addItem(it.value(), it.key());
}

void MainWindow::onSearchPressed()
{
    QString strategy =
        (ui_->strategyBox->currentIndex() == -1
        || ui_->strategyBox->currentIndex() == 0)
        ? QString() : ui_->strategyBox->currentText();

    auto trades = db_.searchOrder(
        strategy,
        ui_->currencyBox->currentData().toInt(),
        ui_->timeFrameBox->currentData().toInt(),
        ui_->fromTimeEdit->dateTime(),
        ui_->toTimeEdit->dateTime());

    if (db_.hasError())
        QMessageBox::warning(this, tr("Search trade error"), db_.lastError());

    ui_->orderTable->displayOrders(
        trades, db_.enumeration("Currency"), db_.enumeration("TimeFrame"));

    onOrderSelected(0);
}

void MainWindow::onOrderSelected(int row, int, int, int)
{
    if (ui_->orderTable->rowCount() == 0)
        return;

    auto events = db_.eventList(
        ui_->orderTable->item(row, 0)->data(Qt::UserRole).toInt());

    if (db_.hasError())
        QMessageBox::warning(this, tr("List events error"), db_.lastError());

    int digits = ui_->orderTable->item(row, 1)->data(Qt::UserRole).toInt();

    auto eventTypes = db_.enumeration("EventType");
    ui_->eventTable->displayEvents(events, eventTypes, digits);

    auto candles = db_.priceData(
        ui_->orderTable->item(row, 0)->data(Qt::UserRole).toInt());

    if (db_.hasError())
        QMessageBox::warning(this, tr("List price error"), db_.lastError());

    scene_.priceData(candles);
    scene_.orderEvents(events, eventTypes);

    scene_.description(
        ui_->orderTable->item(row, 4)->text() + ", " +
        ui_->orderTable->item(row, 5)->text());

    scene_.drawScene();
}

void MainWindow::onZoomIn()
{
    scene_.zoomFactor(scene_.zoomFactor() + 1);
    ui_->statusBar->showMessage(
        tr("Zoom factor: ") + QString::number(scene_.zoomFactor()));
}

void MainWindow::onZoomOut()
{
    scene_.zoomFactor(scene_.zoomFactor() - 1);
    ui_->statusBar->showMessage(
        tr("Zoom factor: ") + QString::number(scene_.zoomFactor()));
}

void MainWindow::onResetZoom()
{
    scene_.zoomFactor(defaultZoomFactor);
    ui_->statusBar->showMessage(
        tr("Zoom factor: ") + QString::number(scene_.zoomFactor()));
}

void MainWindow::onCandleChanged(const ChartCandle& candle)
{
    ui_->statusBar->showMessage(
        tr("Time: ") + candle.dateTime().toString(dateTimeFormat) + " | " +
        tr("Open: ") + QString::number(candle.open()) + " | " +
        tr("High: ") + QString::number(candle.high()) + " | " +
        tr("Low: ") + QString::number(candle.low()) + " | " +
        tr("Close: ") + QString::number(candle.close()) + " | " +
        tr("Volume: ") + QString::number(candle.volume()));
}

void MainWindow::onShowLine(bool checked)
{
    scene_.lineVisible(checked);
}
