/*
 * TMP144.c
 *
 *  Created on: 23 Mar 2022
 *      Author: Jasper
 */

#include "TMP144.h"

#include "stm32f4xx_hal.h"
#include "stm32f4xx_hal_def.h"
#include "stm32f4xx_hal_uart.h"

#include <stdint.h>
#include <string.h>

static const uint8_t TMP144_SEQ_RESET[] = {0x55, 0xB4};
static const uint8_t TMP144_SEQ_ADDR[] = {0x55, 0x8C, 0x90};
static const uint8_t TMP144_SEQ_READ_TEMPS[] = {0x55, 0xF1};

volatile uint16_t temperatures[N_TEMP_SENSORS];
volatile uint16_t min_temperature;
volatile uint16_t max_temperature;

volatile TMP144Bus tmp144_bus_busbar;
volatile TMP144Bus tmp144_bus_other;

#define CHECK_STATUS(s)                                                        \
  {                                                                            \
    HAL_StatusTypeDef _s = s;                                                  \
    if (_s != HAL_OK)                                                          \
      return _s;                                                               \
  }

HAL_StatusTypeDef tmp144_init(UART_HandleTypeDef* busbar_side,
                              UART_HandleTypeDef* other_side) {
  tmp144_bus_busbar.handle = busbar_side;
  tmp144_bus_other.handle = other_side;

  tmp144_bus_busbar.state = TMP144_IDLE;
  tmp144_bus_other.state = TMP144_IDLE;

  // TODO: Configure this in EEPROM
  tmp144_bus_busbar.n_sensors = N_TEMP_SENSORS / 2;
  tmp144_bus_other.n_sensors = N_TEMP_SENSORS / 2;

  CHECK_STATUS(tmp144_init_reset(&tmp144_bus_busbar));
  // CHECK_STATUS(tmp144_init_reset(&tmp144_bus_other));

  return HAL_OK;
}

HAL_StatusTypeDef tmp144_check_timeouts() {
  uint32_t now = HAL_GetTick();
  uint32_t t1 = tmp144_bus_busbar.expected_response_time;
  uint32_t t2 = tmp144_bus_other.expected_response_time;
  if (t1 != 0 && t1 < now) {
    tmp144_bus_busbar.state = TMP144_ERROR;
  }
  // FIXME
  // if (t2 != 0 && t2 < now) {
  //   tmp144_bus_other.state = TMP144_ERROR;
  // }
  return HAL_OK;
}

HAL_StatusTypeDef tmp144_init_reset(TMP144Bus* bus) {
  if (bus->state != TMP144_IDLE) {
    return HAL_ERROR;
  }

  bus->state = TMP144_RESETTING;
  CHECK_STATUS(HAL_UART_Receive_DMA(bus->handle, bus->rxbuf, 2));
  // Keep sending Global Software Reset until it echoes back (as per 7.5.2)
  int tries = 0;
  do {
    if (tries > bus->n_sensors) {
      bus->state = TMP144_ERROR;
      return HAL_TIMEOUT;
    }
    if (bus->handle->RxState == HAL_UART_STATE_READY &&
        bus->state == TMP144_RESETTING) {
      HAL_UART_DMAStop(bus->handle);
      CHECK_STATUS(HAL_UART_Receive_DMA(bus->handle, bus->rxbuf, 2));
    }
    CHECK_STATUS(HAL_UART_Transmit_IT(bus->handle, TMP144_SEQ_RESET,
                                      sizeof(TMP144_SEQ_RESET)));
    HAL_Delay(100);
    tries++;
  } while (bus->state == TMP144_RESETTING);

  bus->state = TMP144_INITIALIZING;
  CHECK_STATUS(HAL_UART_Receive_DMA(bus->handle, bus->rxbuf, 3));
  bus->expected_response_time = HAL_GetTick() + 100;
  CHECK_STATUS(HAL_UART_Transmit(bus->handle, TMP144_SEQ_ADDR,
                                 sizeof(TMP144_SEQ_ADDR), 100));

  return HAL_OK;
}

HAL_StatusTypeDef tmp144_init_post_reset(TMP144Bus* bus) {
  if (bus->state != TMP144_RESETTING ||
      memcmp(bus->rxbuf, TMP144_SEQ_RESET, sizeof(TMP144_SEQ_RESET)) != 0) {
    return HAL_ERROR;
  }

  bus->state = TMP144_IDLE;

  return HAL_OK;
}

HAL_StatusTypeDef tmp144_init_post_addr(TMP144Bus* bus) {
  size_t idx_response = sizeof(TMP144_SEQ_ADDR) - 1;
  if (bus->state != TMP144_INITIALIZING ||
      memcmp(bus->rxbuf, TMP144_SEQ_ADDR, idx_response) != 0) {
    return HAL_ERROR;
  }

  uint8_t n_sensors = bus->rxbuf[idx_response] - TMP144_SEQ_ADDR[idx_response];
  if (n_sensors != bus->n_sensors) {
    bus->state = TMP144_ERROR;
    return HAL_ERROR;
  }
  bus->state = TMP144_IDLE;

  return HAL_OK;
}

HAL_StatusTypeDef tmp144_read_temps() {
  CHECK_STATUS(tmp144_send_read_temps(&tmp144_bus_busbar));
  CHECK_STATUS(tmp144_send_read_temps(&tmp144_bus_other));

  return HAL_OK;
}

HAL_StatusTypeDef tmp144_send_read_temps(TMP144Bus* bus) {
  if (bus->state != TMP144_IDLE) {
    return HAL_ERROR;
  }

  bus->state = TMP144_READING_TEMP;
  CHECK_STATUS(
      HAL_UART_Receive_DMA(bus->handle, bus->rxbuf,
                           sizeof(TMP144_SEQ_READ_TEMPS) + 2 * bus->n_sensors));
  bus->expected_response_time = HAL_GetTick() + TMP144_RESPONSE_TIMEOUT;
  CHECK_STATUS(HAL_UART_Transmit(bus->handle, TMP144_SEQ_READ_TEMPS,
                                 sizeof(TMP144_SEQ_READ_TEMPS), 100));

  return HAL_OK;
}

HAL_StatusTypeDef tmp144_recv_temps(TMP144Bus* bus) {
  if (bus->state != TMP144_READING_TEMP) {
    return HAL_ERROR;
  }

  bus->state = TMP144_IDLE;
  size_t headerlen = sizeof(TMP144_SEQ_READ_TEMPS);
  if (memcmp(bus->rxbuf, TMP144_SEQ_READ_TEMPS, headerlen) != 0) {
    return HAL_ERROR;
  }

  size_t temperatures_offset =
      (bus == &tmp144_bus_busbar) ? 0 : N_TEMP_SENSORS / 2;
  for (size_t i = 0; i < bus->n_sensors; i++) {
    size_t buf_offset = headerlen + 2 * i;
    uint16_t temp =
        (bus->rxbuf[buf_offset] >> 4) | (bus->rxbuf[buf_offset + 1] << 4);
    if (temp == 0x7FF) {
      // Extremely unlikely, probably the sensor is just broken
      temp = 0;
    }
    temperatures[temperatures_offset + i] = temp;
  }

  uint16_t max = temperatures[0];
  uint16_t min = temperatures[0];
  // FIXME
  for (size_t i = 1; i < 16; i++) {
    if (temperatures[i] > max) {
      max = temperatures[i];
    }
    if (temperatures[i] < min) {
      min = temperatures[i];
    }
  }
  max_temperature = max;
  min_temperature = min;

  return HAL_OK;
}

void tmp144_handle_rx_cplt(UART_HandleTypeDef* handle) {
  TMP144Bus* bus;
  if (handle == tmp144_bus_busbar.handle) {
    bus = &tmp144_bus_busbar;
  } else if (handle == tmp144_bus_other.handle) {
    bus = &tmp144_bus_other;
  } else {
    // TODO
    Error_Handler();
  }

  bus->expected_response_time = 0;

  switch (bus->state) {
  case TMP144_IDLE:
    // TODO
    Error_Handler();
  case TMP144_RESETTING:
    tmp144_init_post_reset(bus);
    break;
  case TMP144_INITIALIZING:
    tmp144_init_post_addr(bus);
    break;
  case TMP144_READING_TEMP:
    tmp144_recv_temps(bus);
    break;
  default:
    // TODO
    Error_Handler();
  }
}