
#include "swis.h"
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "Global/NewErrors.h"
#include "Global/RISCOS.h"
#include "Global/Keyboard.h"
#include "Global/Pointer.h"

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

#include "mouse.h"

// Last button state we injected, so we only emit KeyV up/down on a change.
int32_t mouse_previous_select = 0;
int32_t mouse_previous_menu = 0;
int32_t mouse_previous_adjust = 0;


// True when this slot drives the pointer at all - any mouse move axis OR mouse
// button is bound. The decode loop uses this to decide whether to call
// update_mouse; each part is then applied independently inside it.
int mouse_active(uint32_t i)
{
  return joy_data[i].mapped_mouse_x      != NOT_MAPPED ||
         joy_data[i].mapped_mouse_y      != NOT_MAPPED ||
         joy_data[i].mapped_mouse_select != NOT_MAPPED ||
         joy_data[i].mapped_mouse_menu   != NOT_MAPPED ||
         joy_data[i].mapped_mouse_adjust != NOT_MAPPED;
}


void update_mouse(uint32_t i)
{
    // Pointer movement via PointerV Report, additive alongside the real
    // mouse - see doc.MouseKeyboard, "Mouse movement". The step is a crude
    // fixed +/-mouse_factor per USB packet; doc.Todo (Mouse pointer feel)
    // has the intended proportional-velocity model.
    //
    // The global Control gate (Joystick_Control, independent of the binding
    // table - see h.joyswis) is folded into each part below as
    // "mouse_control_on &&", rather than skipping this whole function when
    // it's off: mouse_active() (the caller's gate for whether to call this
    // function at all) stays presence-of-binding-only, so update_mouse still
    // runs every poll while bindings exist and can notice a button that was
    // held when the gate turned off - forcing its read value to 0 here lets
    // the existing change-detection below fire the one KeyV release it needs,
    // the same way it would if the button had just been let go for real.

    // get mouse x update (each axis is independent - X may be bound without Y)
    int32_t x = 0;
    if (mouse_control_on && joy_data[i].mapped_mouse_x != NOT_MAPPED) {
      struct axisdata_struct mx = joy_data[i].axes[joy_data[i].mapped_mouse_x];
      if (mx.val > mx.mid)
        x = mx.mouse_factor;
      else if (mx.val < mx.mid)
        x = -mx.mouse_factor;
      if (mx.flip)
        x = x * -1;
    }

    // get mouse y update
    int32_t y = 0;
    if (mouse_control_on && joy_data[i].mapped_mouse_y != NOT_MAPPED) {
      struct axisdata_struct my = joy_data[i].axes[joy_data[i].mapped_mouse_y];
      if (my.val > my.mid)
        y = -my.mouse_factor;
      else if (my.val < my.mid)
        y = my.mouse_factor;
      if (my.flip)
        y = y * -1;
    }

    // push the relative movement (R4 = 0 => relative, add to pointer)
    if (x || y) {
      _swix(OS_CallAVector, _INR(0,4) | _IN(9),
            PointerReason_Report, POINTER_DEVICE_JOYSTICK, x, y, 0, PointerV);
    }

    // select button - read as "not pressed" whenever unmapped OR the gate is
    // off, so a button held at the moment the gate turns off still gets its
    // KeyV_KeyUp from the change-detection below, not silently left down.
    {
      int32_t select = (mouse_control_on && joy_data[i].mapped_mouse_select != NOT_MAPPED)
                        ? joy_data[i].buttons[joy_data[i].mapped_mouse_select] : 0;
      int changed = (select != mouse_previous_select);
      mouse_previous_select = select;
      if (changed)
        _swix(OS_CallAVector, _INR(0,1) | _IN(9), (select)? KeyV_KeyDown: KeyV_KeyUp, KeyNo_LeftMouse, KEYV);
    }
    // menu button
    {
      int32_t menu = (mouse_control_on && joy_data[i].mapped_mouse_menu != NOT_MAPPED)
                      ? joy_data[i].buttons[joy_data[i].mapped_mouse_menu] : 0;
      int changed = (menu != mouse_previous_menu);
      mouse_previous_menu = menu;
      if (changed)
        _swix(OS_CallAVector, _INR(0,1) | _IN(9), (menu)? KeyV_KeyDown: KeyV_KeyUp, KeyNo_CentreMouse, KEYV);
    }
    // adjust button
    {
      int32_t adjust = (mouse_control_on && joy_data[i].mapped_mouse_adjust != NOT_MAPPED)
                        ? joy_data[i].buttons[joy_data[i].mapped_mouse_adjust] : 0;
      int changed = (adjust != mouse_previous_adjust);
      mouse_previous_adjust = adjust;
      if (changed)
        _swix(OS_CallAVector, _INR(0,1) | _IN(9), (adjust)? KeyV_KeyDown: KeyV_KeyUp, KeyNo_RightMouse, KEYV);
    }
}
