//========================================================================================
//  XInput.java
//    en:XInput Gamepad
//    ja:XInput Gamepad
//  Copyright (C) 2003-2025 Makoto Kamada
//
//  This file is part of the XEiJ (X68000 Emulator in Java).
//  You can use, modify and redistribute the XEiJ if the conditions are met.
//  Read the XEiJ License for more details.
//  https://stdkmd.net/xeij/
//========================================================================================

package xeij;

import java.lang.foreign.*;  //Arena,FunctionDescriptor,Linker,MemorySegment,SymbolLookup,ValueLayout
import java.lang.invoke.*;  //MethodHandle
import java.util.*;

//class XInput
//  XInputクラス
public final class XInput extends Thread {



  //公開クラス定数

  //ボタンのビット
  public static final int UP_BIT        = 0;  //上ボタン
  public static final int DOWN_BIT      = 1;  //下ボタン
  public static final int LEFT_BIT      = 2;  //左ボタン
  public static final int RIGHT_BIT     = 3;  //右ボタン
  public static final int START_BIT     = 4;  //STARTボタン
  public static final int BACK_BIT      = 5;  //BACKボタン
  public static final int LSTICK_BIT    = 6;  //左スティックボタン
  public static final int RSTICK_BIT    = 7;  //右スティックボタン
  public static final int LB_BIT        = 8;  //LBボタン
  public static final int RB_BIT        = 9;  //RBボタン
  public static final int A_BIT         = 12;  //Aボタン
  public static final int B_BIT         = 13;  //Bボタン
  public static final int X_BIT         = 14;  //Xボタン
  public static final int Y_BIT         = 15;  //Yボタン
  public static final int LSUP_BIT      = 16;  //左スティック上
  public static final int LSDOWN_BIT    = 17;  //左スティック下
  public static final int LSLEFT_BIT    = 18;  //左スティック左
  public static final int LSRIGHT_BIT   = 19;  //左スティック右
  public static final int RSUP_BIT      = 20;  //右スティック上
  public static final int RSDOWN_BIT    = 21;  //右スティック下
  public static final int RSLEFT_BIT    = 22;  //右スティック左
  public static final int RSRIGHT_BIT   = 23;  //右スティック右
  public static final int LTRIGGER_BIT  = 24;  //左トリガー
  public static final int RTRIGGER_BIT  = 25;  //右トリガー
  public static final int BUTTONS       = 26;  //ボタンの数

  //ボタンのマスク
  public static final int UP_MASK       = 1 << UP_BIT;
  public static final int DOWN_MASK     = 1 << DOWN_BIT;
  public static final int LEFT_MASK     = 1 << LEFT_BIT;
  public static final int RIGHT_MASK    = 1 << RIGHT_BIT;
  public static final int START_MASK    = 1 << START_BIT;
  public static final int BACK_MASK     = 1 << BACK_BIT;
  public static final int LSTICK_MASK   = 1 << LSTICK_BIT;
  public static final int RSTICK_MASK   = 1 << RSTICK_BIT;
  public static final int LB_MASK       = 1 << LB_BIT;
  public static final int RB_MASK       = 1 << RB_BIT;
  public static final int A_MASK        = 1 << A_BIT;
  public static final int B_MASK        = 1 << B_BIT;
  public static final int X_MASK        = 1 << X_BIT;
  public static final int Y_MASK        = 1 << Y_BIT;
  public static final int LSUP_MASK     = 1 << LSUP_BIT;
  public static final int LSDOWN_MASK   = 1 << LSDOWN_BIT;
  public static final int LSLEFT_MASK   = 1 << LSLEFT_BIT;
  public static final int LSRIGHT_MASK  = 1 << LSRIGHT_BIT;
  public static final int RSUP_MASK     = 1 << RSUP_BIT;
  public static final int RSDOWN_MASK   = 1 << RSDOWN_BIT;
  public static final int RSLEFT_MASK   = 1 << RSLEFT_BIT;
  public static final int RSRIGHT_MASK  = 1 << RSRIGHT_BIT;
  public static final int LTRIGGER_MASK = 1 << LTRIGGER_BIT;
  public static final int RTRIGGER_MASK = 1 << RTRIGGER_BIT;

  //ボタンの名前
  public static final String BIT_TO_TEXT[] = new String[] {
    "UP", "DOWN", "LEFT", "RIGHT", "START", "BACK", "LSTICK", "RSTICK",
    "LB", "RB", "(10)", "(11)", "A", "B", "X", "Y",
    "LSUP", "LSDOWN", "LSLEFT", "LSRIGHT", "RSUP", "RSDOWN", "RSLEFT", "RSRIGHT",
    "LTRIGGER", "RTRIGGER",
  };



  //非公開クラス定数

  //型
  protected static final ValueLayout.OfInt DWORD = ValueLayout.JAVA_INT;
  protected static final ValueLayout.OfShort WORD = ValueLayout.JAVA_SHORT;
  protected static final ValueLayout.OfByte BYTE = ValueLayout.JAVA_BYTE;
  protected static final ValueLayout.OfShort SHORT = ValueLayout.JAVA_SHORT;

  //エラーコード
  protected static final int ERROR_SUCCESS = 0;
  //protected static final int ERROR_DEVICE_NOT_CONNECTED = 1167;

  //構造体
  //  XINPUT_GAMEPAD
  //  https://learn.microsoft.com/ja-jp/windows/win32/api/xinput/ns-xinput-xinput_gamepad
  protected static final MemoryLayout GAMEPAD_LAYOUT = MemoryLayout.structLayout (
    WORD.withName ("wButtons"),
    BYTE.withName ("bLeftTrigger"),
    BYTE.withName ("bRightTrigger"),
    SHORT.withName ("sThumbLX"),
    SHORT.withName ("sThumbLY"),
    SHORT.withName ("sThumbRX"),
    SHORT.withName ("sThumbRY"));
  protected static final long OFFSET_wButtons = GAMEPAD_LAYOUT.byteOffset (MemoryLayout.PathElement.groupElement ("wButtons"));
  protected static final long OFFSET_bLeftTrigger = GAMEPAD_LAYOUT.byteOffset (MemoryLayout.PathElement.groupElement ("bLeftTrigger"));
  protected static final long OFFSET_bRightTrigger = GAMEPAD_LAYOUT.byteOffset (MemoryLayout.PathElement.groupElement ("bRightTrigger"));
  protected static final long OFFSET_sThumbLX = GAMEPAD_LAYOUT.byteOffset (MemoryLayout.PathElement.groupElement ("sThumbLX"));
  protected static final long OFFSET_sThumbLY = GAMEPAD_LAYOUT.byteOffset (MemoryLayout.PathElement.groupElement ("sThumbLY"));
  protected static final long OFFSET_sThumbRX = GAMEPAD_LAYOUT.byteOffset (MemoryLayout.PathElement.groupElement ("sThumbRX"));
  protected static final long OFFSET_sThumbRY = GAMEPAD_LAYOUT.byteOffset (MemoryLayout.PathElement.groupElement ("sThumbRY"));
  //  XINPUT_STATE
  //  https://learn.microsoft.com/ja-jp/windows/win32/api/xinput/ns-xinput-xinput_state
  protected static final MemoryLayout STATE_LAYOUT = MemoryLayout.structLayout (
    DWORD.withName ("dwPacketNumber"),
    GAMEPAD_LAYOUT.withName ("Gamepad"));
  protected static final long OFFSET_dwPacketNumber = STATE_LAYOUT.byteOffset (MemoryLayout.PathElement.groupElement ("dwPacketNumber"));



  //非公開インスタンス変数

  //ゲームパッド
  protected Gamepad[] gamepads;

  //リスナー
  protected ArrayList<GamepadListener> gamepadListeners;

  //ポーリング
  protected boolean polling;  //true=ポーリング動作中

  //関数
  protected MethodHandle XInputGetState;

  //構造体
  protected MemorySegment state;
  protected MemorySegment gamepad;



  //公開コンストラクタ

  //new XInput ()
  //  コンストラクタ
  public XInput () {
    //ゲームパッドを開く
    gamepads = new Gamepad[4];
    for (int index = 0; index < 4; index++) {
      gamepads[index] = new Gamepad (index);
    }
    //リスナーを初期化する
    gamepadListeners = new ArrayList<GamepadListener> ();
    //ポーリングを開始する
    polling = true;
    this.start ();
  }



  //スレッド

  //run ()
  @Override public void run () {
    //アリーナを開く
    //  アリーナはスレッドを跨げない
    try (Arena arena = Arena.ofConfined ()) {
      //ライブラリを開く
      SymbolLookup xinputLibrary = SymbolLookup.libraryLookup ("XInput1_4.dll", arena);
      //関数をリンクする
      Linker linker = Linker.nativeLinker ();
      //  XInputGetState
      //  https://learn.microsoft.com/ja-jp/windows/win32/api/xinput/nf-xinput-xinputgetstate
      XInputGetState = linker.downcallHandle (
        xinputLibrary.findOrThrow ("XInputGetState"),
        FunctionDescriptor.of (
          DWORD,  //return
          DWORD,  //dwUserIndex
          ValueLayout.ADDRESS));  //XINPUT_STATE *
      //構造体の領域を確保する
      state = arena.allocate (STATE_LAYOUT);
      gamepad = state.asSlice (STATE_LAYOUT.byteOffset (MemoryLayout.PathElement.groupElement ("Gamepad")));
      //ポーリングを開始する
      //  使用できるコントローラは1/100間隔、それ以外はおよそ1/3秒間隔でポーリングする
      while (polling) {
        boolean available = false;  //true=使用できるコントローラがある
        int counter = 0;  //0～31
        for (int index = 0; index < 4; index++) {
          Gamepad gamepad = gamepads[index];
          if (counter == 0 || gamepad.isAvailable ()) {
            available = available || gamepad.isAvailable ();
            if (gamepad.getState () &&  //状態を取得する。変化したかつ
                gamepadListeners.size () != 0) {  //リスナーがある
              if (gamepad.isConnected ()) {  //接続された
                for (GamepadListener listener : gamepadListeners) {
                  listener.connected (gamepad);
                }
              } else if (gamepad.isDisconnected ()) {  //切断された
                for (GamepadListener listener : gamepadListeners) {
                  listener.disconnected (gamepad);
                }
              }
              if (gamepad.isAvailable ()) {  //使用できる
                int buttonMasks = gamepad.getPressedButtonMasks ();
                if (buttonMasks != 0) {  //押されたボタンがある
                  for (GamepadListener listener : gamepadListeners) {
                    listener.buttonPressed (gamepad, buttonMasks);
                  }
                }
                buttonMasks = gamepad.getReleasedButtonMasks ();
                if (buttonMasks != 0) {  //離されたボタンがある
                  for (GamepadListener listener : gamepadListeners) {
                    listener.buttonReleased (gamepad, buttonMasks);
                  }
                }
                if (gamepad.isLeftStickMovedX ()) {  //左スティックがX方向に動いた
                  for (GamepadListener listener : gamepadListeners) {
                    listener.leftStickMovedX (gamepad);
                  }
                }
                if (gamepad.isLeftStickMovedY ()) {  //左スティックがY方向に動いた
                  for (GamepadListener listener : gamepadListeners) {
                    listener.leftStickMovedY (gamepad);
                  }
                }
                if (gamepad.isLeftTriggerMoved ()) {  //左トリガーが動いた
                  for (GamepadListener listener : gamepadListeners) {
                    listener.leftTriggerMoved (gamepad);
                  }
                }
                if (gamepad.isRightStickMovedX ()) {  //右スティックがX方向に動いた
                  for (GamepadListener listener : gamepadListeners) {
                    listener.rightStickMovedX (gamepad);
                  }
                }
                if (gamepad.isRightStickMovedY ()) {  //右スティックがY方向に動いた
                  for (GamepadListener listener : gamepadListeners) {
                    listener.rightStickMovedY (gamepad);
                  }
                }
                if (gamepad.isRightTriggerMoved ()) {  //右トリガーが動いた
                  for (GamepadListener listener : gamepadListeners) {
                    listener.rightTriggerMoved (gamepad);
                  }
                }
              }  //if 使用できる
            }  //if 変化したかつリスナーがある
          }  //if counter==0||isAvailable
        }  //for index
        try {
          if (available) {  //使用できるコントローラがある
            counter = (counter + 1) & 31;
            Thread.sleep (10L);
          } else {  //使用できるコントローラがない
            counter = 0;
            Thread.sleep (320L);
          }
        } catch (InterruptedException ie) {  //割り込まれた
          return;  //終了する
        }
      }  //while polling
    }  //try
  }  //run



  //公開インスタンスメソッド

  //end ()
  //  終了する
  public void end () {
    //ポーリングを終了する
    if (polling) {
      polling = false;
      if (this.isAlive ()) {  //スレッドがある
        this.interrupt ();  //割り込む
        try {
          this.join ();  //止まるまで待つ
        } catch (InterruptedException ie) {
        }
      }
    }
    //ゲームパッドを閉じる
    for (int index = 0; index < 4; index++) {
      gamepads[index].close ();
    }
  }

  //buttonMasks = getButtonMasks (index)
  //  すべての押されているボタンのマスクを返す
  //  index  コントローラのインデックス
  //  buttonMasks  すべての押されているボタンのマスク
  public int getButtonMasks (int index) {
    return gamepads[index].getButtonMasks ();
  }

  //leftStickX = getLeftStickX (index)
  //  左スティックのX方向の位置を返す
  //  index  コントローラのインデックス
  //  leftStickX  左スティックのX方向の位置
  public int getLeftStickX (int index) {
    return gamepads[index].getLeftStickX ();
  }

  //leftStickY = getLeftStickY (index)
  //  左スティックのY方向の位置を返す
  //  index  コントローラのインデックス
  //  leftStickY  左スティックのY方向の位置
  public int getLeftStickY (int index) {
    return gamepads[index].getLeftStickY ();
  }

  //leftTrigger = getLeftTrigger (index)
  //  左トリガーの位置を返す
  //  index  コントローラのインデックス
  //  leftTrigger  左トリガーの位置
  public int getLeftTrigger (int index) {
    return gamepads[index].getLeftTrigger ();
  }

  //rightStickX = getRightStickX (index)
  //  右スティックのX方向の位置を返す
  //  index  コントローラのインデックス
  //  rightStickX  右スティックのX方向の位置
  public int getRightStickX (int index) {
    return gamepads[index].getRightStickX ();
  }

  //rightStickY = getRightStickY (index)
  //  右スティックのY方向の位置を返す
  //  index  コントローラのインデックス
  //  rightStickY  右スティックのY方向の位置
  public int getRightStickY (int index) {
    return gamepads[index].getRightStickY ();
  }

  //rightTrigger = getRightTrigger (index)
  //  右トリガーの位置を返す
  //  index  コントローラのインデックス
  //  rightTrigger  右トリガーの位置
  public int getRightTrigger (int index) {
    return gamepads[index].getRightTrigger ();
  }

  //available = isAvailable (index)
  //  使用できるかを返す
  //  index  コントローラのインデックス
  //  available  使用できるか
  public boolean isAvailable (int index) {
    return gamepads[index].isAvailable ();
  }

  //setThresholdOfLeftStick (index, thresholdOfLeftStick)
  //  左スティックの閾値を設定する
  //  index  コントローラのインデックス
  //  thresholdOfLeftStick  左スティックの閾値
  public void setThresholdOfLeftStick (int index, int thresholdOfLeftStick) {
    gamepads[index].setThresholdOfLeftStick (thresholdOfLeftStick);
  }

  //setThresholdOfRightStick (index, thresholdOfRightStick)
  //  右スティックの閾値を設定する
  //  index  コントローラのインデックス
  //  thresholdOfRightStick  右スティックの閾値
  public void setThresholdOfRightStick (int index, int thresholdOfRightStick) {
    gamepads[index].setThresholdOfRightStick (thresholdOfRightStick);
  }

  //setThresholdOfLeftTrigger (index, thresholdOfLeftTrigger)
  //  左トリガーの閾値を設定する
  //  index  コントローラのインデックス
  //  thresholdOfLeftTrigger  左トリガーの閾値
  public void setThresholdOfLeftTrigger (int index, int thresholdOfLeftTrigger) {
    gamepads[index].setThresholdOfLeftTrigger (thresholdOfLeftTrigger);
  }

  //setThresholdOfRightTrigger (index, thresholdOfRightTrigger)
  //  右トリガーの閾値を設定する
  //  index  コントローラのインデックス
  //  thresholdOfRightTrigger  右トリガーの閾値
  public void setThresholdOfRightTrigger (int index, int thresholdOfRightTrigger) {
    gamepads[index].setThresholdOfRightTrigger (thresholdOfRightTrigger);
  }



  //ゲームパッドリスナー

  //interface XInput.GamepadListener
  //  ゲームパッドリスナー
  public interface GamepadListener {
    public void connected (Gamepad gamepad);  //接続された
    public void disconnected (Gamepad gamepad);  //切断された
    public void buttonPressed (Gamepad gamepad, int buttonMasks);  //ボタンが押された
    public void buttonReleased (Gamepad gamepad, int buttonMasks);  //ボタンが離された
    public void leftStickMovedX (Gamepad gamepad);  //左スティックがX方向に動いた
    public void leftStickMovedY (Gamepad gamepad);  //左スティックがY方向に動いた
    public void leftTriggerMoved (Gamepad gamepad);  //左トリガーが動いた
    public void rightStickMovedX (Gamepad gamepad);  //右スティックがX方向に動いた
    public void rightStickMovedY (Gamepad gamepad);  //右スティックがY方向に動いた
    public void rightTriggerMoved (Gamepad gamepad);  //右トリガーが動いた
  }

  //addGamepadListener (listener)
  //  ゲームパッドリスナーを追加する
  //  listener  追加するGamepadリスナー
  public void addGamepadListener (GamepadListener listener) {
    if (listener != null && !gamepadListeners.contains (listener)) {
      gamepadListeners.add (listener);
    }
  }

  //removeGamepadListener (listener)
  //  ゲームパッドリスナーを削除する
  //  listener  削除するGamepadリスナー
  public void removeGamepadListener (GamepadListener listener) {
    gamepadListeners.remove (listener);
  }

  //removeGamepadListeners ()
  //  すべてのゲームパッドリスナーを削除する
  public void removeGamepadListeners () {
    gamepadListeners.clear ();
  }

  //getGamepadListeners ()
  //  すべてのゲームパッドリスナーを取得する
  public GamepadListener[] getGamepadListeners () {
    return gamepadListeners.toArray (new GamepadListener[gamepadListeners.size ()]);
  }



  //ゲームパッドアダプター

  //class XInput.GamepadAdapter
  //  ゲームパッドアダプター
  public static class GamepadAdapter implements GamepadListener {
    @Override public void connected (Gamepad gamepad) {
    }
    @Override public void disconnected (Gamepad gamepad) {
    }
    @Override public void buttonPressed (Gamepad gamepad, int buttonMasks) {
    }
    @Override public void buttonReleased (Gamepad gamepad, int buttonMasks) {
    }
    @Override public void leftStickMovedX (Gamepad gamepad) {
    }
    @Override public void leftStickMovedY (Gamepad gamepad) {
    }
    @Override public void leftTriggerMoved (Gamepad gamepad) {
    }
    @Override public void rightStickMovedX (Gamepad gamepad) {
    }
    @Override public void rightStickMovedY (Gamepad gamepad) {
    }
    @Override public void rightTriggerMoved (Gamepad gamepad) {
    }
  }



  //ゲームパッドクラス

  //class XInput.Gamepad
  //  ゲームパッドクラス
  public class Gamepad implements AutoCloseable {

    //閾値
    //  スティックの閾値を小さくしすぎると初動の方向がブレやすくなる
    //  トリガーは小さくしても押し込まないと入らない印象。コントローラにもよるのだろう
    static final int THRESHOLD_OF_STICK = (32768 / 2);
    static final int THRESHOLD_OF_TRIGGER = (256 / 4);

    //ワークエリア
    int index;  //コントローラのインデックス。0～3
    //  今回の状態
    boolean available;  //true=XInputGetStateがERROR_SUCCESSを返した
    int dwPacketNumber;  //ここからXInputGetStateが出力したXINPUT_STATE構造体から取り出した値
    int wButtons;
    int bLeftTrigger;
    int bRightTrigger;
    int sThumbLX;
    int sThumbLY;
    int sThumbRX;
    int sThumbRY;  //ここまで
    int buttonMasks;  //スティックとトリガーの位置を加えたボタンのマスク
    //  前回の状態
    boolean previous_available;
    int previous_dwPacketNumber;
    int previous_wButtons;
    int previous_bLeftTrigger;
    int previous_bRightTrigger;
    int previous_sThumbLX;
    int previous_sThumbLY;
    int previous_sThumbRX;
    int previous_sThumbRY;
    int previous_buttonMasks;
    //  設定
    long thresholdOfLeftStick2;  //左スティックの閾値の2乗
    long thresholdOfRightStick2;  //右スティックの閾値の2乗
    int thresholdOfLeftTrigger;  //左トリガーの閾値
    int thresholdOfRightTrigger;  //右トリガーの閾値

    //new Gamepad (index)
    //  コンストラクタ
    //  index  コントローラのインデックス。0～3
    public Gamepad (int index) {
      open (index);
    }

    //close ()
    //  閉じる
    @Override public void close () {
      available = false;
      dwPacketNumber = 0;
      wButtons = 0;
      bLeftTrigger = 0;
      bRightTrigger = 0;
      sThumbLX = 0;
      sThumbLY = 0;
      sThumbRX = 0;
      sThumbRY = 0;
      buttonMasks = 0;
      previous_available = false;
      previous_dwPacketNumber = 0;
      previous_wButtons = 0;
      previous_bLeftTrigger = 0;
      previous_bRightTrigger = 0;
      previous_sThumbLX = 0;
      previous_sThumbLY = 0;
      previous_sThumbRX = 0;
      previous_sThumbRY = 0;
      previous_buttonMasks = 0;
    }

    //buttonMasks = getButtonMasks ()
    //  getState()で取得したすべての押されているボタンのマスクを返す
    //  buttonMasks  すべての押されているボタンのマスク
    public int getButtonMasks () {
      return buttonMasks;
    }

    //index = getIndex ()
    //  コントローラのインデックスを返す
    //  index  コントローラのインデックス
    public int getIndex () {
      return index;
    }

    //leftStickX = getLeftStickX ()
    //  getState()で取得した左スティックのX方向の位置を返す
    //  leftStickX  左スティックのX方向の位置
    public int getLeftStickX () {
      return sThumbLX;
    }

    //leftStickY = getLeftStickY ()
    //  getState()で取得した左スティックのY方向の位置を返す
    //  leftStickY  左スティックのY方向の位置
    public int getLeftStickY () {
      return sThumbLY;
    }

    //leftTrigger = getLeftTrigger ()
    //  getState()で取得した左トリガーの位置を返す
    //  leftTrigger  左トリガーの位置
    public int getLeftTrigger () {
      return bLeftTrigger;
    }

    //pressedButtonMasks = getPressedButtonMasks ()
    //  getState()で取得したすべての押されたボタンのマスクを返す
    //  pressedButtonMasks  すべての押されたボタンのマスク
    public int getPressedButtonMasks () {
      return buttonMasks & ~previous_buttonMasks;
    }

    //releasedButtonMasks = getReleasedButtonMasks ()
    //  getState()で取得したすべての離されたボタンのマスクを返す
    //  releasedButtonMasks  すべての離されたボタンのマスク
    public int getReleasedButtonMasks () {
      return ~buttonMasks & previous_buttonMasks;
    }

    //rightStickX = getRightStickX ()
    //  getState()で取得した右スティックのX方向の位置を返す
    //  rightStickX  右スティックのX方向の位置
    public int getRightStickX () {
      return sThumbRX;
    }

    //rightStickY = getRightStickY ()
    //  getState()で取得した右スティックのY方向の位置を返す
    //  rightStickY  右スティックのY方向の位置
    public int getRightStickY () {
      return sThumbRY;
    }

    //rightTrigger = getRightTrigger ()
    //  getState()で取得した右トリガーの位置を返す
    //  rightTrigger  右トリガーの位置
    public int getRightTrigger () {
      return bRightTrigger;
    }

    //changed = getState ()
    //  コントローラの状態を取得する
    //  changed  false=変化しなかった,true=変化した。使用可能とは限らない
    public boolean getState () {
      //前回の状態を保存する
      previous_available = available;
      previous_wButtons = wButtons;
      previous_bLeftTrigger = bLeftTrigger;
      previous_bRightTrigger = bRightTrigger;
      previous_sThumbLX = sThumbLX;
      previous_sThumbLY = sThumbLY;
      previous_sThumbRX = sThumbRX;
      previous_sThumbRY = sThumbRY;
      previous_buttonMasks = buttonMasks;
      //今回の状態を取得する
      available = XInputGetState != null;
      if (available) {
        try {
          available = (int) XInputGetState.invoke (index, state) == ERROR_SUCCESS;
        } catch (Throwable t) {
        }
      }
      if (available) {  //使用できる
        dwPacketNumber = state.get (DWORD, OFFSET_dwPacketNumber);
        wButtons = (char) gamepad.get (WORD, OFFSET_wButtons);
        bLeftTrigger = 0xff & gamepad.get (BYTE, OFFSET_bLeftTrigger);
        bRightTrigger = 0xff & gamepad.get (BYTE, OFFSET_bRightTrigger);
        sThumbLX = gamepad.get (SHORT, OFFSET_sThumbLX);
        sThumbLY = gamepad.get (SHORT, OFFSET_sThumbLY);
        sThumbRX = gamepad.get (SHORT, OFFSET_sThumbRX);
        sThumbRY = gamepad.get (SHORT, OFFSET_sThumbRY);
      } else {  //使用できない
        dwPacketNumber = 0;
        wButtons = 0;
        bLeftTrigger = 0;
        bRightTrigger = 0;
        sThumbLX = 0;
        sThumbLY = 0;
        sThumbRX = 0;
        sThumbRY = 0;
      }
      //ボタンのマスクにスティックとトリガーの位置を加える
      int masks = wButtons;
      //左スティック
      {
        int x = sThumbLX;
        int y = sThumbLY;
        if (thresholdOfLeftStick2 < ((long) x * (long) x + (long) y * (long) y)) {  //閾値の円盤の外側。オーバーフローに注意
          int px = 5741 * x - 2378 * y;  //+xから+yへpi/8回転させた方向。tan(pi/8)≒2378/5741
          int py = 2378 * x + 5741 * y;
          int qx = 5741 * x + 2378 * y;  //+xから+yへ-pi/8回転させた方向
          int qy = -2378 * x + 5741 * y;
          masks |= ((0 <= (-px | -qx) ? LSLEFT_MASK : 0 <= (px | qx) ? LSRIGHT_MASK : 0) |
                    (0 <= (-py | -qy) ? LSDOWN_MASK : 0 <= (py | qy) ? LSUP_MASK : 0));  //8方向判別
        }
      }
      //右スティック
      {
        int x = sThumbRX;
        int y = sThumbRY;
        if (thresholdOfRightStick2 < ((long) x * (long) x + (long) y * (long) y)) {  //閾値の円盤の外側。オーバーフローに注意
          int px = 5741 * x - 2378 * y;  //+xから+yへpi/8回転させた方向。tan(pi/8)≒2378/5741
          int py = 2378 * x + 5741 * y;
          int qx = 5741 * x + 2378 * y;  //+xから+yへ-pi/8回転させた方向
          int qy = -2378 * x + 5741 * y;
          masks |= ((0 <= (-px | -qx) ? RSLEFT_MASK : 0 <= (px | qx) ? RSRIGHT_MASK : 0) |
                    (0 <= (-py | -qy) ? RSDOWN_MASK : 0 <= (py | qy) ? RSUP_MASK : 0));  //8方向判別
        }
      }
      //左トリガー
      if (thresholdOfLeftTrigger < bLeftTrigger) {  //閾値より大きい
        masks |= LTRIGGER_MASK;
      }
      //右トリガー
      if (thresholdOfRightTrigger < bRightTrigger) {  //閾値より大きい
        masks |= RTRIGGER_MASK;
      }
      buttonMasks = masks;
      //変化したかどうかを返す
      return ((available != previous_available) ||  //使用できるかが変化したか
              (available &&  //使用できて
               dwPacketNumber != previous_dwPacketNumber));  //変化した
    }

    //available = isAvailable ()
    //  使用できるかを返す
    //  available  使用できるか
    public boolean isAvailable () {
      return available;
    }

    //connected = isConnected ()
    //  接続されたかを返す
    //  connected  接続されたか
    public boolean isConnected () {
      return available && !previous_available;
    }

    //disconnected = isDisconnected ()
    //  切断されたかを返す
    //  disconnected  切断されたか
    public boolean isDisconnected () {
      return !available && previous_available;
    }

    //leftStickMovedX = isLeftStickMovedX ()
    //  getState()で取得した左スティックがX方向に動いたかを返す
    //  leftStickMovedX  左スティックがX方向に動いたか
    public boolean isLeftStickMovedX () {
      return sThumbLX != previous_sThumbLX;
    }

    //leftStickMovedY = isLeftStickMovedY ()
    //  getState()で取得した左スティックがY方向に動いたかを返す
    //  leftStickMovedY  左スティックがY方向に動いたか
    public boolean isLeftStickMovedY () {
      return sThumbLY != previous_sThumbLY;
    }

    //leftTriggerMoved = isLeftTriggerMoved ()
    //  getState()で取得した左トリガーが動いたかを返す
    //  leftTriggerMoved  左トリガーが動いたか
    public boolean isLeftTriggerMoved () {
      return bLeftTrigger != previous_bLeftTrigger;
    }

    //rightStickMovedX = isRightStickMovedX ()
    //  getState()で取得した右スティックがX方向に動いたかを返す
    //  rightStickMovedX  右スティックがX方向に動いたか
    public boolean isRightStickMovedX () {
      return sThumbRX != previous_sThumbRX;
    }

    //rightStickMovedY = isRightStickMovedY ()
    //  getState()で取得した右スティックがY方向に動いたかを返す
    //  rightStickMovedY  右スティックがY方向に動いたか
    public boolean isRightStickMovedY () {
      return sThumbRY != previous_sThumbRY;
    }

    //rightTriggerMoved = isRightTriggerMoved ()
    //  getState()で取得した右トリガーが動いたかを返す
    //  rightTriggerMoved  右トリガーが動いたか
    public boolean isRightTriggerMoved () {
      return bRightTrigger != previous_bRightTrigger;
    }

    //open (index)
    //  開く
    //  index  コントローラのインデックス。0～3
    private void open (int index) {
      //コントローラのインデックスを確認する
      if (index < 0 || 3 < index) {
        throw new IllegalArgumentException (String.format ("Controller %d cannot be specified", index));
      }
      //ワークエリアを初期化する
      this.index = index;
      available = false;
      dwPacketNumber = 0;
      wButtons = 0;
      bLeftTrigger = 0;
      bRightTrigger = 0;
      sThumbLX = 0;
      sThumbLY = 0;
      sThumbRX = 0;
      sThumbRY = 0;
      buttonMasks = 0;
      previous_available = false;
      previous_dwPacketNumber = 0;
      previous_wButtons = 0;
      previous_bLeftTrigger = 0;
      previous_bRightTrigger = 0;
      previous_sThumbLX = 0;
      previous_sThumbLY = 0;
      previous_sThumbRX = 0;
      previous_sThumbRY = 0;
      previous_buttonMasks = 0;
      thresholdOfLeftStick2 = (long) THRESHOLD_OF_STICK * (long) THRESHOLD_OF_STICK;
      thresholdOfRightStick2 = (long) THRESHOLD_OF_STICK * (long) THRESHOLD_OF_STICK;
      thresholdOfLeftTrigger = THRESHOLD_OF_TRIGGER;
      thresholdOfRightTrigger = THRESHOLD_OF_TRIGGER;
    }

    //setThresholdOfLeftStick (thresholdOfLeftStick)
    //  左スティックの閾値を設定する
    //  thresholdOfLeftStick  左スティックの閾値
    public void setThresholdOfLeftStick (int thresholdOfLeftStick) {
      thresholdOfLeftStick2 = (long) thresholdOfLeftStick * (long) thresholdOfLeftStick;
    }

    //setThresholdOfRightStick (thresholdOfRightStick)
    //  右スティックの閾値を設定する
    //  thresholdOfRightStick  右スティックの閾値
    public void setThresholdOfRightStick (int thresholdOfRightStick) {
      thresholdOfRightStick2 = (long) thresholdOfRightStick * (long) thresholdOfRightStick;
    }

    //setThresholdOfLeftTrigger (thresholdOfLeftTrigger)
    //  左トリガーの閾値を設定する
    //  thresholdOfLeftTrigger  左トリガーの閾値
    public void setThresholdOfLeftTrigger (int thresholdOfLeftTrigger) {
      this.thresholdOfLeftTrigger = thresholdOfLeftTrigger;
    }

    //setThresholdOfRightTrigger (thresholdOfRightTrigger)
    //  右トリガーの閾値を設定する
    //  thresholdOfRightTrigger  右トリガーの閾値
    public void setThresholdOfRightTrigger (int thresholdOfRightTrigger) {
      this.thresholdOfRightTrigger = thresholdOfRightTrigger;
    }

  }



}  //class XInput
