#include "candle_chart_model.hh"
#include "../../TradeCore/exceptions.hh"
#include <QTextStream>
#include <QFile>
#include <QDebug>

QString chartScaleToString(ChartScale scale)
{
    switch(scale){
        case ChartScale::M1: return QString("M1");
        case ChartScale::M5: return QString("M5");
        case ChartScale::M15: return QString("M15");
        case ChartScale::M30: return QString("M30");
        case ChartScale::H1: return QString("H1");
        case ChartScale::H4: return QString("H4");
        case ChartScale::D1: return QString("D1");
        case ChartScale::W1: return QString("W1");
        case ChartScale::MN: return QString("MN");
        case ChartScale::NONE: return QString("NONE");
    }
    return QString("NONE");
}

ChartScale chartScaleFromString(const QString& str)
{
    if (str == "M1") return ChartScale::M1;
    if (str == "M5") return ChartScale::M5;
    if (str == "M15") return ChartScale::M15;
    if (str == "M30") return ChartScale::M30;
    if (str == "H1") return ChartScale::H1;
    if (str == "H4") return ChartScale::H4;
    if (str == "D1") return ChartScale::D1;
    if (str == "W1") return ChartScale::W1;
    if (str == "MN") return ChartScale::MN;
    return ChartScale::NONE;
}

QString currencyPairToString(CurrencyPair pair)
{
    switch(pair){
        case CurrencyPair::NONE: return QString("NONE");
        case CurrencyPair::EURUSD: return QString("EURUSD");
        case CurrencyPair::EURCAD: return QString("EURCAD");
        case CurrencyPair::USDJPY: return QString("USDJPY");
        case CurrencyPair::GPBUSD: return QString("GPBUSD");
        case CurrencyPair::USDCHF: return QString("USDCHF");
        case CurrencyPair::USDCAD: return QString("USDCAD");
        case CurrencyPair::EURJPY: return QString("EURJPY");
        case CurrencyPair::EURGPB: return QString("EURGPB");
        case CurrencyPair::GPBJPY: return QString("GPBJPY");
        case CurrencyPair::GPBNZD: return QString("GPBNZD");
        case CurrencyPair::CHFJPY: return QString("CHFJPY");
        case CurrencyPair::GPBCHF: return QString("GPBCHF");
        case CurrencyPair::AUDJPY: return QString("AUDJPY");
    }
    return QString("NONE");
}

CurrencyPair currencyPairFromString(const QString& str)
{
    if (str == "EURUSD") return CurrencyPair::EURUSD;
    if (str == "EURCAD") return CurrencyPair::EURCAD;
    if (str == "USDJPY") return CurrencyPair::USDJPY;
    if (str == "GPBUSD") return CurrencyPair::GPBUSD;
    if (str == "USDCHF") return CurrencyPair::USDCHF;
    if (str == "USDCAD") return CurrencyPair::USDCAD;
    if (str == "EURJPY") return CurrencyPair::EURJPY;
    if (str == "EURGPB") return CurrencyPair::EURGPB;
    if (str == "GPBJPY") return CurrencyPair::GPBJPY;
    if (str == "GPBNZD") return CurrencyPair::GPBNZD;
    if (str == "CHFJPY") return CurrencyPair::CHFJPY;
    if (str == "GPBCHF") return CurrencyPair::GPBCHF;
    if (str == "AUDJPY") return CurrencyPair::AUDJPY;
    return CurrencyPair::NONE;
}

CandleChartModel::CandleChartModel()
    : scale_(ChartScale::NONE), pair_(CurrencyPair::NONE)
{}

CandleChartModel::CandleChartModel(
    const QList<ChartCandle>& data, ChartScale scale, CurrencyPair pair)
    : scale_(scale), pair_(pair)
{
    for (auto& candle : data)
        insert(candle);
}

CandleChartModel::CandleChartModel(ChartScale scale, CurrencyPair pair)
    : scale_(scale), pair_(pair)
{}

CandleChartModel::CandleChartModel(QString& str)
    : scale_(ChartScale::NONE), pair_(CurrencyPair::NONE)
{
    QTextStream stream(&str, QIODevice::ReadOnly);

    QString firstLine = stream.readLine();
    auto values = firstLine.split(',');

    if (values.length() == 2) {
        scale_ = chartScaleFromString(values[0].trimmed());
        pair_ = currencyPairFromString(values[1].trimmed());
    } else {
        stream.seek(0);
    }

    while (!stream.atEnd()) {
        QString line = stream.readLine();
        ChartCandle candle = ChartCandle::fromString(line);
        if (!candle.isEmpty()) insert(candle);
    }
}

CandleChartIterator CandleChartModel::begin()
{
    return data_.begin();
}

CandleChartIterator CandleChartModel::end()
{
    return data_.end();
}

CandleChartConstIterator CandleChartModel::cbegin() const
{
    return data_.cbegin();
}

CandleChartConstIterator CandleChartModel::cend() const
{
    return data_.cend();
}

ChartCandle& CandleChartModel::first()
{
    return data_.first();
}

ChartCandle& CandleChartModel::last()
{
    return data_.last();
}

ChartCandle& CandleChartModel::operator[](const QDateTime& dateTime)
{
    return data_[dateTime];
}

ChartCandle& CandleChartModel::operator[](int index)
{
    auto iterator = data_.begin();
    iterator += index;
    return iterator.value();
}

int CandleChartModel::count() const
{
    return data_.size();
}

bool CandleChartModel::isEmpty() const
{
    return data_.empty();
}

void CandleChartModel::insert(const ChartCandle& candle)
{
    data_.insert(candle.dateTime(), candle);
}

void CandleChartModel::remove(const QDateTime& dateTime)
{
    data_.remove(dateTime);
}

void CandleChartModel::clear()
{
    data_.clear();
}

ChartScale CandleChartModel::chartScale() const
{
    return scale_;
}

CurrencyPair CandleChartModel::currencyPair() const
{
    return pair_;
}

QString CandleChartModel::toString() const
{
    QString str;
    str += chartScaleToString(scale_) + ", " + currencyPairToString(pair_);
    str += '\n';

    for (auto &candle : data_)
        str += candle.toString() + '\n';

    return str;
}

double CandleChartModel::max() const
{
    if (data_.empty())
        return 0;

    double max = -1E9;

    for (auto &candle : data_) {
        if (candle.high() > max)
            max = candle.high();
    }

    return max;
}

double CandleChartModel::min() const
{
    if (data_.empty())
        return 0;

    double min = 1E9;

    for (auto &candle : data_) {
        if (candle.low() < min)
            min = candle.low();
    }

    return min;
}

CandleChartModelPtr loadChartData(const QString& filename)
{
    QFile file(filename);

    if (!file.open(QIODevice::ReadOnly)) {
        QString errorMessage = QObject::tr("Unable to open file:") + filename;
        throw(FileLoadException(errorMessage.toStdString()));
    }

    QString text = file.readAll();
    CandleChartModelPtr model(new CandleChartModel(text));

    return model;
}

QString toLineString(const CandleChartModel& model)
{
    QString result;

    for (auto it = model.cbegin(); it != model.cend(); ++it)
        result += QString::number((*it).typical()) + ", ";

    if (!result.isEmpty())
        result.chop(2);

    return result;
}

void saveChartData(const QString& filename, const CandleChartModel& chartModel)
{
    QFile file(filename);

    if (!file.open(QIODevice::WriteOnly)) {
        QString errMsg = QObject::tr("Unable to open file:") + filename;
        throw(FileSaveException(errMsg.toStdString()));
    }

    if (file.write(chartModel.toString().toUtf8()) == -1) {
        QString errMsg = QObject::tr("Unable to write file:") + filename;
        throw(FileSaveException(errMsg.toStdString()));
    }
}

void saveChartLine(const QString& filename, const CandleChartModel& chartModel)
{
    QFile file(filename);

    if (!file.open(QIODevice::WriteOnly)) {
        QString errMsg = QObject::tr("Unable to open file:") + filename;
        throw(FileSaveException(errMsg.toStdString()));
    }

    QString chartString = toLineString(chartModel);

    if (file.write(chartString.toUtf8()) == -1) {
        QString errMsg = QObject::tr("Unable to write file:") + filename;
        throw(FileSaveException(errMsg.toStdString()));
    }
}
