
#ifndef BINDING_H_INCLUDED
#define BINDING_H_INCLUDED

#include <stddef.h>
#include <stdint.h>

#include "kernel.h"


// The binding table - the editable, serialisable representation of a slot's
// whole mapping. A binding is one "this source drives that target" record; a
// slot's mapping is a flat list of them. The table is the SOURCE OF TRUTH;
// the resolved fields the hot decode path reads (mapped_x_16,
// mapped_buttons[], key_*, mouse_*, axes[].flip - see struct joydata_struct)
// are a COMPILED form, expanded from the table by binding_compile(). See
// doc.Bindings for the full model (record layout, singular vs shareable
// targets, how Invert interacts with axes[].flip).


// --- source_type ---
enum {
  JB_SRC_AXIS   = 0,   // source_index is a physical axis index (0..num_axes-1)
  JB_SRC_BUTTON = 1    // source_index is a physical button index (0..num_buttons-1)
};

// --- target_kind, and what target_index means for each ---
enum {
  JB_TGT_NONE       = 0,  // index unused - never stored, transient only
  JB_TGT_SLOT_AXIS  = 1,  // index: 0=8bitX 1=8bitY 2=16bitX 3=16bitY
  JB_TGT_SLOT_BTN   = 2,  // index: 0..JOY_BUTTONS-1  slot button
  JB_TGT_MOUSE_MOVE = 3,  // index: 0=X 1=Y
  JB_TGT_MOUSE_BTN  = 4,  // index: 0=select 1=menu 2=adjust
  JB_TGT_KEY        = 5,  // index: RISC OS internal KeyNo (why index is 16-bit)
  JB_TGT_ADC        = 6,  // index: 0..ADC_CHANNELS-1  ADC channel (analogue)
  JB_TGT_ADC_BTN    = 7   // index: 0..ADC_BUTTONS-1   ADC fire button (digital)
};

// slot-axis target_index values (JB_TGT_SLOT_AXIS)
enum { JB_AXIS_8X = 0, JB_AXIS_8Y = 1, JB_AXIS_16X = 2, JB_AXIS_16Y = 3 };

// mouse-move target_index values (JB_TGT_MOUSE_MOVE)
enum { JB_MOUSE_X = 0, JB_MOUSE_Y = 1 };

// mouse-button target_index values (JB_TGT_MOUSE_BTN)
enum { JB_MOUSE_SELECT = 0, JB_MOUSE_MENU = 1, JB_MOUSE_ADJUST = 2 };

// --- flags ---
#define JB_FLAG_INVERT  0x01u   // reverse direction (the old "flip")
#define JB_FLAG_KNOWN   (JB_FLAG_INVERT)   // any bit outside this is rejected


// One binding: 12 bytes, word-aligned, no padding.
typedef struct {
  uint8_t  source_type;   // JB_SRC_*
  uint8_t  source_index;  // physical axis or button number
  uint8_t  target_kind;   // JB_TGT_*
  uint8_t  flags;         // JB_FLAG_*; unknown bits rejected at write time
  uint16_t target_index;  // meaning depends on target_kind (holds KeyNo for JB_TGT_KEY)
  uint16_t reserved;      // 0 - keeps the record 12 bytes and word-aligned
  uint32_t param;         // 0 for now; reserved for future scale/sensitivity
} joy_binding;


// Serialised table header, written to / read from the config store. magic +
// version up front so a future reader can validate and evolve the format.
#define JB_MAGIC    0x50414D4Au   // 'JMAP' little-endian
#define JB_VERSION  1u

typedef struct {
  uint32_t    magic;      // JB_MAGIC
  uint16_t    version;    // JB_VERSION
  uint16_t    count;      // number of valid entries following
  joy_binding entries[1]; // [count] - flexible tail
} joy_binding_map;


// Per-slot in-core storage cap. Comfortably exceeds JOY_AXES targets plus
// JOY_BUTTONS, with room for one source driving several targets.
#define JB_MAX_BINDINGS  128


// Validate a whole incoming table against a slot's real capabilities BEFORE it
// touches live state: header sanity, per-record range checks (indexed by the
// device's actual num_axes/num_buttons), reserved/flag hygiene, and the
// singular-target overlap rule. Returns NULL if clean, else an error and the
// live mapping is left untouched. count is the number of entries in entries[].
_kernel_oserror *binding_validate(uint32_t slot, const joy_binding *entries, uint32_t count);

// Validate one candidate binding against the slot's CURRENT table (range +
// the singular-target rule vs. what's already bound). Backs *USBJoystick_Map /
// Joystick_AddBinding. Returns NULL if it may be added.
_kernel_oserror *binding_validate_one(uint32_t slot, const joy_binding *b);

// Expand the slot's stored binding table into the resolved fields the decode
// path reads (clears them first). Called after any table change. Never fails -
// the table was validated on the way in.
void binding_compile(uint32_t slot);

// The inverse: rebuild the binding table FROM the current resolved fields, so a
// mapping produced the old way (auto_map / config overlay) is visible through
// Joystick_ReadBindings. Does not recompile.
void binding_capture(uint32_t slot);

// Replace the slot's table wholesale: validate, copy in, compile. (Set path.)
_kernel_oserror *binding_write(uint32_t slot, const joy_binding *entries, uint32_t count);

// Append one binding to the slot's table: validate against current, add,
// compile.
_kernel_oserror *binding_add(uint32_t slot, const joy_binding *b);

// Drop bindings from the slot's table and recompile. If src_type < 0, clears
// the whole table; otherwise clears just entries for that one source.
void binding_clear(uint32_t slot, int32_t src_type, int32_t src_index);

// Drop just the binding(s) matching *match on source AND target (kind+index),
// and recompile. Used to remove one specific input->output binding.
void binding_remove(uint32_t slot, const joy_binding *match);


// SWI wrappers (register calling convention) - see h.joyswis for numbers.
_kernel_oserror *swi_joystick_read_bindings(_kernel_swi_regs *r);
_kernel_oserror *swi_joystick_write_bindings(_kernel_swi_regs *r);
_kernel_oserror *swi_joystick_add_binding(_kernel_swi_regs *r);
_kernel_oserror *swi_joystick_read_default_bindings(_kernel_swi_regs *r);

// *USBJoystick_* command wrappers (string args)
_kernel_oserror *command_map(const char *args, int32_t argc);
_kernel_oserror *command_unmap(const char *args, int32_t argc);
_kernel_oserror *command_mappings(const char *args, int32_t argc);

// Shared text<->binding helpers (c.cmdbind), reused by the config store so its
// bind= lines use the exact *USBJoystick_Map grammar. parse_spec modifies the
// token buffers in place (NUL at the ':'); the formatters emit canonical tokens.
_kernel_oserror *binding_parse_spec(uint32_t slot, char *src_tok, char *tgt_tok,
                                    int invert, joy_binding *b);
void binding_format_source(const joy_binding *b, uint32_t slot, char *buf, size_t buflen);
void binding_format_target(const joy_binding *b, char *buf, size_t buflen);

#endif
