#include "canEmu.h"
#include "stm32f1xx_hal.h"
#include "rs_message.h"

volatile uint32_t CANEMU_BIT_TICKS = 0;
CANEmu_HandleTypeDef hcanemu;

int flag_manual = 0;
int transmit_prev = 0;

static void delay_us(uint32_t us) {
    uint32_t ticks = us * CANEMU_TIM_US_TICKS;
    uint32_t start = tim_get(CANEMU_TIM);
    const uint32_t timeout_ticks = 1000000;  // ~макс. 1 млн тиков на случай ошибки (настраивается)

    uint32_t safety_counter = 0;

    while ((uint32_t)(tim_get(CANEMU_TIM) - start) < ticks) {
        if (++safety_counter > timeout_ticks) {
            break;  // Защита от зависания
        }
    }
}

void CANEmu_Init(CANEmu_HandleTypeDef *canemu, uint32_t bitrate) {
    // Настройка GPIO — должна быть сделана отдельно, например:
    // GPIOA->CRL &= ~(0xF << (CANEMU_TX_GPIO_PIN * 4));
    // GPIOA->CRL |=  (0x1 << (CANEMU_TX_GPIO_PIN * 4)); // Output push-pull, 10 MHz

    // Настройка таймера:
    // TIM2->PSC = 0;         // Предделитель
    // TIM2->ARR = 0xFFFF;
    // TIM2->CR1 = 0;
    // TIM2->CR1 |= TIM_CR1_CEN;
    canemu->bitrate_bps = bitrate;
    tim_start(CANEMU_TIM);
    can_tx_set_1(); // Recessive level
}

void wait_exact_ticks(uint16_t target_ticks) {
    const uint32_t timeout_ticks = 1000000;
    uint32_t safety = 0;

    while ((int16_t)(tim_get(CANEMU_TIM) - target_ticks) < 0) {
        if (++safety > timeout_ticks) break;
    }
}


void CANEmu_SendFrame(CANEmu_HandleTypeDef *canemu) {
  if (canemu->bitrate_bps == 0) return;

  form_CAN_bitstream_full(&canemu->header, canemu->data, canemu->errors);

  CANEMU_BIT_TICKS = (CANEMU_TIM_CLOCK_HZ + canemu->bitrate_bps / 2) / canemu->bitrate_bps;

  uint16_t current = tim_get(CANEMU_TIM);
  uint16_t next_edge = current;

  __disable_irq();
  for (uint32_t i = 0; i < can_bits_len; i++) {
      if (can_bits[i])
          can_tx_set_1();
      else
          can_tx_set_0();

      next_edge += (uint16_t)CANEMU_BIT_TICKS;  // автоматически обрежется до 16 бит
      wait_exact_ticks(next_edge);
  }

  can_tx_set_1(); // Завершаем линию
  __enable_irq();
}


void CANEmu_Working(CANEmu_HandleTypeDef *canemu)
{
  
    if(flag_manual == 0)
    {
      canemu->start_poll          = MB_DATA.Coils.START_POLLING;
      canemu->transmit            = MB_DATA.Coils.START_SINGLE_FRAME;
      canemu->period_ms           = MB_DATA.HoldRegs.CAN_PERIOD;
      if(canemu->period_ms < CAN_MIN_PERIOD)
      {
        canemu->period_ms = CAN_MIN_PERIOD;
      }
      // can message
      canemu->header.IDE    = MB_DATA.Coils.HEADER_IDE;
      canemu->header.RTR    = MB_DATA.Coils.HEADER_RTR;      
      canemu->bitrate_bps   = (uint32_t)MB_DATA.HoldRegs.CAN_BITRATE_KBPS*1000;
      canemu->header.ExtId  = ((uint32_t)MB_DATA.HoldRegs.CAN_ID_HI << 16) | MB_DATA.HoldRegs.CAN_ID_LO;
      canemu->header.StdId  = canemu->header.ExtId;
      canemu->header.DLC    = (uint8_t)MB_DATA.HoldRegs.CAN_DLC & 0xF;
      canemu->data[0]       = (uint8_t)MB_DATA.HoldRegs.CAN_DATA_0;
      canemu->data[1]       = (uint8_t)MB_DATA.HoldRegs.CAN_DATA_1;
      canemu->data[2]       = (uint8_t)MB_DATA.HoldRegs.CAN_DATA_2;
      canemu->data[3]       = (uint8_t)MB_DATA.HoldRegs.CAN_DATA_3;
      canemu->data[4]       = (uint8_t)MB_DATA.HoldRegs.CAN_DATA_4;
      canemu->data[5]       = (uint8_t)MB_DATA.HoldRegs.CAN_DATA_5;
      canemu->data[6]       = (uint8_t)MB_DATA.HoldRegs.CAN_DATA_6;
      canemu->data[7]       = (uint8_t)MB_DATA.HoldRegs.CAN_DATA_7;
      
      // errors
      canemu->errors.FF_SRS       = MB_DATA.Coils.FLIP_SRS;
      canemu->errors.FF_IDE       = MB_DATA.Coils.FLIP_IDE;
      canemu->errors.FF_RTR       = MB_DATA.Coils.FLIP_RTR;
      canemu->errors.FF_R1        = MB_DATA.Coils.FLIP_R1;
      canemu->errors.FF_R0        = MB_DATA.Coils.FLIP_R0;
      canemu->errors.MSGID        = MB_DATA.Coils.FLIP_MSGID_BIT;
      canemu->errors.DATA         = MB_DATA.Coils.FLIP_DATA_BIT;
      canemu->errors.CRC_ERR      = MB_DATA.Coils.FLIP_CRC_BIT;
      canemu->errors.STUFF_BITS   = MB_DATA.Coils.DISABLE_STUFF_BITS;
      
    }
    
    
    
    if(canemu->start_poll)
    {
      canemu->transmit = 0;
      HAL_Delay(canemu->period_ms);
      CANEmu_SendFrame(&hcanemu);
    }
    else if((canemu->transmit == 1) && (transmit_prev == 0))
    {
      CANEmu_SendFrame(&hcanemu);
    }
    transmit_prev = canemu->transmit;
  }
