#pragma once
#include <myapp/persistence/database.h>
#include <myapp/persistence/databasefactory.h>
#include <myapp/logging/logger.h>
#include <myapp/logging/loggerfactory.h>
#include <memory>
#include <string>
#include "iec6205661obis.h"

using namespace myapp;

class readouts
{
private:
    std::shared_ptr<logging::logger> logger_;
    std::shared_ptr<persistence::database> db_;
    std::string device_;

    readouts()
    {
    }

public:
    void initialize(std::string connection_string, std::string device)
    {
        logger_ = logging::logger_factory::create_console_logger();
        db_ = persistence::database_factory::create_redis(connection_string, logger_);
        device_ = std::move(device);
    }

    std::string get_last_value_by_obis(std::string const& obis)
    {
        using namespace std::string_literals;

        try 
        {
            if (obis == iec62056_61_obis::Voltage_L1)
                return db_->last_readout(device_ + ":U1"s);
            if (obis == iec62056_61_obis::Voltage_L2)
                return db_->last_readout(device_ + ":U2"s);
            if (obis == iec62056_61_obis::Voltage_L3)
                return db_->last_readout(device_ + ":U3"s);
            if (obis == iec62056_61_obis::Current_L1)
                return db_->last_readout(device_ + ":I1"s);
            if (obis == iec62056_61_obis::Current_L2)
                return db_->last_readout(device_ + ":I2"s);
            if (obis == iec62056_61_obis::Current_L3)
                return db_->last_readout(device_ + ":I3"s);
            if (obis == iec62056_61_obis::Power_L1)
                return db_->last_readout(device_ + ":P1"s);
            if (obis == iec62056_61_obis::Power_L2)
                return db_->last_readout(device_ + ":P2"s);
            if (obis == iec62056_61_obis::Power_L3)
                return db_->last_readout(device_ + ":P3"s);
            if (obis == iec62056_61_obis::PowerReactive_L1)
                return db_->last_readout(device_ + ":Q1"s);
            if (obis == iec62056_61_obis::PowerReactive_L2)
                return db_->last_readout(device_ + ":Q2"s);
            if (obis == iec62056_61_obis::PowerReactive_L3)
                return db_->last_readout(device_ + ":Q3"s);
            if (obis == iec62056_61_obis::PowerFactor_L1)
                return db_->last_readout(device_ + ":PF1"s);
            if (obis == iec62056_61_obis::PowerFactor_L2)
                return db_->last_readout(device_ + ":PF2"s);
            if (obis == iec62056_61_obis::PowerFactor_L3)
                return db_->last_readout(device_ + ":PF3"s);

            // should be for each phase, here only for the third one, because MEg40 modbus is bugged.
            // also do not know if quadrants are correctly mapped to MEg40 registries.
            if (obis == iec62056_61_obis::PositiveActiveEnergy_Total)
                return db_->last_readout(device_ + ":EPP3"s);
            if (obis == iec62056_61_obis::NegativeActiveEnergy_Total)
                return db_->last_readout(device_ + ":EPM3"s);
            if (obis == iec62056_61_obis::ImportedInductiveReactiveEnergyFirstQuadrant_Total)
                return db_->last_readout(device_ + ":EQPP3"s);
            if (obis == iec62056_61_obis::ImportedCapacitiveReactiveEnergySecondQuadrant_Total)
                return db_->last_readout(device_ + ":EQPM3"s);
            if (obis == iec62056_61_obis::ExportedInductiveReactiveEnergyThirdQuadrant_Total)
                return db_->last_readout(device_ + ":EQMP3"s);
            if (obis == iec62056_61_obis::ExportedCapacitiveReactiveEnergyFourthQuadrant_Total)
                return db_->last_readout(device_ + ":EQMM3"s);
        }
        catch (std::runtime_error const& e)
        {
            logger_->warning("READOUT", "Error while reading '"s + obis + "': "s + e.what());
            return std::string{};
        }

        logger_->warning("READOUT", "Handler not registered for '"s + obis + "'"s);
        return std::string{};
    }

    static std::shared_ptr<readouts> instance()
    {
        static std::weak_ptr<readouts> ref;
        std::shared_ptr<readouts> instance_;

        if (!ref.expired())
        {
            instance_ = ref.lock();
        }
        else
        {
            instance_ = std::shared_ptr<readouts>(new readouts());
            ref = instance_;
        }

        return instance_;
    }
};
