#include "lfr_state_machine.h"

// for string delimiter
vector<string> LFR_StateMachine::split (string s, string delimiter) const {
    size_t pos_start = 0, pos_end, delim_len = delimiter.length();
    string token;
    vector<string> res;

    while ((pos_end = s.find (delimiter, pos_start)) != string::npos) {
        token = s.substr (pos_start, pos_end - pos_start);
        pos_start = pos_end + delim_len;
        res.push_back (token);
    }

    res.push_back (s.substr (pos_start));
    return res;
}

void LFR_StateMachine::sanitize (string& s) const
{
        char r = '\r', n = '\n';
        s.erase(std::remove(s.begin(), s.end(), n), s.end());
        s.erase(std::remove(s.begin(), s.end(), r), s.end());
}

bool LFR_StateMachine::checkStringValidity (const std::vector<std::string>& s) const
{
    if(s.size() != 5)
    {
        return false;
    }

    try
    {
        for(int i = 0; i < 4; i ++)
        {
            if(stod(s[i]) > 1.0 || stod(s[i]) < -1.0)
            {
                return false;
            }
        }
        int i = stoi(s[4]);
        if(i != 0 && i != 1)
        {
            return false;
        }
    }
    catch(const std::exception& e) {return false;}
    return true;
}

void LFR_StateMachine::parseString(string s)
{
    sanitize(s);
    std::vector<std::string> splitStr = split(s, ";");
    if(!checkStringValidity(splitStr))
    {
        std::cout<<"Invalid String" << std::endl;
        return;
    }
    double wheels[4] = {0.0, 0.0, 0.0, 0.0};
    int mode = std::stoi(splitStr[4]);
    for(int i = 0; i < 3; i++)
    {
        wheels[i] = std::stod(splitStr[i]);
    }
    if(mode == 0) {
        setState(State::Manual::getInstance());
        uartCommunicator.sendTelegram(wheels[0], wheels[1], wheels[2], wheels[3]);
    }
    else if (mode == 1) {setState(State::Autonomous::getInstance());}
    return;
}

LFR_StateMachine::LFR_StateMachine():    
    autonomousMode(videoHeight, videoWidth, thresholdBinary, gaussKernelSize, maxSpeed [&](std::exception const &ex)
    {
        std::unique_lock<std::mutex> lock(mutex);
        std::cerr<<"camera exception:"<<ex.what()<<std::endl;
        return false;
    }),
    uartCommunicator(),
    socket([&](std::exception const &ex)
    {
        std::unique_lock<std::mutex> lock(mutex);
        std::cerr<<"socket exception:"<<ex.what()<<std::endl;
        return false;
    })
{
    // Connect String parser to socket
    socket.addListener([&](LFR_Socket::LFR_Telegram telegram)
    {
        std::unique_lock<std::mutex> lock(mutex);
        parseString(std::string(telegram));
    }, &mutex);
    socket.startLoop();
    
    currentState = &State::Idle::getInstance();
    currentState->enter(this);

    //Start the permanent loop
    char input;
    std::cout << "press q to quit" << std::endl;
    std::cin >> input;
    std::cout << "binned" << std::endl;
    while (input != 'q')
    {
        std::cin >> input;
        std::cout << "binned" << std::endl;
    }
    std::cout << "Exiting central" << std::endl;
}

void LFR_StateMachine::setState(LFR_IState& newState)
{
    if (&newState != currentState)
    {
        currentState->exit(this);
        currentState = &newState;
        currentState->enter(this);
    }
    else
    {
        std::cout << "no switch" << std::endl;
    }

}

void LFR_StateMachine::enterAutonomous()
{
    //cv::Mat img;
    autonomousMode.addListener([&](LFR_Result result)
    {
        std::unique_lock<std::mutex> lock(mutex);
        if (!result.rawImage.empty())
        {
            /*
            img = result.rawImage;
            //Resize to minimize latency using raspi over ssh/adapt to 3.5in screen
            cv::resize(result.rawImage, img, cv::Size(480, 320));

            //Calculate frame rate
            now = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch());
            unsigned int deltaMs = static_cast<unsigned int>((now-last).count());
            double delta = static_cast<double>(deltaMs) / 1000.0;
            double frameRate = 1.0 / static_cast<double>(delta);*/
            double frameRate = -1.0;

            if (result.validLane)
            {
                std::cout << "Frame rate: " << frameRate << " angle: " << result.data.angle
                    << " motor 1: " << result.motorSignals[0] << " motor 2: " << result.motorSignals[1]
                    << " motor 3: " << result.motorSignals[2] << " motor 4: " << result.motorSignals[3] <<std::endl;
                uartCommunicator.sendTelegram(result.motorSignals[0], result.motorSignals[1], result.motorSignals[2], result.motorSignals[3]);
            }
            else
            {
                std::cout << "No lane found." << std::endl;;
            }
            //last = now;
        }
    }, &mutex);

    autonomousMode.startLoop();
}

void LFR_StateMachine::exitAutonomous()
{
    autonomousMode.removeListener(&mutex);
    autonomousMode.endLoop();
}