
//#include "swis.h"
#include <stdlib.h>
#include <stdint.h>

#include "debugrep.h"
#include "USBJoystickHdr.h"
#include "usbjoystick.h"

#include "device.h"
#include "map.h"
#include "config.h"


void auto_map(uint32_t i)
{
  if (joy_data[i].in_use == FALSE || joy_data[i].open == FALSE)
    return;

  // 8-bit axes mapping
  int mapped8 = find_and_map_x_y_8(i, AXIS_DPADX, AXIS_DPADY);

  if (!mapped8)
    mapped8 = find_and_map_x_y_8(i, AXIS_HATX, AXIS_HATY);

  if (!mapped8)
    mapped8 = find_and_map_x_y_8(i, AXIS_X, AXIS_Y);

  // 16-bit axes mapping
  int mapped16 = find_and_map_x_y_16(i, AXIS_X, AXIS_Y);

  if (!mapped16)
    mapped16 = find_and_map_x_y_8(i, AXIS_DPADX, AXIS_DPADY);

  if (!mapped16)
    mapped16 = find_and_map_x_y_8(i, AXIS_HATX, AXIS_HATY);

  // Auto flip any axes that this 'driver' wants us to
  for (uint32_t a=0; a<joy_data[i].device->auto_flip_axes_length; a++) {
    for (uint32_t j=0; j<joy_data[i].num_axes; j++) {
      if (joy_data[i].axes[j].type == joy_data[i].device->auto_flip_axes[a])
        joy_data[i].axes[j].flip = TRUE;
    }
  }

  // Automatically map the buttons
  uint32_t maxbutton = joy_data[i].num_buttons;

  for (uint32_t b=0; b<maxbutton; b++)
    joy_data[i].mapped_buttons[b] = b;

  // Automatically assign a 'stick number' based on the highest used so far.
  // Skip slot i itself: on fresh enumeration it's still NOT_MAPPED so this is
  // a no-op, but on a re-map (Joystick_RevertToDefaultMap) the slot already holds
  // its current number, and counting it would bump us one past ourselves.
  int32_t highest_stick = NOT_MAPPED;
  for (uint32_t j=0; j<JOY_MAX; j++) {
    if (j == i)
      continue;
    if (joy_data[j].mapped_number > highest_stick)
      highest_stick = joy_data[j].mapped_number;
  }

  if (highest_stick == NOT_MAPPED)
    joy_data[i].mapped_number = 0;
  else
    joy_data[i].mapped_number = highest_stick + 1;

  // Mouse control is opt-in: a device drives the pointer only when the user
  // binds axes/buttons to mouse:* targets, so the default mapping leaves the
  // mouse fields unmapped.

  // Capture the just-computed default mapping (incl. auto-flip) into the
  // binding table, so Joystick_ReadBindings reflects it even with no saved
  // config. See h.binding: the table mirrors the resolved fields for reading.
  binding_capture(i);
  joy_data[i].automap = TRUE;   // config_apply below clears this if it loads a saved mapping

  // Finally, apply any saved per-device mapping from the config store: this
  // REPLACES the binding table wholesale (binding_write) and recompiles - so
  // it must run after the capture above. Does nothing if there's no config
  // file, no matching section, or the section is automap.
  config_apply(i);
}





void unmap(uint32_t i)
{
  // don't need to do anything if we were not mapped
  if (joy_data[i].mapped_number == NOT_MAPPED)
    return;

  // decrement any higher mapped stick numbers
  int32_t old_map = joy_data[i].mapped_number;
  joy_data[i].mapped_number = NOT_MAPPED;

  for (uint32_t j=0; j<JOY_MAX; j++) {
    if (joy_data[j].mapped_number > old_map)
      joy_data[j].mapped_number--;
  }

}





// Shared search: the first axis of type x_type/y_type on slot i, or
// NOT_MAPPED for either. Used by both the live 8/16-bit mappers below and the
// read-only binding_compute_auto_map preview, so the search order can't drift
// between the two.
static int pick_x_y(uint32_t i, uint32_t x_type, uint32_t y_type, int32_t *x_out, int32_t *y_out)
{
  int32_t x = NOT_MAPPED;
  int32_t y = NOT_MAPPED;

  for (uint32_t j=0; j<joy_data[i].num_axes; j++) {
    if (joy_data[i].axes[j].type == x_type)
      x = j;
    else if (joy_data[i].axes[j].type == y_type)
      y = j;
  }

  if (x != NOT_MAPPED && y != NOT_MAPPED) {
    *x_out = x;
    *y_out = y;
    return true;
  }
  else {
    return false;
  }
}


int find_and_map_x_y_8(uint32_t i, uint32_t x_type, uint32_t y_type)
{
  int32_t x, y;

  if (!pick_x_y(i, x_type, y_type, &x, &y))
    return false;

  joy_data[i].mapped_x_8 = x;
  joy_data[i].mapped_y_8 = y;
  return true;
}




int find_and_map_x_y_16(uint32_t i, uint32_t x_type, uint32_t y_type)
{
  int32_t x, y;

  if (!pick_x_y(i, x_type, y_type, &x, &y))
    return false;

  joy_data[i].mapped_x_16 = x;
  joy_data[i].mapped_y_16 = y;
  return true;
}




// See h.map. Mirrors auto_map()'s axis-pair/flip/button selection exactly
// (via the shared pick_x_y above), but reads only static device shape data
// (num_axes, axes[].type, num_buttons, device->auto_flip_axes) and never
// writes to joy_data, so it has no live state to leave dirty if the caller's
// draft is later discarded.
uint32_t binding_compute_auto_map(uint32_t i, joy_binding *out, uint32_t max, int32_t *out_stick)
{
  uint32_t n = 0;

  if (out_stick) *out_stick = NOT_MAPPED;

  if (joy_data[i].in_use == FALSE || joy_data[i].open == FALSE)
    return 0;

  int32_t x8 = NOT_MAPPED, y8 = NOT_MAPPED, x16 = NOT_MAPPED, y16 = NOT_MAPPED;

  if (!pick_x_y(i, AXIS_DPADX, AXIS_DPADY, &x8, &y8))
    if (!pick_x_y(i, AXIS_HATX, AXIS_HATY, &x8, &y8))
      pick_x_y(i, AXIS_X, AXIS_Y, &x8, &y8);

  if (!pick_x_y(i, AXIS_X, AXIS_Y, &x16, &y16))
    if (!pick_x_y(i, AXIS_DPADX, AXIS_DPADY, &x16, &y16))
      pick_x_y(i, AXIS_HATX, AXIS_HATY, &x16, &y16);

  // Same auto-flip list as auto_map() - static device shape data, so this is
  // safe to read even though no draft/live mapping has been touched.
  int flip[JOY_AXES] = { 0 };
  for (uint32_t a=0; a<joy_data[i].device->auto_flip_axes_length; a++)
    for (uint32_t j=0; j<joy_data[i].num_axes; j++)
      if (joy_data[i].axes[j].type == joy_data[i].device->auto_flip_axes[a])
        flip[j] = TRUE;

  struct { int32_t axis; uint16_t target_index; } slot_axes[4] = {
    { x8,  JB_AXIS_8X  }, { y8,  JB_AXIS_8Y  },
    { x16, JB_AXIS_16X }, { y16, JB_AXIS_16Y }
  };
  for (int k = 0; k < 4; k++)
  {
    if (slot_axes[k].axis == NOT_MAPPED || n >= max) continue;
    out[n].source_type  = JB_SRC_AXIS;
    out[n].source_index = (uint8_t) slot_axes[k].axis;
    out[n].target_kind  = JB_TGT_SLOT_AXIS;
    out[n].target_index = slot_axes[k].target_index;
    out[n].flags        = flip[slot_axes[k].axis] ? JB_FLAG_INVERT : 0;
    out[n].reserved     = 0;
    out[n].param        = 0;
    n++;
  }

  for (uint32_t b=0; b<joy_data[i].num_buttons && n < max; b++)
  {
    out[n].source_type  = JB_SRC_BUTTON;
    out[n].source_index = (uint8_t) b;
    out[n].target_kind  = JB_TGT_SLOT_BTN;
    out[n].target_index = (uint16_t) b;
    out[n].flags        = 0;
    out[n].reserved     = 0;
    out[n].param        = 0;
    n++;
  }

  // Same "next number after the highest currently in use" rule as auto_map()
  // (slot i itself is skipped so a re-preview doesn't count its own number).
  if (out_stick)
  {
    int32_t highest = NOT_MAPPED;
    for (uint32_t j=0; j<JOY_MAX; j++) {
      if (j == i) continue;
      if (joy_data[j].mapped_number > highest)
        highest = joy_data[j].mapped_number;
    }
    *out_stick = (highest == NOT_MAPPED) ? 0 : highest + 1;
  }

  return n;
}


