#include <electricity/logger/dsmr.hpp>
#include <sstream>
#include <utility>

namespace DSMR
{
    const std::unordered_map<std::string, LineTag> & Data::GetMap()
    {
        static std::unordered_map<std::string, LineTag> map = {
            { "1-3:0.2.8", LineTag::DSMRversion },
            { "0-0:1.0.0", LineTag::DateTimeStamp },
            { "0-0:96.1.1", LineTag::SerialNo },
            { "1-0:1.8.1", LineTag::TotalPowerConsumedTariff1 },
            { "1-0:1.8.2", LineTag::TotalPowerConsumedTariff2 },
            { "1-0:2.8.1", LineTag::TotalReturnedPowerTariff1 },
            { "1-0:2.8.2", LineTag::TotalReturnedPowerTariff2 },
            { "0-0:96.14.0", LineTag::CurrentTarif }, // 1 == night, 2 == day,
            { "1-0:1.7.0", LineTag::CurrentPowerConsumption },
            { "1-0:2.7.0", LineTag::CurrentPowerReturn },
            { "0-0:96.7.21", LineTag::PowerFailureCount },
            { "0-0:96.7.9", LineTag::PowerFailureLongCount },
            { "1-0:99.97.0", LineTag::PowerFailureEventLog },
            { "1-0:32.32.0", LineTag::VoltageL1SagCount },
            { "1-0:52.32.0", LineTag::VoltageL2SagCount },
            { "1-0:72.32.0", LineTag::VoltageL3SagCount },
            { "1-0:32.36.0", LineTag::VoltageL1SwellCount },
            { "1-0:52.36.0", LineTag::VoltageL2SwellCount },
            { "1-0:72.36.0", LineTag::VoltageL3SwellCount },
            { "0-0:96.13.0", LineTag::TextMessageMaxChar },
            { "1-0:32.7.0", LineTag::InstantL1VoltageResolution },
            { "1-0:52.7.0", LineTag::InstantL2VoltageResolution },
            { "1-0:72.7.0", LineTag::InstantL3VoltageResolution },
            { "1-0:31.7.0", LineTag::InstantL1CurrentResolution },
            { "1-0:51.7.0", LineTag::InstantL2CurrentResolution },
            { "1-0:71.7.0", LineTag::InstantL3CurrentResolution },
            { "1-0:21.7.0", LineTag::InstantL1ActivePowerResolution },
            { "1-0:41.7.0", LineTag::InstantL2ActivePowerResolution },
            { "1-0:61.7.0", LineTag::InstantL3ActivePowerResolution },
            { "1-0:22.7.0", LineTag::InstantL1ActivePowerResolutionA },
            { "1-0:42.7.0", LineTag::InstantL2ActivePowerResolutionA },
            { "1-0:62.7.0", LineTag::InstantL3ActivePowerResolutionA },
            { "0-1:24.1.0", LineTag::DeviceType },
            { "0-1:96.1.0", LineTag::GasDeviceIdentifier },
            { "0-1:24.2.1", LineTag::GasTotalConsumptionLog },
        };

        return map;
    }

    void Data::RemoveUnit(std::string & value)
    {
        const auto fresult = value.find('*');
        if(fresult == std::string::npos)
        {
            return;
        }
        value.resize(fresult);
    }

    std::pair<std::string, std::string> Data::GetKeyValuePair(const std::string & line)
    {
        const auto bracketOpen = line.find('(');
        if(bracketOpen == std::string::npos)
        {
            return std::make_pair("", "");
        }
        const auto bracketClose = line.find(')', bracketOpen);
        if(bracketClose == std::string::npos)
        {
            return std::make_pair(line.substr(0, bracketOpen), "");
        }

        return std::make_pair(
            line.substr(0, bracketOpen),
            line.substr(bracketOpen + 1, bracketClose - (bracketOpen + 1)));
    }

    void Data::ParseLine(const std::string & line)
    {
        const auto & map = GetMap();

        auto pair = GetKeyValuePair(line);
        auto & key = pair.first;
        auto & value = pair.second;

        const auto fresult = map.find(key);
        if(fresult == map.end())
        {
            return;
        }

        RemoveUnit(value);
        std::stringstream ss;
        ss.str(value);

        switch(fresult->second)
        {
            case LineTag::CurrentPowerConsumption:
                ss >> currentPowerUsageKw;
                break;

            case LineTag::CurrentPowerReturn:
                ss >> currentPowerReturnKw;
                break;

            case LineTag::TotalPowerConsumedTariff2:
                ss >> totalPowerConsumptionDayKwh;
                break;

            case LineTag::TotalPowerConsumedTariff1:
                ss >> totalPowerConsumptionNightKwh;
                break;

            case LineTag::TotalReturnedPowerTariff1:
                ss >> totalPowerReturnedDayKwh;
                break;

            case LineTag::TotalReturnedPowerTariff2:
                ss >> totalPowerReturnedNightKwh;
                break;

            case LineTag::CurrentTarif:
            {
                int tarif = 1;
                ss >> tarif;
                usingDayTarif = (tarif == 2);
            }
            break;

            case LineTag::GasTotalConsumptionLog:
                ss >> gasTimestamp;
                {
                    const auto secondBracket = line.find('(', key.size() + value.size() + 2);
                    if(secondBracket == std::string::npos)
                    {
                        break;
                    }
                    const auto unitSymbol = line.find('*', secondBracket);
                    if(secondBracket == std::string::npos)
                    {
                        break;
                    }
                    const std::string consumption = line.substr(secondBracket + 1, unitSymbol - (secondBracket + 1));
                    ss.clear();
                    ss.str(consumption);
                    ss >> gasConsumptionCubicMeters;
                }
                break;

            default:
                break;
        }
    }

    void Data::ParseLines(const std::vector<std::string> & lines)
    {
        for(const auto & line: lines)
        {
            ParseLine(line);
        }
    }

    std::string Data::GetFormattedString(char const separator) const
    {
        std::stringstream ss;
        ss << currentPowerUsageKw << separator << totalPowerConsumptionDayKwh << separator
           << totalPowerConsumptionNightKwh << separator << currentPowerReturnKw << separator
           << totalPowerReturnedDayKwh << separator << totalPowerReturnedNightKwh << separator
           << (usingDayTarif ? "day-tarif" : "night-tarif") << separator << gasTimestamp << separator
           << gasConsumptionCubicMeters;

        return ss.str();
    }
}
