/******	fake.c ***********************************************************\

Synthetic joystick device(s) for development/testing without real USB
hardware. See h.fake for the rationale. Guarded by USBJOY_FAKE_DEVICE so the
whole file compiles to just the (empty) fake_devices_create when disabled.

\**************************************************************************/

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

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

#include "device.h"
#include "binding.h"
#include "fake.h"


#if USBJOY_FAKE_DEVICE

/* Axis names; the 6-axis pad uses the first six, the 16-axis rig all sixteen. */
static const char *const fake_axis_names [16] =
  { "X", "Y", "Z", "RX", "RY", "RZ", "U", "V",
    "Slider0", "Slider1", "Dial0", "Dial1", "Hat0X", "Hat0Y", "Hat1X", "Hat1Y" };

/* Starter binding tables, so a freshly-injected device shows some mappings to
   edit (as well as free targets to add). */
static const joy_binding fake_binds_a [] = {
  { JB_SRC_AXIS, 0, JB_TGT_MOUSE_MOVE, 0,              JB_MOUSE_X,  0, 0 },
  { JB_SRC_AXIS, 1, JB_TGT_MOUSE_MOVE, JB_FLAG_INVERT, JB_MOUSE_Y,  0, 0 },
  { JB_SRC_AXIS, 0, JB_TGT_SLOT_AXIS,  0,              JB_AXIS_16X, 0, 0 },
  { JB_SRC_AXIS, 1, JB_TGT_SLOT_AXIS,  0,              JB_AXIS_16Y, 0, 0 },
  { JB_SRC_AXIS, 4, JB_TGT_SLOT_AXIS,  0,              JB_AXIS_8X,  0, 0 },
  { JB_SRC_AXIS, 5, JB_TGT_SLOT_AXIS,  0,              JB_AXIS_8Y,  0, 0 }
};
static const joy_binding fake_binds_b [] = {
  { JB_SRC_AXIS, 0,  JB_TGT_SLOT_AXIS, 0, JB_AXIS_16X, 0, 0 },
  { JB_SRC_AXIS, 1,  JB_TGT_SLOT_AXIS, 0, JB_AXIS_16Y, 0, 0 },
  { JB_SRC_AXIS, 15, JB_TGT_KEY,       0, 65,          0, 0 }	/* Hat1Y -> key 65 */
};

struct fake_desc {
  const char   *manuf, *product, *serial;
  uint32_t      vid, pid, did;
  uint32_t      num_axes, num_buttons;
  const joy_binding *binds;
  uint32_t      nbinds;
};

static const struct fake_desc fake_devs [] = {
  { "Fake", "6-axis pad",  "TEST0001", 0xdead, 0xbeef, 1,  6, 11, fake_binds_a, 6 },
  { "Fake", "16-axis rig", "TEST0002", 0xdead, 0xbeef, 2, 16, 11, fake_binds_b, 3 }
};
#define NUM_FAKE	((uint32_t) (sizeof fake_devs / sizeof fake_devs [0]))


static void create_one (const struct fake_desc *fd)
{
  struct joydata_struct *j;
  uint32_t slot, a;

  for (slot = 0; slot < JOY_MAX; slot++)
    if (!joy_data[slot].in_use) break;
  if (slot >= JOY_MAX) return;			/* no free slot */

  j = &joy_data[slot];				/* zeroed by clean_joystick_data at init */

  j->in_use = TRUE;
  j->open   = TRUE;				/* no real fp; the UpCall poll matches on fp, ours is 0 */
  j->fp     = 0;
  j->device = NULL;

  strncpy (j->manufacturer, fd->manuf,   USB_MAX_STRING_LEN - 1);
  strncpy (j->product,      fd->product, USB_MAX_STRING_LEN - 1);
  strncpy (j->serial,       fd->serial,  USB_MAX_STRING_LEN - 1);
  j->vendor_id  = fd->vid;
  j->product_id = fd->pid;
  j->device_id  = fd->did;

  j->num_axes    = fd->num_axes;
  j->num_buttons = fd->num_buttons;
  for (a = 0; a < fd->num_axes && a < JOY_AXES; a++) {
    j->axes[a].min = -128;
    j->axes[a].mid = 0;
    j->axes[a].max = 127;
    j->axes[a].val = 0;
    j->axes[a].is_digital = FALSE;
    j->axes[a].name = (char *) fake_axis_names[a];
    j->axes[a].type = a;			/* arbitrary distinct type */
    j->axes[a].flip = FALSE;
  }

  j->mapped_number = NOT_MAPPED;			/* no legacy stick assigned by default */

  /* Seed the binding table (validates + compiles). Fake data is known-good, so
     an error here would be a bug in this file - log and carry on. */
  {
    _kernel_oserror *e = binding_write (slot, fd->binds, fd->nbinds);
    if (e) debug_printf ("fake: binding_write failed for slot %u: %s\n", slot, e->errmess);
  }

  joy_generation++;
  debug_printf ("fake: created slot %u (%s %s), %u axes, %u buttons\n",
                slot, fd->manuf, fd->product, fd->num_axes, fd->num_buttons);
}

#endif /* USBJOY_FAKE_DEVICE */


void fake_devices_create (void)
{
#if USBJOY_FAKE_DEVICE
  uint32_t i;
  for (i = 0; i < NUM_FAKE; i++)
    create_one (&fake_devs[i]);
#endif
}
