#include "../psx.h"
#include "../frontio.h"
#include "mouse.h"

#include "../../mednafen-endian.h"

class InputDevice_Mouse : public InputDevice
{
   public:

      InputDevice_Mouse();
      virtual ~InputDevice_Mouse();

      virtual void Power(void);
      virtual int StateAction(StateMem* sm, int load, int data_only, const char* section_name);
      virtual void UpdateInput(const void *data);

      virtual void Update(const int32_t timestamp);
      virtual void ResetTS(void);

      //
      //
      //
      virtual void SetDTR(bool new_dtr);
      virtual bool Clock(bool TxD, int32 &dsr_pulse_delay);

   private:

      int32 lastts;
      int32 clear_timeout;

      bool dtr;

      uint8 button;
      uint8 button_post_mask;
      int32 accum_xdelta;
      int32 accum_ydelta;

      int32 command_phase;
      uint32 bitpos;
      uint8 receive_buffer;

      uint8 command;

      uint8 transmit_buffer[5];
      uint32 transmit_pos;
      uint32 transmit_count;
};

InputDevice_Mouse::InputDevice_Mouse()
{
   Power();
}

InputDevice_Mouse::~InputDevice_Mouse()
{

}

void InputDevice_Mouse::Update(const int32_t timestamp)
{
   int32 cycles = timestamp - lastts;

   clear_timeout += cycles;
   if(clear_timeout >= (33868800 / 4))
   {
      //puts("Mouse timeout\n");
      clear_timeout = 0;
      accum_xdelta = 0;
      accum_ydelta = 0;
      button &= button_post_mask;
   }

   lastts = timestamp;
}

void InputDevice_Mouse::ResetTS(void)
{
   lastts = 0;
}

void InputDevice_Mouse::Power(void)
{
   lastts = 0;
   clear_timeout = 0;

   dtr = 0;

   button = 0;
   button_post_mask = 0;
   accum_xdelta = 0;
   accum_ydelta = 0;

   command_phase = 0;

   bitpos = 0;

   receive_buffer = 0;

   command = 0;

   memset(transmit_buffer, 0, sizeof(transmit_buffer));

   transmit_pos = 0;
   transmit_count = 0;
}

int InputDevice_Mouse::StateAction(StateMem* sm, int load, int data_only, const char* section_name)
{
   SFORMAT StateRegs[] =
   {
      SFVAR(clear_timeout),

      SFVAR(dtr),

      SFVAR(button),
      SFVAR(button_post_mask),
      SFVAR(accum_xdelta),
      SFVAR(accum_ydelta),

      SFVAR(command_phase),
      SFVAR(bitpos),
      SFVAR(receive_buffer),

      SFVAR(command),

      SFARRAY(transmit_buffer, sizeof(transmit_buffer)),
      SFVAR(transmit_pos),
      SFVAR(transmit_count),

      SFEND
   };
   int ret = MDFNSS_StateAction(sm, load, data_only, StateRegs, section_name);

   if(load)
   {
      if(((uint64_t)transmit_pos + transmit_count) > sizeof(transmit_buffer))
      {
         transmit_pos = 0;
         transmit_count = 0;
      }
   }

   return(ret);
}

void InputDevice_Mouse::UpdateInput(const void *data)
{
   accum_xdelta += (int32)MDFN_de32lsb<false>((uint8*)data + 0);
   accum_ydelta += (int32)MDFN_de32lsb<false>((uint8*)data + 4);

   if(accum_xdelta > 30 * 127) accum_xdelta = 30 * 127;
   if(accum_xdelta < 30 * -128) accum_xdelta = 30 * -128;

   if(accum_ydelta > 30 * 127) accum_ydelta = 30 * 127;
   if(accum_ydelta < 30 * -128) accum_ydelta = 30 * -128;

   button |= *((uint8 *)data + 8);
   button_post_mask = *((uint8 *)data + 8);

   //printf("%d %d\n", accum_xdelta, accum_ydelta);
}


void InputDevice_Mouse::SetDTR(bool new_dtr)
{
   if(!dtr && new_dtr)
   {
      command_phase = 0;
      bitpos = 0;
      transmit_pos = 0;
      transmit_count = 0;
   }
   else if(dtr && !new_dtr)
   {
      //if(bitpos || transmit_count)
      // printf("[PAD] Abort communication!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
   }

   dtr = new_dtr;
}

bool InputDevice_Mouse::Clock(bool TxD, int32 &dsr_pulse_delay)
{
   bool ret = 1;

   dsr_pulse_delay = 0;

   if(!dtr)
      return(1);

   if(transmit_count)
      ret = (transmit_buffer[transmit_pos] >> bitpos) & 1;

   receive_buffer &= ~(1 << bitpos);
   receive_buffer |= TxD << bitpos;
   bitpos = (bitpos + 1) & 0x7;

   if(!bitpos)
   {
      //printf("[PAD] Receive: %02x -- command_phase=%d\n", receive_buffer, command_phase);

      if(transmit_count)
      {
         transmit_pos++;
         transmit_count--;
      }


      switch(command_phase)
      {
         case 0:
            if(receive_buffer != 0x01)
               command_phase = -1;
            else
            {
               transmit_buffer[0] = 0x12;
               transmit_pos = 0;
               transmit_count = 1;
               command_phase++;
            }
            break;

         case 1:
            command = receive_buffer;
            command_phase++;

            transmit_buffer[0] = 0x5A;

            if(command == 0x42)
            {
               int32 xdelta = accum_xdelta;
               int32 ydelta = accum_ydelta;

               if(xdelta < -128) xdelta = -128;
               if(xdelta > 127) xdelta = 127;

               if(ydelta < -128) ydelta = -128;
               if(ydelta > 127) ydelta = 127;

               transmit_buffer[1] = 0xFF;
               transmit_buffer[2] = 0xFC ^ (button << 2);
               transmit_buffer[3] = xdelta;
               transmit_buffer[4] = ydelta;

               accum_xdelta -= xdelta;
               accum_ydelta -= ydelta;

               button &= button_post_mask;

               transmit_pos = 0;
               transmit_count = 5;

               clear_timeout = 0;
            }
            else
            {
               command_phase = -1;
               transmit_pos = 0;
               transmit_count = 0;
            }
            break;

      }
   }

   if(!bitpos && transmit_count)
      dsr_pulse_delay = 0x40; //0x100;

   return(ret);
}

InputDevice *Device_Mouse_Create(void)
{
   return new InputDevice_Mouse();
}


InputDeviceInputInfoStruct Device_Mouse_IDII[4] =
{
   { "x_axis", "X Axis", -1, IDIT_X_AXIS_REL },
   { "y_axis", "Y Axis", -1, IDIT_Y_AXIS_REL },
   { "right", "Right Button", 1, IDIT_BUTTON, NULL },
   { "left", "Left Button", 0, IDIT_BUTTON, NULL },
};
