#include "rlpsij_comp.h"
#include "commands.h"
#include "hal.h"
#include "math.h"
#include "defines.h"
#include "angle.h"

HAL_COMP(rlpsij);

HAL_PIN(r);
HAL_PIN(l);
HAL_PIN(psi);
HAL_PIN(j);
HAL_PIN(load);
HAL_PIN(friction);
HAL_PIN(damping);
HAL_PIN(polecount);
HAL_PIN(fb_offset);

HAL_PIN(cmd_mode);
HAL_PIN(q_cmd);
HAL_PIN(d_cmd);
HAL_PIN(iq_fb);
HAL_PIN(id_fb);
HAL_PIN(uq_fb);
HAL_PIN(ud_fb);

HAL_PIN(abs_pos_fb);
HAL_PIN(com_pos);
HAL_PIN(en_out);

HAL_PIN(en);
HAL_PIN(test_cur);
HAL_PIN(test_vel);
HAL_PIN(rl_ki);
HAL_PIN(vel_ki);
HAL_PIN(vel_kp);
HAL_PIN(rl_time);
HAL_PIN(vel_time);
HAL_PIN(lpf);

HAL_PIN(timer);
HAL_PIN(multi_pos);
HAL_PIN(state);
HAL_PIN(vel);
HAL_PIN(acc);
HAL_PIN(last_pos);
HAL_PIN(error_sum);
HAL_PINA(i, 4);


static void nrt_init(void *ctx_ptr, hal_pin_inst_t *pin_ptr) {
  struct rlpsij_pin_ctx_t *pins = (struct rlpsij_pin_ctx_t *)pin_ptr;
  PIN(test_cur) = 1.0;
  PIN(test_vel) = 2.0 * M_PI * 1.0;
  PIN(rl_ki) = 10.0;
  PIN(vel_ki) = 0.01;
  PIN(vel_kp) = 0.1;
  PIN(rl_time) = 1.0;
  PIN(vel_time) = 4.0;
  PIN(lpf) = 0.9955;
}

static void rt_func(float period, void *ctx_ptr, hal_pin_inst_t *pin_ptr) {
  // struct rlpsij_ctx_t * ctx = (struct rlpsij_ctx_t *)ctx_ptr;
  struct rlpsij_pin_ctx_t *pins = (struct rlpsij_pin_ctx_t *)pin_ptr;

  float vel = minus(PIN(abs_pos_fb), PIN(last_pos)) / period;
  float acc = (vel - PIN(vel)) / period;
  PIN(vel) = PIN(vel) * 0.99 + vel * (1.0 - 0.99);
  PIN(acc) = PIN(acc) * 0.99 + acc * (1.0 - 0.99);
  PIN(last_pos) = PIN(abs_pos_fb);

  PIN(timer) += period;

  float vel_cmd = 0.0;
  float error = 0.0;

  switch((int)PIN(state)){
    case 0: // disabled
      PIN(timer) = 0.0;
      PIN(en_out) = 0.0;
      
      if(PIN(en) > 0.0){
        PIN(state) = 1;
      }
    break;

    case 1: // r, fb_offset
      PIN(en_out) = 1.0;
      PIN(com_pos) = 0.0;
      PIN(cmd_mode) = 0.0; // volt mode

      PIN(d_cmd) += PIN(rl_ki) * period * (PIN(test_cur) - PIN(id_fb));

      if(PIN(id_fb) > PIN(test_cur) * 0.5){
        PIN(r) = PIN(r) * PIN(lpf) + PIN(ud_fb) / PIN(id_fb) * (1.0 - PIN(lpf));
      }

      if(PIN(timer) > PIN(rl_time)){
        PIN(fb_offset) = PIN(abs_pos_fb);
        PIN(timer) = 0.0;
        PIN(state) = 2.0;
      }
    break;

    case 2: // l
      PIN(timer) = 0.0;
      PIN(state) = 3.0;
      PIN(polecount) = 0.0;
      PIN(d_cmd) = 0.0;
    break;

    case 3: // polecount
      PIN(cmd_mode) = 1.0;
      PIN(d_cmd) = PIN(test_cur);

      PIN(com_pos) += 2.0 * M_PI * period;

      if(PIN(com_pos) >= M_PI){
        PIN(polecount) += 1.0;
        PIN(com_pos) = -M_PI;
      }
      if(PIN(timer) > 1.0 && ABS(minus(PIN(abs_pos_fb), PIN(fb_offset))) < 2.0 * M_PI * 0.01){
        PIN(d_cmd) = 0.0;
        PIN(com_pos) = 0.0;
        PIN(error_sum) = 0.0;
        PIN(timer) = 0.0;
        PIN(state) = 4.0;
      }
    break;

    case 4: // psi, friction, damping, load
      vel_cmd = PIN(test_vel) / 10.0;
      int phase = 0;
      if(PIN(timer) > PIN(vel_time) / 4.0){
        phase = 1;
        vel_cmd = PIN(test_vel);
      }
      if(PIN(timer) > PIN(vel_time) / 4.0 * 2.0)
      {
        phase = 2;
        vel_cmd = -PIN(test_vel) / 10.0;
      }
      if(PIN(timer) > PIN(vel_time) / 4.0 * 3.0)
      {
        phase = 3;
        vel_cmd = -PIN(test_vel);
      }

      PIN(com_pos) = mod(minus(PIN(abs_pos_fb), PIN(fb_offset)) * PIN(polecount));

      error = vel_cmd - PIN(vel);
      PIN(error_sum) += PIN(vel_ki) * period * (vel_cmd - PIN(vel));
      PIN(error_sum) = LIMIT(PIN(error_sum), PIN(test_cur));
      PIN(q_cmd) = PIN(vel_kp) * error + PIN(error_sum);
      PIN(q_cmd) = LIMIT(PIN(q_cmd), PIN(test_cur));

      if(ABS(vel_cmd - PIN(vel)) < PIN(test_vel) * 0.05){
        float bemf = PIN(uq_fb) - PIN(iq_fb) * PIN(r);
        PIN(psi) = PIN(psi) * PIN(lpf) + bemf / PIN(vel) / PIN(polecount) * (1.0 - PIN(lpf));

        PINA(i, phase) = PINA(i, phase) * PIN(lpf) + PIN(iq_fb) * (1.0 - PIN(lpf));        
      }

      if(PIN(timer) > PIN(vel_time)){
        PIN(load) = (PINA(i, 0) + PINA(i, 1) + PINA(i, 2) + PINA(i, 3)) / 4.0;
        PIN(damping) = (PINA(i, 1) - PINA(i, 0) - PINA(i, 3) + PINA(i, 2)) / 2.0 / (PIN(test_vel) * 0.9);
        PIN(friction) = (PINA(i, 1) - PIN(damping) * PIN(test_vel) - PINA(i, 3) - PIN(damping) * PIN(test_vel)) / 2.0;

        PIN(load) *= 3.0 / 2.0 * PIN(polecount) * PIN(psi);
        PIN(damping) *= 3.0 / 2.0 * PIN(polecount) * PIN(psi);
        PIN(load) *= 3.0 / 2.0 * PIN(polecount) * PIN(psi);

        PIN(q_cmd) = 0.0;
        PIN(timer) = 0.0;
        PIN(state) = 5.0;
      }
    break;

    case 5: // j
      if(PIN(timer) < PIN(vel_time) / 2.0){
        PIN(q_cmd) = PIN(test_cur);
      }
      else{
        PIN(q_cmd) = -PIN(test_cur);
      }

      PIN(com_pos) = mod(minus(PIN(abs_pos_fb), PIN(fb_offset)) * PIN(polecount));

      float t = 3.0 / 2.0 * PIN(polecount) * PIN(psi) * PIN(iq_fb);
      t -= PIN(load);
      t -= PIN(friction) * SIGN(PIN(vel));
      t -= PIN(damping) * PIN(vel);

      if(ABS(PIN(acc)) > PIN(test_vel)){
        PIN(j) = PIN(j) * PIN(lpf) + t / PIN(acc) * (1.0 - PIN(lpf));
      }

      if(PIN(timer) > PIN(vel_time)){
        PIN(state) = 6.0;
        PIN(q_cmd) = 0.0;
        PIN(d_cmd) = 0.0;
      }
    break;

    case 6:
      PIN(com_pos) = mod(minus(PIN(abs_pos_fb), PIN(fb_offset)) * PIN(polecount));
      if(PIN(en) <= 0.0){
        PIN(state) = 0.0;
      }
    break;
  }
}

hal_comp_t rlpsij_comp_struct = {
    .name      = "rlpsij",
    .nrt       = 0,
    .rt        = rt_func,
    .frt       = 0,
    .nrt_init  = nrt_init,
    .rt_start  = 0,
    .frt_start = 0,
    .rt_stop   = 0,
    .frt_stop  = 0,
    .ctx_size  = 0,
    .pin_count = sizeof(struct rlpsij_pin_ctx_t) / sizeof(struct hal_pin_inst_t),
};
