#include <stdint.h>
#include <stdlib.h>

#include <stm32f4xx.h>
#include "exti.h"
#include "registers.h"
#include "stm32f401xe.h"

void exti_init(exti_t* exti, uint8_t line, EXTI_TypeDef *exti_periph, SYSCFG_TypeDef *syscfg) {
  exti->exti = exti_periph;
  exti->syscfg = syscfg;
  exti->line = line;
}

void exti_external_interrupt(exti_t *exti, uint8_t gpio) {
  uint8_t index = exti->line >> 2;
  uint8_t pos = (exti->line & 0x3) * 4;
  volatile uint32_t *exticr = exti->syscfg->EXTICR;
  reg_write_bits_pos(exticr + index, gpio, pos, 0x0F);
}

void exti_rising_interrupt(exti_t *exti) {
  reg_write_bits_pos(&exti->exti->RTSR, 1, exti->line, 1);
  reg_write_bits_pos(&exti->exti->FTSR, 0, exti->line, 1);
  reg_write_bits_pos(&exti->exti->IMR, 1, exti->line, 1);
}

void exti_falling_interrupt(exti_t *exti) {
  reg_write_bits_pos(&exti->exti->FTSR, 1, exti->line, 1);
  reg_write_bits_pos(&exti->exti->RTSR, 0, exti->line, 1);
}

void exti_disable_interrupt(exti_t *exti) {
  reg_write_bits_pos(&exti->exti->IMR, 0, exti->line, 1);
}

IRQn_Type exti_irq_idx(uint8_t line) {
  switch (line) {
  case 0:
    return EXTI0_IRQn;
  case 1:
    return EXTI1_IRQn;
  case 2:
    return EXTI2_IRQn;
  case 3:
    return EXTI3_IRQn;
  case 4:
    return EXTI4_IRQn;
  case 5:
  case 6:
  case 7:
  case 8:
  case 9:
    return EXTI9_5_IRQn;
  case 10:
  case 11:
  case 12:
  case 13:
  case 14:
  case 15:
    return EXTI15_10_IRQn;
  default:
    return -1;
  }
}

void exti_enable_interrupt(exti_t *exti) {
  reg_write_bits_pos(&exti->exti->IMR, 1, exti->line, 1);

  IRQn_Type idx = exti_irq_idx(exti->line);
  NVIC_SetPriority(idx, 2);
  NVIC_EnableIRQ(idx);
}

uint32_t exti_is_interrupt(exti_t *exti) {
  return exti->exti->PR & (EXTI_PR_PR0 << exti->line);
}

void exti_clear_interrupt(exti_t *exti) {
  exti->exti->PR |= (EXTI_PR_PR0 << exti->line);
}
