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

volatile uint32_t CANEMU_BIT_TICKS = 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

    GPIO_InitTypeDef GPIO_InitStruct = {0};

    /*Configure GPIO pin */
    GPIO_InitStruct.Pin = (1<<CANEMU_TX_GPIO_PIN);
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(CANEMU_TX_GPIO_PORT, &GPIO_InitStruct);
    
  
    // Настройка таймера:
    // 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
    canemu->EmuFlag = 1;
}

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;
#ifdef LED_WORKING
  LED_WORKING(1);
#endif

  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();
#ifdef LED_WORKING
  LED_WORKING(0);
#endif
}
