#include "dinput_app.h"

#include "main.h"
#include "usb_device.h"
#include "usbd_dinput.h"

typedef struct __attribute__((packed))
{
  uint16_t buttons;
  uint8_t hat;
  uint8_t axes[6];
} DInputReport;

_Static_assert(sizeof(DInputReport) == DINPUT_REPORT_SIZE,
               "DInput input report must be exactly 9 bytes");

extern USBD_HandleTypeDef hUsbDeviceFS;

static uint8_t IsPressed(GPIO_TypeDef *port, uint16_t pin)
{
  return (HAL_GPIO_ReadPin(port, pin) == GPIO_PIN_RESET) ? 1U : 0U;
}

static uint8_t ReadHat(void)
{
  uint8_t up = IsPressed(UP_GPIO_Port, UP_Pin);
  uint8_t down = IsPressed(DOWN_GPIO_Port, DOWN_Pin);
  uint8_t left = IsPressed(LEFT_GPIO_Port, LEFT_Pin);
  uint8_t right = IsPressed(RIGHT_GPIO_Port, RIGHT_Pin);

  if (up && right && !down && !left) return 1U;
  if (down && right && !up && !left) return 3U;
  if (down && left && !up && !right) return 5U;
  if (up && left && !down && !right) return 7U;
  if (up && !down && !left && !right) return 0U;
  if (right && !up && !down && !left) return 2U;
  if (down && !up && !left && !right) return 4U;
  if (left && !up && !down && !right) return 6U;
  return 8U;
}

void DInput_AppProcess(void)
{
  DInputReport report =
  {
    .buttons = 0U,
    .hat = 8U,
    .axes = {0x80U, 0x80U, 0x80U, 0x80U, 0x80U, 0x80U}
  };

  report.hat = ReadHat();
  if (IsPressed(X_GPIO_Port, X_Pin))           report.buttons |= (1U << 0);
  if (IsPressed(Y_GPIO_Port, Y_Pin))           report.buttons |= (1U << 1);
  if (IsPressed(A_GPIO_Port, A_Pin))           report.buttons |= (1U << 2);
  if (IsPressed(B_GPIO_Port, B_Pin))           report.buttons |= (1U << 3);
  if (IsPressed(RB_GPIO_Port, RB_Pin))         report.buttons |= (1U << 4);
  if (IsPressed(LB_GPIO_Port, LB_Pin))         report.buttons |= (1U << 5);
  if (IsPressed(RT_GPIO_Port, RT_Pin))         report.buttons |= (1U << 6);
  if (IsPressed(LT_GPIO_Port, LT_Pin))         report.buttons |= (1U << 7);
  if (IsPressed(Start_GPIO_Port, Start_Pin))   report.buttons |= (1U << 8);
  if (IsPressed(Select_GPIO_Port, Select_Pin)) report.buttons |= (1U << 9);

  (void)USBD_DINPUT_SendReport(&hUsbDeviceFS,
                               (uint8_t *)&report,
                               sizeof(report));
}
