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

#include "swis.h"
#include "Global/RISCOS.h"

#include "oslib/osbyte.h"

#include "debugrep.h"
#include "usbjoystick.h"
#include "usbjoystickHdr.h"

#include "adc.h"


osbool emulate_adc_on = TRUE;



int32_t bytev_hook_handler(_kernel_swi_regs *r, void *pw)
{
  IGNORE(pw);

  // an application is trying to use osbyte to perform an I/O podule ADC conversion

  if (!emulate_adc_on)
    return VECTOR_PASSON;

  uint32_t value = 0;
  IGNORE(value);     // not used all the time!

  switch ((osbyte_op)(r->r[0]))
  {
    case osbyte_CHANNEL_COUNT:
    {
      // OSBYTE 16 - select how many channels are to be sampled
      //debug_printf("bytev_hook_handler: OSByte 16\n");
      adc_data.channels_to_be_sampled = r->r[1];
      return VECTOR_CLAIM;
    }

    case osbyte_CONVERT_CHANNEL:
    {
      // OSBYTE 17 - force ADC conversion (we just set current channel, no need to 'force')
      if (r->r[1] >= 1 && r->r[1] <= 4) {
        //debug_printf("bytev_hook_handler: OSByte 17 (valid channel)\n");
        adc_data.current_channel = r->r[1];
        return VECTOR_CLAIM;
      }
      //debug_printf("bytev_hook_handler: OSByte 17 (invalid channel)\n");
      break;
    }

    case osbyte_BUFFER_OP:
    {
      // OSBYTE 128 - read ADC channel value and fire button status
      // BASIC's ADVAL() function uses this
      // TODO: Add cases for 7/8 which is mouse x/y position
      int32_t value = 0;
      //debug_printf("bytev_hook_handlerOSByte 128 Buffer Op, r1 = %u\n", r->r[1]);
      switch (r->r[1])
      {
        case osbyte_OP_JOYSTICK_STATE:
        {
          // OSByte 128 with r1=0
          // hack: setting r2 to != 0 causes output of ADVAL(0) to be screwed up, so fix to 0
          //debug_printf("bytev_hook_handler: OSByte 128 Read joystick state");
          r->r[1] = adc_read_fire();   // bits 0-1 should be button status
          r->r[2] = 0;                 // channel number last used for conversion (or 0=none)
          return VECTOR_CLAIM;
        }

        case osbyte_OP_CHANNEL_POSITION1:
        case osbyte_OP_CHANNEL_POSITION2:
        case osbyte_OP_CHANNEL_POSITION3:
        case osbyte_OP_CHANNEL_POSITION4:
        {
          // OSByte 128 with r1=(1/2/3/4)
          //debug_printf("bytev_hook_handler: OSByte 128 Read ADC channel");
          value = adc_read_channel(r->r[1]);
          r->r[1] = (value & 0xFF) ;  // bits 0-7 should be low 8-bits
          r->r[2] = (value >> 8);     // bits 0-23 should be high 24 bits
          return VECTOR_CLAIM;
        }

        case osbyte_OP_POINTER_XPOSITION:
        case osbyte_OP_POINTER_YPOSITION:
        {
          //debug_printf("bytev_hook_handler: OSByte 128 Read pointer position - not implemented, will pass-on");
          // todo: is this wise?  maybe we should read position via OS_Mouse and return it?
          return VECTOR_PASSON;
        }

        //debug_printf("bytev_hook_handler: OSByte 128, unimplemented reason code %u", r->r[1]);
        //break;
      }
      debug_printf("bytev_hook_handler: OSByte 128, not a buffer op we care about");
      break;
    }

    case osbyte_VAR_CURRENT_CHANNEL:
    {
      // OSBYTE 188 - read current ADC channel number
      debug_printf("bytev_hook_handler: OSByte 188\n");
      r->r[1] = adc_data.current_channel;
      return VECTOR_CLAIM;
    }

    case osbyte_VAR_CHANNEL_COUNT:
    {
      // OSBYTE 189 - read maximum ADC channel number (as set by osbyte 16)
      debug_printf("bytev_hook_handler: OSByte 189\n");
      r->r[1] = adc_data.channels_to_be_sampled;
      return VECTOR_CLAIM;
    }

    case osbyte_VAR_CONVERSION_RESOLUTION:
    {
      // OSBYTE 190 - read resolution of conversion: 12 (r0=0 or 12) or 8-bit (r0=8)
      debug_printf("bytev_hook_handler: OSByte 190\n");
      r->r[1] = 12;
      return VECTOR_CLAIM;
    }

  }

  // bytev reason code/subcode was not for us
  return VECTOR_PASSON;
}





uint32_t adc_read_channel(uint32_t channel)
{
  int32_t value = 0;

  // channel passed in is 1/2/3/4  (0 shouldn't be passed in, should it?!)
  struct adc_channel_struct *c = &(adc_map.channels[channel-1]);

  if (c->joy_id == NOT_MAPPED || c->axis == NOT_MAPPED)
    return value;

  struct axisdata_struct *a = &(joy_data[c->joy_id].axes[c->axis]);

  if (a->is_digital) {
    // todo: does this make any sense when ranges are used?!
    if (a->val == a->min)
      value = ADC_AXIS_HAT_MIN;
    else if (a->val == a->max)
      value = ADC_AXIS_HAT_MAX;
    else
      value = ADC_AXIS_HAT_MID;
  }
  else {
    if (a->val >= c->start && a->val <= c->end) {
      // axis is presenting a value in the range we care about
      // scale our input from the user-secified range to the ADC range
      if (c->slope_div)
        value = ADC_AXIS_MIN_VALUE + ((a->val - c->start) / c->slope);
      else
        value = ADC_AXIS_MIN_VALUE + (c->slope * (a->val - c->start));
      // this is required as scaling isn't terribly accurate: it may under/over-shoot
      if (value > ADC_AXIS_MAX_VALUE)
        value = ADC_AXIS_MAX_VALUE;
      else if (value < ADC_AXIS_MIN_VALUE)
        value = ADC_AXIS_MIN_VALUE;
    }
    else if (a->val < c->start) {
      // axis is presenting a value before our range, so show min
      value = ADC_AXIS_MIN_VALUE;
    }
    else if (a->val > c->end) {
      // axis is presenting a value after our range, so show max
      value = ADC_AXIS_MAX_VALUE;
    }
  }

  // do we need to flip?
  if (c->flip) {
    value = ADC_AXIS_MAX_VALUE - value;
  }

  return value;
}





uint32_t adc_read_fire(void)
{
  uint32_t buttons = 0;

  for (uint32_t b=0; b<ADC_BUTTONS; b++) {
    int32_t mapped_joy_id = adc_map.buttons[b].joy_id;
    int32_t mapped_button = adc_map.buttons[b].button;
    if (mapped_joy_id != NOT_MAPPED && mapped_button != NOT_MAPPED) {
        buttons = buttons | (joy_data[mapped_joy_id].buttons[mapped_button] << b);
      }
  }

  return (buttons & 0x3);   // todo: make 0x3 a const alongside ADC_BUTTONS?
}



