using System;
using System.Collections.Generic;
using System.Text;
using LibUsbDotNet;
using LibUsbDotNet.Main;

namespace Salamax
{
  public class Xbox360Controller : USBDevice
  {
    private UsbDevice _device = null;

    public override bool IsConnected
    {
      get
      {
        return (_device != null && _device.IsOpen);
      }
    }

    public override void Find()
    {
      bool wasValid = (_device != null && _device.IsOpen);

      //Find the Xbox 360 controller
      bool found = _FindController();

      if (found)
      {
        if (!wasValid)
        {
          Logger.WriteLine("Xbox 360 controller found, initializing...", Logger.Type.Warning);
          /*
          var device = _device as IUsbDevice;
          device.ResetDevice();
          System.Threading.Thread.Sleep(3000);
          _FindController();
           * */
          var writer = _device.OpenEndpointWriter(WriteEndpointID.Ep02, EndpointType.Interrupt);
          int transferred;
          writer.Write(new byte[] { 0x01, 0x03, 0x00 }, 10000, out transferred);

          Logger.WriteLine("Xbox 360 controller initialized.", Logger.Type.Information);
        }
      }
      else
      {
        if (_device != null)
        {
          _device = null;
          Logger.WriteLine("Xbox 360 controller disconnected.", Logger.Type.Warning);
        }
      }
    }

    public override void Disconnect()
    {
      if (_device != null && _device.IsOpen)
      {
        _device.Close();
      }
    }

    public byte[] RequestStaticData()
    {
      var ret = new byte[0x1D];

      int transferred;
      Logger.WriteLine("Setting Xbox 360 controller configuration 1.", Logger.Type.Information);
      var setup = new UsbSetupPacket(0x00, 0x09, 0x0001, 0x00000, 0x0000);
      var success = _device.ControlTransfer(ref setup, IntPtr.Zero, 0x0000, out transferred);
      Logger.WriteLine("Requesting Xbox 360 controller security string descriptor...", Logger.Type.Information);
      setup = new UsbSetupPacket(0x80, 0x06, 0x0304, 0x0409, 0x00B2);
      var buffer = new byte[0x00B2];
      success = _device.ControlTransfer(ref setup, buffer, 0x00B2, out transferred);

      setup = new UsbSetupPacket(0xC1, 0x81, 0x5B17, 0x0103, 0x001D);
      Logger.WriteLine("Requesting static data from controller...", Logger.Type.Information);
      if (_device.ControlTransfer(ref setup, ret, 0x001D, out transferred))
        Logger.WriteLine("Static controller data received successfully.", Logger.Type.Information);

      return ret;
    }

    public byte[] DoChallenge1(byte[] data)
    {
      int transferred;
      var ret = new byte[0x2E];

      Logger.WriteLine("Sending first challenge to Xbox 360 controller...", Logger.Type.Information);
      var setup = new UsbSetupPacket(0x41, 0x82, 0x0003, 0x0103, 0x0022);
      if (_device.ControlTransfer(ref setup, data, 0x0022, out transferred))
      {
        //Wait till the controller is done with our response
        bool complete = false;
        do
        {
          setup = new UsbSetupPacket(0xC1, 0x86, 0x0000, 0x0103, 0x0002);
          var response = new byte[0x0002];
          if (_device.ControlTransfer(ref setup, response, 0x0002, out transferred) && transferred == 2)
          {
            Logger.WriteLine("Checking status of first response from Xbox 360 controller: " + response[0].ToString("X2"),
              Logger.Type.Information);
            if (response[0] > 1)
              complete = true;
          }

          System.Threading.Thread.Sleep(100);
        } while (!complete);

        Logger.WriteLine("First response is ready from Xbox 360 controller.", Logger.Type.Information);

        //Get the response
        setup = new UsbSetupPacket(0xC1, 0x83, 0x5C28, 0x0103, 0x002E);
        Logger.WriteLine("Requesting first response from Xbox 360 controller...", Logger.Type.Information);
        if (_device.ControlTransfer(ref setup, ret, 0x002E, out transferred))
        {
          Logger.WriteLine("Sending next step to Xbox 360 controller...", Logger.Type.Information);
          setup = new UsbSetupPacket(0x41, 0x84, 0x0003, 0x0103, 0x0000);
          _device.ControlTransfer(ref setup, IntPtr.Zero, 0, out transferred);
        }
        else
          Logger.WriteLine("First response request from Xbox 360 controller failed.", Logger.Type.Error);
      }
      else
        Logger.WriteLine("Sending first challenge to Xbox 360 controller failed.", Logger.Type.Error);

      return ret;
    }

    public byte[] DoChallenge2(byte[] data)
    {
      int transferred;
      var ret = new byte[0x16];

      Logger.WriteLine("Sending second challenge to Xbox 360 controller...", Logger.Type.Information);
      var setup = new UsbSetupPacket(0x41, 0x87, 0x0003, 0x0103, 0x0016);
      var buffer = new byte[0x100];
      for (int i = 0; i < data.Length; i++)
        buffer[i] = data[i];
      if (_device.ControlTransfer(ref setup, buffer, 0x0016, out transferred))
      {
        //Wait till the controller is done with our response
        bool complete = false;
        do
        {
          setup = new UsbSetupPacket(0xC1, 0x86, 0x0000, 0x0103, 0x0002);
          var response = new byte[0x0002];
          if (_device.ControlTransfer(ref setup, response, 0x0002, out transferred) && transferred == 2)
          {
            Logger.WriteLine("Checking status of second response from Xbox 360 controller: " + response[0].ToString("X2"),
              Logger.Type.Information);
            if (response[0] > 1)
              complete = true;
          }

          System.Threading.Thread.Sleep(100);
        } while (!complete);

        Logger.WriteLine("Second response is ready from Xbox 360 controller.", Logger.Type.Information);

        //Get the response
        setup = new UsbSetupPacket(0xC1, 0x83, 0x5C10, 0x0103, 0x0016);
        Logger.WriteLine("Requesting second response from Xbox 360 controller...", Logger.Type.Information);
        if (_device.ControlTransfer(ref setup, ret, 0x0016, out transferred))
        {
          Logger.WriteLine("Sending next step to Xbox 360 controller...", Logger.Type.Information);
          setup = new UsbSetupPacket(0x41, 0x84, 0x0003, 0x0103, 0x0000);
          _device.ControlTransfer(ref setup, IntPtr.Zero, 0, out transferred);
        }
        else
          Logger.WriteLine("Second response request from Xbox 360 controller failed.", Logger.Type.Error);
      }
      else
        Logger.WriteLine("Sending second challenge to Xbox 360 controller failed.", Logger.Type.Error);

      return ret;
    }

    private bool _FindController()
    {
      bool ret = false;

      foreach (object reg in LibUsbDotNet.LibUsb.LibUsbRegistry.DeviceList)
      {
        if (reg.GetType() == typeof(LibUsbDotNet.LibUsb.LibUsbRegistry))
        {
          var usb = reg as LibUsbDotNet.LibUsb.LibUsbRegistry;

          if (usb.Vid == 0x1BAD && usb.Pid == 0xF900)
          {
            _device = usb.Device;
            ret = true;
            break;
          }
        }
      }

      return ret;
    }
  }
}
