#include <cxxopts.hpp>
#include <electricity/logger/database.hpp>
#include <electricity/logger/dsmr.hpp>
#include <electricity/logger/serialport.hpp>
#include <exception>
#include <fcntl.h>
#include <optional>
#include <spdlog/spdlog.h>
#include <stdexcept>
#include <string>

std::optional<cxxopts::ParseResult> ExtractArgs(int argc, char ** argv)
{
    cxxopts::Options options(
        "electricity-logger",
        "electricity-logger is a small program that fetches power and gas statistics from an attached Landis Gyr E350 electricity meter and outputs this on stdout.");

    options.add_options()(
        "d,serial-device",
        "Absolute path to the serial device to read from",
        cxxopts::value<std::string>())(
        "connection-string",
        "Path to the sqlite3 database file",
        cxxopts::value<std::string>());

    if(argc == 1)
    {
        std::cout << options.help() << std::endl;
        return {};
    }

    try
    {
        auto const parsed = options.parse(argc, argv);
        return parsed;
    }
    catch(cxxopts::OptionException const & e)
    {
        spdlog::error(e.what());
        return {};
    }
}

DSMR::Data ReadData(std::string const & devicePath)
{
    char const * serialDeviceValue = devicePath.c_str();
    int fd = open(serialDeviceValue, O_RDONLY | O_NOCTTY | O_SYNC);
    if(fd < 0)
    {
        spdlog::error("Error opening device {}", serialDeviceValue);
        throw std::runtime_error("Error opening serial device for reading");
    }
    SerialPort serial(fd);

    spdlog::info("Starting logging operation with serial device {}", serialDeviceValue);

    // Find the first line, which starts with a forward slash
    bool waiting = true;
    do
    {
        try
        {
            auto line = serial.ReadLine();
            if(line.size() > 0 && line[0] == '/')
            {
                waiting = false;
            }
        }
        catch(std::exception const & ex)
        {
            spdlog::error("Error whilst seeking first output line from device {}: {}", serialDeviceValue, ex.what());
            throw std::runtime_error("Error reading from serial device");
        }
    } while(waiting);

    // We reached the interesting bits, parse them and keep what we want
    DSMR::Data data;
    int i = 1;
    while(i < 37)
    {
        try
        {
            auto line = serial.ReadLine();
            if(line.size() > 5)
            {
                data.ParseLine(line);
                ++i;
            }
        }
        catch(std::exception const & ex)
        {
            spdlog::error("Error whilst parsing line from device {}: {}", serialDeviceValue, ex.what());
            throw std::runtime_error("Error parsing data from serial device");
        }
    }

    return data;
}

int main(int argc, char ** argv)
{
    auto const maybeArgs = ExtractArgs(argc, argv);
    if(!maybeArgs.has_value())
    {
        return 1;
    }

    auto const & args = maybeArgs.value();

    auto const data = ReadData(args["serial-device"].as<std::string>());

    auto const connectionStringValue = args["connection-string"].as<std::string>();
    Database db(connectionStringValue);
    db.Insert(data, std::time(nullptr));

    return 0;
}
