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

class InputDevice_Gamepad : public InputDevice
{
   public:

      InputDevice_Gamepad();
      virtual ~InputDevice_Gamepad();

      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 SetDTR(bool new_dtr);
      virtual bool GetDSR(void);
      virtual bool Clock(bool TxD, int32 &dsr_pulse_delay);

   private:

      bool dtr;

      uint8 buttons[2];

      int32 command_phase;
      uint32 bitpos;
      uint8 receive_buffer;

      uint8 command;

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

InputDevice_Gamepad::InputDevice_Gamepad()
{
   Power();
}

InputDevice_Gamepad::~InputDevice_Gamepad()
{

}

void InputDevice_Gamepad::Power(void)
{
   dtr = 0;

   buttons[0] = buttons[1] = 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_Gamepad::StateAction(StateMem* sm, int load, int data_only, const char* section_name)
{
   SFORMAT StateRegs[] =
   {
      SFVAR(dtr),

      SFARRAY(buttons, sizeof(buttons)),

      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_Gamepad::UpdateInput(const void *data)
{
   uint8 *d8 = (uint8 *)data;

   buttons[0] = d8[0];
   buttons[1] = d8[1];
}


void InputDevice_Gamepad::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_Gamepad::GetDSR(void)
{
   if(!dtr)
      return(0);

   if(!bitpos && transmit_count)
      return(1);

   return(0);
}

bool InputDevice_Gamepad::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] = 0x41;
               transmit_pos = 0;
               transmit_count = 1;
               command_phase++;
            }
            break;

         case 1:
            command = receive_buffer;
            command_phase++;

            transmit_buffer[0] = 0x5A;

            //if(command != 0x42)
            // fprintf(stderr, "Gamepad unhandled command: 0x%02x\n", command);
            //assert(command == 0x42);
            if(command == 0x42)
            {
               //printf("PAD COmmand 0x42, sl=%u\n", GPU->GetScanlineNum());

               transmit_buffer[1] = 0xFF ^ buttons[0];
               transmit_buffer[2] = 0xFF ^ buttons[1];
               transmit_pos = 0;
               transmit_count = 3;
            }
            else
            {
               command_phase = -1;
               transmit_buffer[1] = 0;
               transmit_buffer[2] = 0;
               transmit_pos = 0;
               transmit_count = 0;
            }
            break;

      }
   }

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

   return(ret);
}

InputDevice *Device_Gamepad_Create(void)
{
   return new InputDevice_Gamepad();
}


InputDeviceInputInfoStruct Device_Gamepad_IDII[16] =
{
   { "select", "SELECT", 4, IDIT_BUTTON, NULL },
   { NULL, "empty", 0, IDIT_BUTTON },
   { NULL, "empty", 0, IDIT_BUTTON },
   { "start", "START", 5, IDIT_BUTTON, NULL },
   { "up", "UP ↑", 0, IDIT_BUTTON, "down" },
   { "right", "RIGHT →", 3, IDIT_BUTTON, "left" },
   { "down", "DOWN ↓", 1, IDIT_BUTTON, "up" },
   { "left", "LEFT ←", 2, IDIT_BUTTON, "right" },

   { "l2", "L2 (rear left shoulder)", 11, IDIT_BUTTON, NULL },
   { "r2", "R2 (rear right shoulder)", 13, IDIT_BUTTON, NULL },
   { "l1", "L1 (front left shoulder)", 10, IDIT_BUTTON, NULL },
   { "r1", "R1 (front right shoulder)", 12, IDIT_BUTTON, NULL },

   { "triangle", "△ (upper)", 6, IDIT_BUTTON_CAN_RAPID, NULL },
   { "circle", "○ (right)", 9, IDIT_BUTTON_CAN_RAPID, NULL },
   { "cross", "x (lower)", 7, IDIT_BUTTON_CAN_RAPID, NULL },
   { "square", "□ (left)", 8, IDIT_BUTTON_CAN_RAPID, NULL },
};

InputDeviceInputInfoStruct Device_Dancepad_IDII[16] =
{
   { "select", "SELECT", 0, IDIT_BUTTON, NULL },
   { NULL, "empty", 0, IDIT_BUTTON },
   { NULL, "empty", 0, IDIT_BUTTON },
   { "start", "START", 1, IDIT_BUTTON, NULL },

   { "up", "UP ↑", 3, IDIT_BUTTON, 	NULL },
   { "right", "RIGHT →", 6, IDIT_BUTTON, 	NULL },
   { "down", "DOWN ↓", 8, IDIT_BUTTON, 	NULL },
   { "left", "LEFT ←", 5, IDIT_BUTTON, 	NULL },

   { NULL, "empty", 0, IDIT_BUTTON, NULL },
   { NULL, "empty", 0, IDIT_BUTTON, NULL },
   { NULL, "empty", 0, IDIT_BUTTON, NULL },
   { NULL, "empty", 0, IDIT_BUTTON, NULL },

   { "triangle", "△ (lower left)", 7, IDIT_BUTTON, NULL },
   { "circle", "○ (upper right)", 4, IDIT_BUTTON, NULL },
   { "cross", "x (upper left)", 2, IDIT_BUTTON, NULL },
   { "square", "□ (lower right)", 9, IDIT_BUTTON, NULL },
};
