#include "larsmmode.h"

// local includes
#include "globals.h"
#include "utils.h"

namespace modes {
LarsmMode larsmMode;
} // namespace modes

void LarsmMode::start()
{
    Base::start();

    adc1_filtered = 0.f;
    adc2_filtered = 0.f;
    speed = 0;
    weak = 0.f;
}

void LarsmMode::update()
{
    if (!gas || !brems)
    {
        start();

        for (bobbycar::protocol::serial::MotorState &motor : motors())
        {
            motor.ctrlTyp = bobbycar::protocol::ControlType::FieldOrientedControl;
            motor.ctrlMod = bobbycar::protocol::ControlMode::OpenMode;
            motor.pwm = 0;
            motor.cruiseCtrlEna = false;
            motor.nCruiseMotTgt = 0;
        }
    }
    else
    {
        for (uint8_t i = 0; i < profileSettings.larsmMode.iterations; i++) // run multiple times to emulate higher refreshrate
        {
            // ####### larsm's bobby car code #######

            // LOW-PASS FILTER (fliessender Mittelwert)
            adc1_filtered = adc1_filtered * 0.9 + *gas * 0.1;  // ADC1, TX, rechts, vorwaerts, blau
            adc2_filtered = adc2_filtered * 0.9 + *brems * 0.1;  // ADC2, RX, links, rueckwearts, gruen

            // magic numbers die ich nicht mehr nachvollziehen kann, faehrt sich aber gut ;-)
            #define LOSLASS_BREMS_ACC 0.996f  // naeher an 1 = gemaechlicher
            #define DRUECK_ACC2 (1.0f - LOSLASS_BREMS_ACC + 0.001f)  // naeher an 0 = gemaechlicher
            #define DRUECK_ACC1 (1.0f - LOSLASS_BREMS_ACC + 0.001f)  // naeher an 0 = gemaechlicher
            //die + 0.001f gleichen float ungenauigkeiten aus.

            #define ADC1_MIN 0
            #define ADC2_MIN 0
            #define ADC1_MAX 1000
            #define ADC2_MAX 1000

            #define ADC2_DELTA (ADC2_MAX - ADC2_MIN)
            #define ADC1_DELTA (ADC1_MAX - ADC1_MIN)

            #define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))

            if (profileSettings.larsmMode.mode == LarsmModeMode::Mode1) {  // Mode 1, links: 3 kmh
              speed = (float)speed * LOSLASS_BREMS_ACC  // bremsen wenn kein poti gedrueckt
                      - (CLAMP(*brems - ADC2_MIN, 0, ADC2_DELTA) / (ADC2_DELTA / 280.0f)) * DRUECK_ACC2   // links gedrueckt = zusatzbremsen oder rueckwaertsfahren
                      + (CLAMP(*gas - ADC1_MIN, 0, ADC1_DELTA) / (ADC1_DELTA / 350.0f)) * DRUECK_ACC1;  // vorwaerts gedrueckt = beschleunigen 12s: 350=3kmh
              weak = 0;

            } else if (profileSettings.larsmMode.mode == LarsmModeMode::Mode2) { // Mode 2, default: 6 kmh
              speed = (float)speed * LOSLASS_BREMS_ACC
                      - (CLAMP(*brems - ADC2_MIN, 0, ADC2_DELTA) / (ADC2_DELTA / 310.0f)) * DRUECK_ACC2
                      + (CLAMP(*gas - ADC1_MIN, 0, ADC1_DELTA) / (ADC1_DELTA / 420.0f)) * DRUECK_ACC1;  // 12s: 400=5-6kmh 450=7kmh
              weak = 0;

            } else if (profileSettings.larsmMode.mode == LarsmModeMode::Mode3) { // Mode 3, rechts: 12 kmh
              speed = (float)speed * LOSLASS_BREMS_ACC
                      - (CLAMP(*brems - ADC2_MIN, 0, ADC2_DELTA) / (ADC2_DELTA / 340.0f)) * DRUECK_ACC2
                      + (CLAMP(*gas - ADC1_MIN, 0, ADC1_DELTA) / (ADC1_DELTA / 600.0f)) * DRUECK_ACC1;  // 12s: 600=12kmh
              weak = 0;

            } else if (profileSettings.larsmMode.mode == LarsmModeMode::Mode4) { // Mode 4, l + r: full kmh
              // Feldschwaechung wird nur aktiviert wenn man schon sehr schnell ist. So gehts: Rechts voll druecken und warten bis man schnell ist, dann zusaetzlich links schnell voll druecken.
              if (adc2_filtered > (ADC2_MAX - 450) && speed > 800) { // field weakening at high speeds
                speed = (float)speed * LOSLASS_BREMS_ACC
                      + (CLAMP(*gas - ADC1_MIN, 0, ADC1_DELTA) / (ADC1_DELTA / 1000.0f)) * DRUECK_ACC1;
                weak = weak * 0.95 + 400.0 * 0.05;  // sanftes hinzuschalten des turbos, 12s: 400=29kmh
              } else { //normale fahrt ohne feldschwaechung
                speed = (float)speed * LOSLASS_BREMS_ACC
                      - (CLAMP(*brems - ADC2_MIN, 0, ADC2_DELTA) / (ADC2_DELTA / 340.0f)) * DRUECK_ACC2
                      + (CLAMP(*gas - ADC1_MIN, 0, ADC1_DELTA) / (ADC1_DELTA / 1000.0f)) * DRUECK_ACC1;  // 12s: 1000=22kmh
                weak = weak * 0.95;  // sanftes abschalten des turbos
              }
              // weak should never exceed 400 or 450 MAX!!
            }

            speed = CLAMP(speed, -1000, 1000);  // clamp output
        }

        for (bobbycar::protocol::serial::MotorState &motor : motors())
        {
            const auto pair = split(profileSettings.larsmMode.modelMode);
            motor.ctrlTyp = pair.first;
            motor.ctrlMod = pair.second;
            motor.pwm = speed + weak;
            motor.cruiseCtrlEna = false;
            motor.nCruiseMotTgt = 0;
        }
    }
}
