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

HAL_COMP(fb_switch);

HAL_PIN(polecount);
HAL_PIN(track_fb);
HAL_PIN(offset_first_enable);

HAL_PIN(pos_fb);
HAL_PIN(vel_fb);
HAL_PIN(com_fb);
HAL_PIN(joint_fb);
HAL_PIN(state);  // 0 = disabled, 1 = enabled

HAL_PIN(cmd_pos);

HAL_PIN(mot_pos);
HAL_PIN(mot_abs_pos);
HAL_PIN(mot_polecount);
HAL_PIN(mot_offset);
HAL_PIN(mot_state);  // 0 = disabled, 1 = inc, 2 = start abs, 3 = abs
HAL_PIN(mot_rev);
HAL_PIN(mot_fb_no_offset);
HAL_PIN(mot_abs_fb_no_offset);

HAL_PIN(plot_fb_pos);

HAL_PIN(com_pos);
HAL_PIN(com_abs_pos);
HAL_PIN(com_polecount);
HAL_PIN(com_offset);
HAL_PIN(com_state);
HAL_PIN(com_rev);
HAL_PIN(com_fb_no_offset);

HAL_PIN(joint_pos);
HAL_PIN(joint_abs_pos);
HAL_PIN(joint_offset);
HAL_PIN(joint_state);
HAL_PIN(joint_rev);
HAL_PIN(joint_fb_no_offset);

HAL_PIN(mot_joint_ratio);

HAL_PIN(phase_time);
HAL_PIN(phase_cur);
HAL_PIN(phase_gain);
HAL_PIN(offset);
HAL_PIN(id);

HAL_PIN(current_com_pos);

HAL_PIN(en);

struct fb_switch_ctx_t {
  int32_t current_com_pos;
  float phase_start_pos;
  float cmd_com_offset;
  float cmd_mot_offset;
  float cmd_joint_offset;
  float com_offset;
  float phase_timer;
  int32_t phase_state;
};

static void nrt_init(void *ctx_ptr, hal_pin_inst_t *pin_ptr) {
  struct fb_switch_ctx_t *ctx      = (struct fb_switch_ctx_t *)ctx_ptr;
  struct fb_switch_pin_ctx_t *pins = (struct fb_switch_pin_ctx_t *)pin_ptr;

  ctx->current_com_pos = 10;
  ctx->cmd_mot_offset      = 0.0;
  ctx->com_offset      = 0.0;
  PIN(phase_cur)       = 1.0;
  PIN(phase_time)      = 1.0;
  PIN(phase_gain)      = 100.0;
  PIN(offset_first_enable) = 1.0;
}

static void rt_func(float period, void *ctx_ptr, hal_pin_inst_t *pin_ptr) {
  struct fb_switch_ctx_t *ctx      = (struct fb_switch_ctx_t *)ctx_ptr;
  struct fb_switch_pin_ctx_t *pins = (struct fb_switch_pin_ctx_t *)pin_ptr;

  float mot_pos   = PIN(mot_pos);
  float com_pos   = PIN(com_pos);
  float joint_pos = PIN(joint_pos);

  float mot_abs_pos   = PIN(mot_abs_pos);
  float com_abs_pos   = PIN(com_abs_pos);
  float joint_abs_pos = PIN(joint_abs_pos);

  float mot_offset   = PIN(mot_offset);
  float com_offset   = PIN(com_offset);
  float joint_offset = PIN(joint_offset);


  if(PIN(mot_rev) > 0.0) {
    mot_pos *= -1.0;
    mot_abs_pos *= -1.0;
    mot_offset *= -1.0;
  }

  if(PIN(com_rev) > 0.0) {
    com_pos *= -1.0;
    com_abs_pos *= -1.0;
    com_offset *= -1.0;
  }

  if(PIN(joint_rev) > 0.0) {
    joint_pos *= -1.0;
    joint_abs_pos *= -1.0;
    joint_offset *= -1.0;
  }

  PIN(com_fb_no_offset)     = com_pos;
  PIN(mot_fb_no_offset)     = mot_pos;
  PIN(mot_abs_fb_no_offset) = mot_abs_pos;
  PIN(joint_fb_no_offset)   = joint_pos;

  PIN(id) = 0.0;

  if(PIN(joint_state) == 1.0 || PIN(joint_state) == 3.0){
    PIN(pos_fb) = mod(joint_pos + ctx->cmd_joint_offset);
    PIN(plot_fb_pos) = PIN(joint_fb);
  }
  else if(PIN(mot_state) == 1.0 || PIN(mot_state) == 3.0){
    PIN(pos_fb) = mod(mot_pos + ctx->cmd_mot_offset);
    PIN(plot_fb_pos) = PIN(pos_fb);
  }
  else if(PIN(com_state) == 1.0 || PIN(com_state) == 3.0){
    PIN(pos_fb) = mod(com_pos + ctx->cmd_com_offset);
    PIN(plot_fb_pos) = PIN(pos_fb);
  }

  if(PIN(mot_state) == 1.0 || PIN(mot_state) == 3.0){
    PIN(vel_fb) = mot_pos;
  }
  else if(PIN(com_state) == 1.0 || PIN(com_state) == 3.0){
    PIN(vel_fb) = com_pos;
  }

  if(PIN(en) <= 0.0) {
    PIN(state)           = 0.0;
    ctx->current_com_pos = 10;
    ctx->com_offset      = 0.0;
    if((PIN(track_fb) <= 0.0 || PIN(offset_first_enable) > 0) && PIN(mot_state) > 0.0){
      PIN(offset_first_enable) = 0;
      ctx->cmd_com_offset      = minus(PIN(cmd_pos), com_pos);
      ctx->cmd_mot_offset      = minus(PIN(cmd_pos), mot_pos);
      ctx->cmd_joint_offset    = minus(PIN(cmd_pos), joint_pos);
    }
    PIN(plot_fb_pos)          = mod(mot_pos);
  } else {
    PIN(state) = 1.0;
    if(PIN(joint_state) >= 2.0 && ctx->current_com_pos > 3.0) {
      ctx->current_com_pos = 3; // joint fb absolute
      ctx->com_offset      = minus(mod((joint_abs_pos + joint_offset) * PIN(polecount) / PIN(mot_joint_ratio)), mod(mot_pos * PIN(polecount) / PIN(mot_polecount)));
    }
    if(PIN(com_state) >= 2.0 && ctx->current_com_pos > 2.0) {
      ctx->current_com_pos = 2; // com fb absolute
      ctx->com_offset      = minus(mod((com_abs_pos + com_offset) * PIN(polecount) / PIN(com_polecount)), mod(mot_pos * PIN(polecount) / PIN(mot_polecount)));
    }
    if(PIN(mot_state) >= 2.0 && ctx->current_com_pos > 1.0) {
      ctx->current_com_pos = 1; // mot fb absolute
      ctx->com_offset      = 0.0;
    }
    if(ctx->current_com_pos > 4.0) { // autophasing
      switch(ctx->current_com_pos) {
        case 5: // phasing
          ctx->phase_timer += period;
          PIN(id) = CLAMP(ctx->phase_timer / (MAX(PIN(phase_time), 0.1) / 4.0) * PIN(phase_cur), 0.0, PIN(phase_cur)); // ramp up id
          ctx->com_offset += minus(ctx->phase_start_pos, mot_pos) * PIN(phase_gain) * period;

          if(ctx->phase_timer >= MAX(PIN(phase_time), 0.1)) { // end
            ctx->phase_timer     = 0.0;
            //ctx->com_offset      = -mod(mot_pos * PIN(polecount) / PIN(mot_polecount));
            ctx->cmd_mot_offset      = minus(PIN(cmd_pos), mot_pos);
            ctx->current_com_pos = 4.0;
          }
        break;

        default: // init
          ctx->phase_start_pos = mot_pos; // safe start pos
          PIN(com_fb) = 0.0;
          ctx->com_offset = 0.0;
          ctx->current_com_pos = 5.0;
      }
    }

    switch(ctx->current_com_pos) {
      case 4: // autophasing + mot fb -> com fb
        PIN(com_fb) = mod(mot_pos * PIN(polecount) / PIN(mot_polecount) + ctx->com_offset);
        break;

      case 3: // joint fb -> com fb
        if(PIN(joint_state) != 3.0) {
          PIN(com_fb) = mod(mot_pos * PIN(polecount) / PIN(mot_polecount) + ctx->com_offset); // tracking
        } else {
          PIN(com_fb) = mod((joint_abs_pos + joint_offset) * PIN(polecount));
        }
        break;

      case 2: // com fb -> com fb
        if(PIN(com_state) != 3.0) {
          PIN(com_fb) = mod(mot_pos * PIN(polecount) / PIN(mot_polecount) + ctx->com_offset); // tracking
        } else {
          PIN(com_fb) = mod((com_abs_pos + com_offset) * PIN(polecount) / PIN(com_polecount));
        }
        break;

      case 1: // mot fb -> com fb
        if(PIN(mot_state) != 3.0) {
          PIN(state) = 0.0;
        } else {
          PIN(com_fb) = mod((mot_abs_pos + mot_offset) * PIN(polecount) / PIN(mot_polecount));
        }
        break;

      default:
        PIN(state) = 0.0;
        PIN(com_fb) = mod(ctx->phase_start_pos * PIN(polecount) / PIN(mot_polecount) + ctx->com_offset);
    }
  }
  PIN(current_com_pos) = ctx->current_com_pos;
  PIN(offset) = ctx->com_offset;
}

hal_comp_t fb_switch_comp_struct = {
    .name      = "fb_switch",
    .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  = sizeof(struct fb_switch_ctx_t),
    .pin_count = sizeof(struct fb_switch_pin_ctx_t) / sizeof(struct hal_pin_inst_t),
};
