#include "movement_statistics.hh"
#include "functions.hh"
#include "statistical_class.hh"
#include "../../TradeCore/chart_candle.hh"
#include "../../Indicators/Source/sma.hh"
#include "../../TradeCore/exceptions.hh"
#include <fstream>
#include <iostream>

namespace {

void saveHistogram(
    const std::string& histogramFile,
    const std::vector<StatisticalClass>& classes)
{
    std::ofstream stream;
    stream.open(histogramFile);

    for (auto& statClass : classes) {
        stream << "(" << statClass.min() << ", " << statClass.max() << "]; ";
        stream << statClass.min() << "; " << statClass.max();
        stream << "; " << statClass.count() << std::endl;
    }
}

std::vector<StatisticalClass> boundaries(
    const std::vector<StatisticalClass>& classes, int count)
{
    std::vector<StatisticalClass> result;
    int classCount = 0;

    for (auto it = classes.begin(); it != classes.end(); it++) {
        classCount += it->count();

        if (classCount >= count) {
            StatisticalClass statClass(
                -std::numeric_limits<double>::infinity(),
                it->max(), 0, "StrongBearish");

            result.push_back(statClass);
            break;
        }
    }

    classCount = 0;

    unsigned int centerL = classes.size() / 2;
    unsigned int centerR = classes.size() / 2;

    if (classes.size() % 2 == 0) {
        centerL--;
    }
    else {
        classCount += classes[centerL].count();
        centerL--;
        centerR++;
    }

    while (centerL != 0 || centerR != classes.size()) {
        classCount += classes[centerL].count() + classes[centerR].count();

        if (classCount >= count) {
            StatisticalClass statClass(
                classes[centerL].min(),
                classes[centerR].max(), 0, "Steady");

            result.push_back(statClass);
            break;
        }

        centerL--;
        centerR++;
    }

    classCount = 0;

    for (auto it = classes.rbegin(); it != classes.rend(); it++) {
        classCount += it->count();

        if (classCount >= count) {
            StatisticalClass statClass(
                it->min(), std::numeric_limits<double>::infinity(),
                0, "StrongBullish");

            result.push_back(statClass);
            break;
        }
    }

    StatisticalClass weakBearish(
        result[0].max(), result[1].min(), 0, "WeakBearish");

    StatisticalClass weakBullish(
        result[1].max(), result[2].min(), 0, "WeakBullish");

    result.insert(result.begin() + 1, weakBearish);
    result.insert(result.end() - 1, weakBullish);

    for (auto& statClass : classes) {
        for (auto& resultClass : result) {
            if (resultClass.isMember(statClass.center()))
                resultClass += statClass.count();
        }
    }

    return result;
}

} // namespace

void MovementStatistics::histogramFile(const std::string& histogramFileName)
{
    histogramFile_ = histogramFileName;
}

void MovementStatistics::data(std::shared_ptr<std::vector<ChartCandle>> data)
{
    data_ = data;
}

void MovementStatistics::classes(const std::vector<StatisticalClass>& classes)
{
    classes_ = classes;
}

std::vector<StatisticalClass> MovementStatistics::calculate(int count)
{
    if (!data_)
        throw LogicException("No price data given");

    if (classes_.empty())
        throw LogicException("No statistical classes given");

    AI::SMA currentSma(9);
    AI::SMA futureSma(9);

    for (unsigned int i = 0; i < data_->size()-12; i++) {
        currentSma((*data_)[i].close());
        futureSma((*data_)[i+12].close());

        if (!trainingTimeFilter((*data_)[i].dateTime()))
            continue;

        for (auto& statClass : classes_) {
            double diff = futureSma() - currentSma();

            if (statClass.isMember(diff)) {
                statClass++;
                break;
            }
        }
    }

    if (!histogramFile_.empty())
        saveHistogram(histogramFile_, classes_);

    return boundaries(classes_, count);
}
