//
// Created by philipp on 26.04.18.
//

#include "../include/VeloxProtocolLib/Connection.h"
#include <iostream>
#include <boost/algorithm/string.hpp>
#include <fstream>
#include "ultrasonic/UltrasonicSensor.h"
#include "tools/StreamMeanFilter.h"

constexpr float MAX_SPEED = 100.0f;

int readDevId()
{
    // open config file
    std::string userHome = getenv("HOME");
    std::ifstream configFile;
    configFile.open(userHome + "/CarConfig/sensor.config", std::ifstream::in);
    if (!configFile.is_open())
    { throw std::runtime_error{"File '/CarConfig/sensor.config' not found!\n"}; }

    // desired parameters
    int devId;
    bool devIdFound{false};
    std::string contentLine;
    while (!configFile.eof())
    {
        std::getline(configFile, contentLine);

        // split this line
        std::istringstream iss(contentLine);
        std::vector<std::string> words{std::istream_iterator<std::string>{iss}, {}};

        if (words.size() > 1)
        { // this line contains parameter[at(0)] and value[at(1)]
            if (words.at(0) == "devId:")
            {
                std::stringstream ss;
                ss << std::hex << words.at(1);
                ss >> devId;
                devIdFound = true;
            }
        }
    }

    if (!devIdFound)
    { throw std::runtime_error{"No attribute devId found!\n"}; }

    configFile.close();

    return devId;
}

int main(int argc, char ** argv)
{
    using namespace veloxProtocol;
    networking::Networking net;
    std::atomic<bool> monitorSTM{false};
    std::atomic<bool> monitorUSS{false};
    std::atomic<float> speed{0.0f};
    std::atomic<bool> running{true};
    std::mutex outputMutex;
    UltrasonicSensor sensor{readDevId()};
    car::StreamMeanFilter streamMeanFilter{5};
    auto timer = networking::time::Timer::create(net);
    auto conn = Connection::create(net);
    constexpr float brakeSpeed = -0.03f * 4;
    conn->open(
        "/dev/ttySAC0",
        [&]
        {
            if (!monitorSTM)
                return;

            std::lock_guard<std::mutex> lock{outputMutex};
            std::cout << "[STM Update]\nSpeed=" << conn->getMeasuredSpeed().get()
                      << "\nSteering Angle: " << conn->getMeasuredSteeringAngle().get() << "\n\n";
        },
        []
        { std::cout << "Connection closed!\n"; });

    sensor.init();
    timer = networking::time::Timer::create(net);
    timer->startPeriodicTimeout(
        std::chrono::milliseconds(UltrasonicSensor::DELAY),
        [&]
        {
            auto originalDistance = sensor.getDistance();
            auto filteredDistance = streamMeanFilter.moveWindow(originalDistance);
            if (filteredDistance <= 50)
                conn->setSpeed(brakeSpeed);
            else
                conn->setSpeed(speed);

            if (!monitorUSS)
                return;

            std::lock_guard<std::mutex> lock{outputMutex};
            std::cout << "[USS Update]\n"
                      << "Original Distance=" << originalDistance << "\n"
                      << "Filtered Distance=" << filteredDistance << "\n\n";
        });

    std::cout << "\n\nMANUAL\n"
              << "------\n"
              << "Enter 'stm' to enable/disable monitoring for STM data.\n"
              << "Enter 'uss' to enable/disable monitoring for USS data.\n"
              << "Enter 'q' to quit.\n"
              << "Enter 's <float> t' to set speed\n"
              << "Enter 'a <float>' to set angle\n"
              << "\n\n";

    for (std::string in; in != "q"; std::getline(std::cin, in))
    {
        if (in.empty())
        {
            speed = brakeSpeed;
            continue;
        }

        if (in == "stm")
        {
            monitorSTM = !monitorSTM;
            continue;
        }
        else if (in == "uss")
        {
            monitorUSS = !monitorUSS;
            continue;
        }

        std::vector<std::string> split;
        boost::split(split, in, [](char c)
        { return c == ' '; });

        std::string cmd = split.at(0);
        float value = std::stof(split.at(1));

        if (cmd == "a")
        {
            std::cout << "Setting steering angle to: " << value << "\n";
            conn->setSteeringAngle(value);
        }
        else if (cmd == "s")
        {
            value /= 100.0f;
            if (std::abs(value) > MAX_SPEED)
            {
                value = value >= 0 ? MAX_SPEED : -MAX_SPEED;
                std::cout << "WARNING: Speed set to 1.0 due to risk of collision!\n";
            }
            speed = value;
            std::cout << "Setting speed to: " << value << "\n";
        }
        else
        {
            std::cout << "Error: Unknown command!\n";
            speed = brakeSpeed;
        }
    }

    running = false;
    conn->close();
    sleep(1);

    return 0;
}