#include "enc_cmd_comp.h"
#include "commands.h"
#include "hal.h"
#include "math.h"
#include "defines.h"
#include "angle.h"
#include "stm32f4xx_conf.h"
#include "hw/hw.h"

HAL_COMP(enc_cmd);

HAL_PIN(res);
HAL_PIN(pos);
HAL_PIN(a);
HAL_PIN(b);
HAL_PIN(fault);
HAL_PIN(mode);   // 0 = quad, 1 = step/dir, 2 = dir/step, 3 = up/down
HAL_PIN(remap);  // 0 = cmd, 1 = fb0, 2 = fb1, 3 = cmd bene style
HAL_PIN(input_filter);

struct enc_cmd_ctx_t {
  int e_res;
  uint32_t a_pin, b_pin, c_pin, c_en_pin, a_pin_source, b_pin_source, tim_af, tim_rcc;
  GPIO_TypeDef *a_port, *b_port, *c_port, *c_en_port;
  TIM_TypeDef *tim;
};

static void nrt_init(void *ctx_ptr, hal_pin_inst_t *pin_ptr) {
  struct enc_cmd_ctx_t *ctx      = (struct enc_cmd_ctx_t *)ctx_ptr;
  struct enc_cmd_pin_ctx_t *pins = (struct enc_cmd_pin_ctx_t *)pin_ptr;
  ctx->e_res                     = 0;
  PIN(res)                       = 4096.0;
  PIN(input_filter) = 3;
}

static void hw_init(void *ctx_ptr, hal_pin_inst_t *pin_ptr) {
  struct enc_cmd_ctx_t *ctx      = (struct enc_cmd_ctx_t *)ctx_ptr;
  struct enc_cmd_pin_ctx_t *pins = (struct enc_cmd_pin_ctx_t *)pin_ptr;

  ctx->e_res = (int)PIN(res);
  if(ctx->e_res < 1) {
    ctx->e_res = 1;
  }

  //RCC_AHB1PeriphClockCmd(FB1_|ENC0_B_IO_RCC, ENABLE);    //Enable needed Clocks for IOs
  GPIO_InitTypeDef GPIO_InitStruct;
  GPIO_StructInit(&GPIO_InitStruct);
  GPIO_InitStruct.GPIO_Mode  = GPIO_Mode_AF;
  GPIO_InitStruct.GPIO_Speed = GPIO_Speed_100MHz;
  GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStruct.GPIO_PuPd  = GPIO_PuPd_UP;


  switch((int)PIN(remap)) {
    case 0:
      ctx->a_pin        = CMD_A_PIN;
      ctx->b_pin        = CMD_B_PIN;
      ctx->c_pin        = CMD_C_PIN;
      ctx->c_en_pin     = CMD_C_EN_PIN;
      ctx->a_port       = CMD_A_PORT;
      ctx->b_port       = CMD_B_PORT;
      ctx->c_port       = CMD_C_PORT;
      ctx->c_en_port    = CMD_C_EN_PORT;
      ctx->a_pin_source = CMD_A_PIN_SOURCE;
      ctx->b_pin_source = CMD_B_PIN_SOURCE;
      ctx->tim_af       = CMD_ENC_TIM_AF;
      ctx->tim_rcc      = CMD_ENC_TIM_RCC;
      ctx->tim          = CMD_ENC_TIM;
      RCC_APB1PeriphClockCmd(ctx->tim_rcc, ENABLE);
      break;
    case 1:
      ctx->a_pin        = FB0_A_PIN;
      ctx->b_pin        = FB0_B_PIN;
      ctx->c_pin        = FB0_Z_PIN;
      ctx->c_en_pin     = FB0_Z_TXEN_PIN;
      ctx->a_port       = FB0_A_PORT;
      ctx->b_port       = FB0_B_PORT;
      ctx->c_port       = FB0_Z_PORT;
      ctx->c_en_port    = FB0_Z_TXEN_PORT;
      ctx->a_pin_source = FB0_A_PIN_SOURCE;
      ctx->b_pin_source = FB0_B_PIN_SOURCE;
      ctx->tim_af       = FB0_ENC_TIM_AF;
      ctx->tim_rcc      = FB0_ENC_TIM_RCC;
      ctx->tim          = FB0_ENC_TIM;
      RCC_APB1PeriphClockCmd(ctx->tim_rcc, ENABLE);
      break;
    case 2:
      ctx->a_pin        = FB1_A_PIN;
      ctx->b_pin        = FB1_B_PIN;
      ctx->c_pin        = FB1_Z_PIN;
      ctx->c_en_pin     = FB1_Z_TXEN_PIN;
      ctx->a_port       = FB1_A_PORT;
      ctx->b_port       = FB1_B_PORT;
      ctx->c_port       = FB1_Z_PORT;
      ctx->c_en_port    = FB1_Z_TXEN_PORT;
      ctx->a_pin_source = FB1_A_PIN_SOURCE;
      ctx->b_pin_source = FB1_B_PIN_SOURCE;
      ctx->tim_af       = FB1_ENC_TIM_AF;
      ctx->tim_rcc      = FB1_ENC_TIM_RCC;
      ctx->tim          = FB1_ENC_TIM;
      RCC_APB2PeriphClockCmd(ctx->tim_rcc, ENABLE);
      break;
    case 3:
      ctx->a_pin        = GPIO_Pin_8;
      ctx->b_pin        = GPIO_Pin_9;
      ctx->c_pin        = GPIO_Pin_8;
      ctx->c_en_pin     = GPIO_Pin_2;
      ctx->a_port       = GPIOA;
      ctx->b_port       = GPIOA;
      ctx->c_port       = GPIOB;
      ctx->c_en_port    = GPIOB;
      ctx->a_pin_source = GPIO_PinSource8;
      ctx->b_pin_source = GPIO_PinSource9;
      ctx->tim_af       = FB1_ENC_TIM_AF;
      ctx->tim_rcc      = FB1_ENC_TIM_RCC;
      ctx->tim          = FB1_ENC_TIM;
      RCC_APB2PeriphClockCmd(ctx->tim_rcc, ENABLE);
      break;
    default:
      return;
  }
  GPIO_InitStruct.GPIO_Pin = ctx->a_pin;
  GPIO_Init(ctx->a_port, &GPIO_InitStruct);

  GPIO_InitStruct.GPIO_Pin = ctx->b_pin;
  GPIO_Init(ctx->b_port, &GPIO_InitStruct);

  GPIO_InitStruct.GPIO_Mode  = GPIO_Mode_OUT;
  GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
  GPIO_InitStruct.GPIO_PuPd  = GPIO_PuPd_NOPULL;
  GPIO_InitStruct.GPIO_Pin   = ctx->c_pin;
  GPIO_Init(ctx->c_port, &GPIO_InitStruct);
  GPIO_InitStruct.GPIO_Pin = ctx->c_en_pin;
  GPIO_Init(ctx->c_en_port, &GPIO_InitStruct);

  GPIO_SetBits(ctx->c_en_port, ctx->c_en_pin);

  //Bind pins to Timer
  GPIO_PinAFConfig(ctx->a_port, ctx->a_pin_source, ctx->tim_af);
  GPIO_PinAFConfig(ctx->b_port, ctx->b_pin_source, ctx->tim_af);

  TIM_SetAutoreload(ctx->tim, ctx->e_res * 2 - 1);
  // quad
  TIM_Cmd(ctx->tim, DISABLE);
  TIM_EncoderInterfaceConfig(ctx->tim, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);
  TIM_ICInitTypeDef TIM_ICInitStruct;
  TIM_ICInitStruct.TIM_Channel     = TIM_Channel_1;
  TIM_ICInitStruct.TIM_ICFilter    = MAX(MIN(PIN(input_filter), 15), 0);                        //Digital filtering @ 1/32 fDTS
  TIM_ICInitStruct.TIM_ICPolarity  = TIM_ICPolarity_BothEdge;     //Just trigger at the rising edge, because its the  clock
  TIM_ICInitStruct.TIM_ICPrescaler = 1;                           //no prescaler, capture is done each time an edge is detected on the capture input
  TIM_ICInitStruct.TIM_ICSelection = TIM_ICSelection_IndirectTI;  //IC1 mapped to TI1
  TIM_ICInit(ctx->tim, &TIM_ICInitStruct);

  TIM_ICInitStruct.TIM_Channel     = TIM_Channel_2;
  TIM_ICInitStruct.TIM_ICFilter    = MAX(MIN(PIN(input_filter), 15), 0);                      //Digital filtering @ 1/32 fDTS
  TIM_ICInitStruct.TIM_ICPolarity  = TIM_ICPolarity_Rising;     //Trigger at every edge, because its the direction
  TIM_ICInitStruct.TIM_ICPrescaler = 1;                         //no prescaler, capture is done each time an edge is detected on the capture input
  TIM_ICInitStruct.TIM_ICSelection = TIM_ICSelection_DirectTI;  //IC2 mapped to TI1
  TIM_ICInit(ctx->tim, &TIM_ICInitStruct);
  TIM_Cmd(ctx->tim, ENABLE);
}

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

  int32_t tim = TIM_GetCounter(ctx->tim);

  PIN(a) = GPIO_ReadInputDataBit(ctx->a_port, ctx->a_pin);
  PIN(b) = GPIO_ReadInputDataBit(ctx->b_port, ctx->b_pin);

  float p = 0.0;
  p       = mod(tim * 2.0f * M_PI / (float)ctx->e_res);

  PIN(pos) = p;

  int r = (int)PIN(res);
  if(r < 1) {
    r = 1;
  }

  if(ctx->e_res != r) {
    ctx->e_res = r;
    TIM_SetAutoreload(ctx->tim, ctx->e_res * 2 - 1);
  }

  if(PIN(fault) > 0.0) {
    GPIO_SetBits(ctx->c_port, ctx->c_pin);
  } else {
    GPIO_ResetBits(ctx->c_port, ctx->c_pin);
  }
}

const hal_comp_t enc_cmd_comp_struct = {
    .name      = "enc_cmd",
    .nrt       = 0,
    .rt        = rt_func,
    .frt       = 0,
    .nrt_init  = nrt_init,
    .hw_init   = hw_init,
    .rt_start  = 0,
    .frt_start = 0,
    .rt_stop   = 0,
    .frt_stop  = 0,
    .ctx_size  = sizeof(struct enc_cmd_ctx_t),
    .pin_count = sizeof(struct enc_cmd_pin_ctx_t) / sizeof(struct hal_pin_inst_t),
};
