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

#include "debugrep.h"
#include "usbjoystick.h"
#include "errors.h"
#include "joyhelp.h"     // validate_joystick_slot*, round_up_down
#include "adc.h"         // adc_map, ADC_CHANNELS, ADC_BUTTONS, ADC_AXIS_VALUES
#include "map.h"         // binding_compute_auto_map

#include "binding.h"


// The mapping engine. The binding table (joy_data[slot].bindings) is the
// source of truth; binding_compile() expands it into the resolved fields the
// decode path reads. Validation happens on the way in, so compile never fails.
// See h.binding for the model and the v1 decisions (per-axis invert, axis->key
// via the stick's directional keys, full-range ADC).


// ---------------------------------------------------------------------------
// Validation
// ---------------------------------------------------------------------------

// One shared "bad argument" error - the module reports every out-of-range /
// malformed mapping argument as Error_BadNoParms (see c.cmdmap), with the
// specifics going to the debug log. No new message tokens needed.
static _kernel_oserror *badarg(void)
{
  return geterror(Error_BadNoParms);
}


// Range/flag/reserved sanity for one record against the device's real caps.
// Does NOT check singular-target overlap - that needs the whole-table (or
// live-table) context and is done by the callers below.
static _kernel_oserror *check_record(struct joydata_struct *j, const joy_binding *b)
{
  if (b->flags & ~JB_FLAG_KNOWN) {
    debug_printf("binding: reserved flag bits set (%02x)\n", b->flags);
    return badarg();
  }
  if (b->reserved != 0) {
    debug_printf("binding: reserved field nonzero (%04x)\n", b->reserved);
    return badarg();
  }

  switch (b->source_type) {
    case JB_SRC_AXIS:
      if (b->source_index >= j->num_axes) {
        debug_printf("binding: axis %u out of range (num_axes %u)\n", b->source_index, j->num_axes);
        return badarg();
      }
      break;
    case JB_SRC_BUTTON:
      if (b->source_index >= j->num_buttons) {
        debug_printf("binding: button %u out of range (num_buttons %u)\n", b->source_index, j->num_buttons);
        return badarg();
      }
      break;
    default:
      debug_printf("binding: bad source_type %u\n", b->source_type);
      return badarg();
  }

  switch (b->target_kind) {
    case JB_TGT_SLOT_AXIS:  if (b->target_index >= 4)           return badarg(); break;
    case JB_TGT_SLOT_BTN:   if (b->target_index >= JOY_BUTTONS) return badarg(); break;
    case JB_TGT_MOUSE_MOVE: if (b->target_index >= 2)           return badarg(); break;
    case JB_TGT_MOUSE_BTN:  if (b->target_index >= 3)           return badarg(); break;
    case JB_TGT_KEY:        /* any 16-bit KeyNo accepted */              break;
    case JB_TGT_ADC:        if (b->target_index >= ADC_CHANNELS) return badarg(); break;
    case JB_TGT_ADC_BTN:    if (b->target_index >= ADC_BUTTONS)  return badarg(); break;
    case JB_TGT_NONE:
      debug_printf("binding: JB_TGT_NONE should not be stored\n");
      return badarg();
    default:
      debug_printf("binding: bad target_kind %u\n", b->target_kind);
      return badarg();
  }

  // A button is a digital (on/off) source and can't drive an analogue output:
  // the engine records the driving source as an axis index (mapped_x_16 etc.),
  // so a button index there is meaningless. Reject rather than store a binding
  // that would silently misbehave.
  if (b->source_type == JB_SRC_BUTTON &&
      (b->target_kind == JB_TGT_SLOT_AXIS ||
       b->target_kind == JB_TGT_MOUSE_MOVE ||
       b->target_kind == JB_TGT_ADC)) {
    debug_printf("binding: button source can't drive analogue target (kind %u)\n", b->target_kind);
    return badarg();
  }

  return NULL;
}


// Is this target a SINGULAR (analogue) output - at most one source may drive
// it? Returns 1 and fills bucket/idx for the occupancy check; 0 for shareable
// (digital) targets, which may be OR-ed from many sources.
static int singular_target(const joy_binding *b, int *bucket, int *idx)
{
  switch (b->target_kind) {
    case JB_TGT_SLOT_AXIS:  *bucket = 0; *idx = b->target_index; return 1;
    case JB_TGT_MOUSE_MOVE: *bucket = 1; *idx = b->target_index; return 1;
    case JB_TGT_ADC:        *bucket = 2; *idx = b->target_index; return 1;
    default:                return 0;
  }
}


_kernel_oserror *binding_validate(uint32_t slot, const joy_binding *entries, uint32_t count)
{
  _kernel_oserror *err = validate_joystick_slot_active("Joystick_WriteBindings", slot);
  if (err) return err;

  if (count > JB_MAX_BINDINGS) {
    debug_printf("binding: too many bindings (%u > %u)\n", count, JB_MAX_BINDINGS);
    return badarg();
  }

  struct joydata_struct *j = &joy_data[slot];

  // Occupancy for the three singular target families; buckets: 0 slot-axis
  // (4), 1 mouse-move (2), 2 ADC channel (ADC_CHANNELS).
  uint8_t used[3][ADC_CHANNELS > 4 ? ADC_CHANNELS : 4];
  memset(used, 0, sizeof(used));

  for (uint32_t i = 0; i < count; i++) {
    err = check_record(j, &entries[i]);
    if (err) return err;

    int bucket, idx;
    if (singular_target(&entries[i], &bucket, &idx)) {
      if (used[bucket][idx]) {
        debug_printf("binding: singular target (kind %u index %d) driven twice\n",
                     entries[i].target_kind, idx);
        return badarg();
      }
      used[bucket][idx] = 1;
    }
  }

  return NULL;
}


_kernel_oserror *binding_validate_one(uint32_t slot, const joy_binding *b)
{
  _kernel_oserror *err = validate_joystick_slot_active("Joystick_AddBinding", slot);
  if (err) return err;

  struct joydata_struct *j = &joy_data[slot];

  err = check_record(j, b);
  if (err) return err;

  int bucket, idx;
  if (singular_target(b, &bucket, &idx)) {
    // reject if the CURRENT table already drives this singular output
    for (uint32_t i = 0; i < j->num_bindings; i++) {
      int obucket, oidx;
      if (singular_target(&j->bindings[i], &obucket, &oidx) &&
          obucket == bucket && oidx == idx) {
        debug_printf("binding: singular target (kind %u index %d) already driven\n",
                     b->target_kind, idx);
        return badarg();
      }
    }
  }

  return NULL;
}


// ---------------------------------------------------------------------------
// Compile: binding table -> resolved fields
// ---------------------------------------------------------------------------

static void compile_adc_channel(uint32_t slot, uint32_t ch, uint32_t axis, int invert)
{
  struct joydata_struct *j = &joy_data[slot];
  int32_t mn = j->axes[axis].min;
  int32_t mx = j->axes[axis].max;
  int32_t range = mx - mn;

  if (range <= 0) {
    debug_printf("binding: ADC axis %u has empty range, skipping\n", axis);
    return;
  }

  adc_map.channels[ch].joy_id    = slot;
  adc_map.channels[ch].axis      = axis;
  adc_map.channels[ch].start     = mn;
  adc_map.channels[ch].end       = mx;
  adc_map.channels[ch].flip      = invert ? TRUE : FALSE;
  adc_map.channels[ch].slope_div = (range > ADC_AXIS_VALUES);

  if (adc_map.channels[ch].slope_div)
    adc_map.channels[ch].slope = round_up_down((float)range / (float)ADC_AXIS_VALUES);
  else
    adc_map.channels[ch].slope = round_up_down((float)ADC_AXIS_VALUES / (float)range);
}


void binding_compile(uint32_t slot)
{
  struct joydata_struct *j = &joy_data[slot];

  // 1. Reset every resolved field this engine owns to "unmapped".
  j->mapped_x_8 = j->mapped_y_8 = j->mapped_x_16 = j->mapped_y_16 = NOT_MAPPED;
  for (uint32_t b = 0; b < JOY_BUTTONS; b++)
    j->mapped_buttons[b] = NOT_MAPPED;
  j->mapped_mouse_x = j->mapped_mouse_y = NOT_MAPPED;
  j->mapped_mouse_select = j->mapped_mouse_menu = j->mapped_mouse_adjust = NOT_MAPPED;
  j->key_up = j->key_down = j->key_left = j->key_right = NOT_MAPPED;
  for (uint32_t b = 0; b < JOY_BUTTONS; b++)
    j->key_buttons[b] = NOT_MAPPED;
  for (uint32_t a = 0; a < j->num_axes; a++)
    j->axes[a].flip = FALSE;

  // Release any ADC channels/fire-buttons this slot currently owns; other
  // slots' ADC ownership is left alone.
  for (uint32_t c = 0; c < ADC_CHANNELS; c++)
    if (adc_map.channels[c].joy_id == (int32_t)slot) {
      adc_map.channels[c].joy_id = NOT_MAPPED;
      adc_map.channels[c].axis   = NOT_MAPPED;
    }
  for (uint32_t bt = 0; bt < ADC_BUTTONS; bt++)
    if (adc_map.buttons[bt].joy_id == (int32_t)slot) {
      adc_map.buttons[bt].joy_id = NOT_MAPPED;
      adc_map.buttons[bt].button = NOT_MAPPED;
    }

  // 2. Per-axis invert. axes[a].flip is read ONLY by the slot-axis and mouse
  //    paths, so only SLOT_AXIS / MOUSE_MOVE bindings feed it. ADC carries its
  //    own channels[c].flip (set in compile_adc_channel) and axis->key invert
  //    is resolved in pass 4 - neither must touch axes[].flip, or it would
  //    wrongly invert this axis's slot/mouse mapping too.
  for (uint32_t i = 0; i < j->num_bindings; i++) {
    const joy_binding *b = &j->bindings[i];
    if ((b->flags & JB_FLAG_INVERT) && b->source_type == JB_SRC_AXIS &&
        b->source_index < j->num_axes &&
        (b->target_kind == JB_TGT_SLOT_AXIS || b->target_kind == JB_TGT_MOUSE_MOVE))
      j->axes[b->source_index].flip = TRUE;
  }

  // 3. Apply every target EXCEPT axis->key (which needs the 8-bit stick axes
  //    resolved first, so it waits for pass 4).
  for (uint32_t i = 0; i < j->num_bindings; i++) {
    const joy_binding *b = &j->bindings[i];
    uint32_t src = b->source_index;

    switch (b->target_kind) {
      case JB_TGT_SLOT_AXIS:
        switch (b->target_index) {
          case JB_AXIS_8X:  j->mapped_x_8  = src; break;
          case JB_AXIS_8Y:  j->mapped_y_8  = src; break;
          case JB_AXIS_16X: j->mapped_x_16 = src; break;
          case JB_AXIS_16Y: j->mapped_y_16 = src; break;
        }
        break;

      case JB_TGT_SLOT_BTN:
        j->mapped_buttons[b->target_index] = src;
        break;

      case JB_TGT_MOUSE_MOVE:
        if (b->target_index == JB_MOUSE_X) j->mapped_mouse_x = src;
        else                               j->mapped_mouse_y = src;
        break;

      case JB_TGT_MOUSE_BTN:
        switch (b->target_index) {
          case JB_MOUSE_SELECT: j->mapped_mouse_select = src; break;
          case JB_MOUSE_MENU:   j->mapped_mouse_menu   = src; break;
          case JB_MOUSE_ADJUST: j->mapped_mouse_adjust = src; break;
        }
        break;

      case JB_TGT_KEY:
        // button->key is direct; axis->key handled in pass 4.
        if (b->source_type == JB_SRC_BUTTON)
          j->key_buttons[src] = b->target_index;
        break;

      case JB_TGT_ADC:
        compile_adc_channel(slot, b->target_index, src, (b->flags & JB_FLAG_INVERT));
        break;

      case JB_TGT_ADC_BTN:
        adc_map.buttons[b->target_index].joy_id = slot;
        adc_map.buttons[b->target_index].button = src;
        break;

      default:
        break;
    }
  }

  // 4. axis->key. update_keys() drives the four directional keys from the
  //    8-bit stick axes only, so an axis->key binding takes effect only for
  //    whichever axis is mapped_x_8 / mapped_y_8. positive axis value =
  //    down/right; Invert selects the opposite half. (An axis->key on a third
  //    axis can't be driven by the current engine - documented, skipped.)
  for (uint32_t i = 0; i < j->num_bindings; i++) {
    const joy_binding *b = &j->bindings[i];
    if (b->target_kind != JB_TGT_KEY || b->source_type != JB_SRC_AXIS)
      continue;

    int32_t keyno  = (int32_t)b->target_index;
    int     invert = (b->flags & JB_FLAG_INVERT) != 0;

    if ((int32_t)b->source_index == j->mapped_x_8) {
      if (invert) j->key_left  = keyno;
      else        j->key_right = keyno;
    }
    else if ((int32_t)b->source_index == j->mapped_y_8) {
      if (invert) j->key_up   = keyno;
      else        j->key_down = keyno;
    }
    else {
      debug_printf("binding: axis->key on axis %u not a stick axis, skipped\n", b->source_index);
    }
  }
}


// ---------------------------------------------------------------------------
// Mutators
// ---------------------------------------------------------------------------

_kernel_oserror *binding_write(uint32_t slot, const joy_binding *entries, uint32_t count)
{
  _kernel_oserror *err = binding_validate(slot, entries, count);
  if (err) return err;

  struct joydata_struct *j = &joy_data[slot];
  if (count) memcpy(j->bindings, entries, count * sizeof(joy_binding));
  j->num_bindings = count;
  j->automap = FALSE;

  binding_compile(slot);
  return NULL;
}


_kernel_oserror *binding_add(uint32_t slot, const joy_binding *b)
{
  _kernel_oserror *err = binding_validate_one(slot, b);
  if (err) return err;

  struct joydata_struct *j = &joy_data[slot];
  if (j->num_bindings >= JB_MAX_BINDINGS) {
    debug_printf("binding: table full (%u)\n", j->num_bindings);
    return badarg();
  }

  j->bindings[j->num_bindings++] = *b;
  j->automap = FALSE;
  binding_compile(slot);
  return NULL;
}


void binding_clear(uint32_t slot, int32_t src_type, int32_t src_index)
{
  struct joydata_struct *j = &joy_data[slot];

  if (src_type < 0) {
    j->num_bindings = 0;
  }
  else {
    uint32_t w = 0;
    for (uint32_t r = 0; r < j->num_bindings; r++) {
      const joy_binding *b = &j->bindings[r];
      int match = (b->source_type == (uint8_t)src_type) &&
                  (b->source_index == (uint8_t)src_index);
      if (!match)
        j->bindings[w++] = *b;
    }
    j->num_bindings = w;
  }
  j->automap = FALSE;

  binding_compile(slot);
}


void binding_remove(uint32_t slot, const joy_binding *match)
{
  struct joydata_struct *j = &joy_data[slot];

  uint32_t w = 0;
  for (uint32_t r = 0; r < j->num_bindings; r++) {
    const joy_binding *b = &j->bindings[r];
    int same = b->source_type  == match->source_type  &&
               b->source_index == match->source_index &&
               b->target_kind  == match->target_kind  &&
               b->target_index == match->target_index;
    if (!same)
      j->bindings[w++] = *b;
  }
  j->num_bindings = w;
  j->automap = FALSE;

  binding_compile(slot);
}


// ---------------------------------------------------------------------------
// Capture: resolved fields -> binding table (the inverse of compile)
// ---------------------------------------------------------------------------

// Append one binding to the slot's table (bounds-checked).
static void cap_add(struct joydata_struct *j, uint8_t st, uint32_t si,
                    uint8_t tk, uint16_t ti, int invert)
{
  if (j->num_bindings >= JB_MAX_BINDINGS) return;
  joy_binding *b = &j->bindings[j->num_bindings++];
  b->source_type  = st;
  b->source_index = (uint8_t)si;
  b->target_kind  = tk;
  b->flags        = invert ? JB_FLAG_INVERT : 0;
  b->target_index = ti;
  b->reserved     = 0;
  b->param        = 0;
}

// Rebuild the slot's binding table FROM the current resolved fields, so a
// mapping produced the old way (auto_map, or a config overlay) is reflected by
// Joystick_ReadBindings. Does NOT recompile - the resolved fields are already
// what these bindings would compile back to (see the invert notes in
// h.binding: the directional-key + axes[].flip interaction is preserved).
void binding_capture(uint32_t slot)
{
  struct joydata_struct *j = &joy_data[slot];
  j->num_bindings = 0;

  // slot axes (invert = the source axis's flip, for slot/mouse targets)
  if (j->mapped_x_8  != NOT_MAPPED) cap_add(j, JB_SRC_AXIS, j->mapped_x_8,  JB_TGT_SLOT_AXIS, JB_AXIS_8X,  j->axes[j->mapped_x_8].flip);
  if (j->mapped_y_8  != NOT_MAPPED) cap_add(j, JB_SRC_AXIS, j->mapped_y_8,  JB_TGT_SLOT_AXIS, JB_AXIS_8Y,  j->axes[j->mapped_y_8].flip);
  if (j->mapped_x_16 != NOT_MAPPED) cap_add(j, JB_SRC_AXIS, j->mapped_x_16, JB_TGT_SLOT_AXIS, JB_AXIS_16X, j->axes[j->mapped_x_16].flip);
  if (j->mapped_y_16 != NOT_MAPPED) cap_add(j, JB_SRC_AXIS, j->mapped_y_16, JB_TGT_SLOT_AXIS, JB_AXIS_16Y, j->axes[j->mapped_y_16].flip);

  // slot buttons
  for (uint32_t b = 0; b < JOY_BUTTONS; b++)
    if (j->mapped_buttons[b] != (uint32_t)NOT_MAPPED)
      cap_add(j, JB_SRC_BUTTON, j->mapped_buttons[b], JB_TGT_SLOT_BTN, (uint16_t)b, 0);

  // mouse move / buttons
  if (j->mapped_mouse_x != NOT_MAPPED) cap_add(j, JB_SRC_AXIS, j->mapped_mouse_x, JB_TGT_MOUSE_MOVE, JB_MOUSE_X, j->axes[j->mapped_mouse_x].flip);
  if (j->mapped_mouse_y != NOT_MAPPED) cap_add(j, JB_SRC_AXIS, j->mapped_mouse_y, JB_TGT_MOUSE_MOVE, JB_MOUSE_Y, j->axes[j->mapped_mouse_y].flip);
  if (j->mapped_mouse_select != NOT_MAPPED) cap_add(j, JB_SRC_BUTTON, j->mapped_mouse_select, JB_TGT_MOUSE_BTN, JB_MOUSE_SELECT, 0);
  if (j->mapped_mouse_menu   != NOT_MAPPED) cap_add(j, JB_SRC_BUTTON, j->mapped_mouse_menu,   JB_TGT_MOUSE_BTN, JB_MOUSE_MENU,   0);
  if (j->mapped_mouse_adjust != NOT_MAPPED) cap_add(j, JB_SRC_BUTTON, j->mapped_mouse_adjust, JB_TGT_MOUSE_BTN, JB_MOUSE_ADJUST, 0);

  // keys: per-button, then the four directional keys off the 8-bit stick axes
  // (positive half = down/right; the opposite half is the Invert binding).
  for (uint32_t b = 0; b < JOY_BUTTONS; b++)
    if (j->key_buttons[b] != NOT_MAPPED)
      cap_add(j, JB_SRC_BUTTON, b, JB_TGT_KEY, (uint16_t)j->key_buttons[b], 0);

  if (j->mapped_x_8 != NOT_MAPPED) {
    if (j->key_right != NOT_MAPPED) cap_add(j, JB_SRC_AXIS, j->mapped_x_8, JB_TGT_KEY, (uint16_t)j->key_right, 0);
    if (j->key_left  != NOT_MAPPED) cap_add(j, JB_SRC_AXIS, j->mapped_x_8, JB_TGT_KEY, (uint16_t)j->key_left,  1);
  }
  if (j->mapped_y_8 != NOT_MAPPED) {
    if (j->key_down != NOT_MAPPED) cap_add(j, JB_SRC_AXIS, j->mapped_y_8, JB_TGT_KEY, (uint16_t)j->key_down, 0);
    if (j->key_up   != NOT_MAPPED) cap_add(j, JB_SRC_AXIS, j->mapped_y_8, JB_TGT_KEY, (uint16_t)j->key_up,   1);
  }

  // ADC channels / fire buttons owned by this slot
  for (uint32_t c = 0; c < ADC_CHANNELS; c++)
    if (adc_map.channels[c].joy_id == (int32_t)slot && adc_map.channels[c].axis != NOT_MAPPED)
      cap_add(j, JB_SRC_AXIS, adc_map.channels[c].axis, JB_TGT_ADC, (uint16_t)c, adc_map.channels[c].flip);
  for (uint32_t bt = 0; bt < ADC_BUTTONS; bt++)
    if (adc_map.buttons[bt].joy_id == (int32_t)slot)
      cap_add(j, JB_SRC_BUTTON, adc_map.buttons[bt].button, JB_TGT_ADC_BTN, (uint16_t)bt, 0);
}


// ---------------------------------------------------------------------------
// SWI wrappers (register calling convention)
// ---------------------------------------------------------------------------

// Joystick_ReadBindings
//   in:  R0 = slot
//   out: R0 = count, R1 = record size (bytes), R2 = pointer to entries[]
// Mirrors Joystick_GetMapping: returns a pointer to the module's own live
// table (no caller buffer). The block is valid until the next mapping change.
_kernel_oserror *swi_joystick_read_bindings(_kernel_swi_regs *r)
{
  uint32_t slot = (uint32_t) r->r[0];

  _kernel_oserror *err = validate_joystick_slot("Joystick_ReadBindings", slot);
  if (err) return err;
  err = validate_joystick_slot_active("Joystick_ReadBindings", slot);
  if (err) return err;

  struct joydata_struct *j = &joy_data[slot];

  r->r[0] = (int) j->num_bindings;
  r->r[1] = (int) sizeof(joy_binding);
  r->r[2] = (int) j->bindings;

  return NULL;
}


// Joystick_WriteBindings
//   in: R0 = slot, R1 = pointer to entries[], R2 = count
// Validates the whole table, then replaces + recompiles (all-or-nothing: on a
// bad record the live mapping is left untouched).
_kernel_oserror *swi_joystick_write_bindings(_kernel_swi_regs *r)
{
  uint32_t slot = (uint32_t) r->r[0];

  _kernel_oserror *err = validate_joystick_slot("Joystick_WriteBindings", slot);
  if (err) return err;

  return binding_write(slot, (const joy_binding *) r->r[1], (uint32_t) r->r[2]);
}


// Joystick_AddBinding
//   in: R0 = slot, R1 = pointer to one joy_binding
// Validates against the current table (range + singular-target rule), appends,
// recompiles.
_kernel_oserror *swi_joystick_add_binding(_kernel_swi_regs *r)
{
  uint32_t slot = (uint32_t) r->r[0];

  _kernel_oserror *err = validate_joystick_slot("Joystick_AddBinding", slot);
  if (err) return err;

  return binding_add(slot, (const joy_binding *) r->r[1]);
}


// Scratch buffer for Joystick_ReadDefaultBindings' computed table. Unlike
// ReadBindings (which points at the slot's own live joy_data[slot].bindings),
// this is a preview that was never written to any slot, so it has no live
// home - valid until the next ReadDefaultBindings call.
static joy_binding default_bindings_scratch[JB_MAX_BINDINGS];

// Joystick_ReadDefaultBindings
//   in:  R0 = slot
//   out: R0 = count, R1 = record size (bytes), R2 = pointer to entries[],
//        R3 = legacy stick number auto_map would assign right now
// Read-only preview of what Joystick_RevertToDefaultMap would produce - see
// h.joyswis and binding_compute_auto_map (c.map).
_kernel_oserror *swi_joystick_read_default_bindings(_kernel_swi_regs *r)
{
  uint32_t slot = (uint32_t) r->r[0];
  int32_t stick = NOT_MAPPED;

  _kernel_oserror *err = validate_joystick_slot("Joystick_ReadDefaultBindings", slot);
  if (err) return err;
  err = validate_joystick_slot_active("Joystick_ReadDefaultBindings", slot);
  if (err) return err;

  uint32_t count = binding_compute_auto_map(slot, default_bindings_scratch, JB_MAX_BINDINGS, &stick);

  r->r[0] = (int) count;
  r->r[1] = (int) sizeof(joy_binding);
  r->r[2] = (int) default_bindings_scratch;
  r->r[3] = (int) stick;

  return NULL;
}
