using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.InputSystem.Users;
using UnityEngine.InputSystem.LowLevel;
using DG.Tweening;

public class GameController : MonoBehaviour
{
    public bool singlePlayerMode = true;
    public bool joinedLegsMode = true;
    public GameObject gamePads;
    public Image logo;
    public Player player;

    private float startPlayerX;
    public float timescale = 1;
    public float slowmoScale = 0.2f;

    public static bool logoHidden = false;
    public static bool easyMode = false;

    [System.Serializable]
    public class Half {
        public BodyPart arm;
        public BodyPart leg;
        public Pincher pincher;

        private PlayerControls controls;

        public void SetupControls(PlayerControls _controls) {
            GameController gc = FindObjectOfType<GameController>();
            controls = _controls;
           
            if (gc.singlePlayerMode) {
                if (gc.controlIndex == 0) {
                    _controls.Gameplay.LeftStick.performed += v => gc.leftHalf.arm.input = v.ReadValue<Vector2>();
                    _controls.Gameplay.LeftStick.canceled += v => gc.leftHalf.arm.input = Vector2.zero;

                    _controls.Gameplay.RightStick.performed += v => gc.rightHalf.arm.input = v.ReadValue<Vector2>();
                    _controls.Gameplay.RightStick.canceled += v => gc.rightHalf.arm.input = Vector2.zero;
                }
                if (gc.controlIndex == 1 || gc.joinedLegsMode) {
                    _controls.Gameplay.LeftStick.performed += v => gc.leftHalf.leg.input = v.ReadValue<Vector2>();
                    _controls.Gameplay.LeftStick.canceled += v => gc.leftHalf.leg.input = Vector2.zero;

                    _controls.Gameplay.RightStick.performed += v => gc.rightHalf.leg.input = v.ReadValue<Vector2>();
                    _controls.Gameplay.RightStick.canceled += v => gc.rightHalf.leg.input = Vector2.zero;
                }
            } else {
                _controls.Gameplay.LeftStick.performed += v => leg.input = v.ReadValue<Vector2>();
                _controls.Gameplay.LeftStick.canceled += v => leg.input = Vector2.zero;
                _controls.Gameplay.RightStick.performed += v => arm.input = v.ReadValue<Vector2>();
                _controls.Gameplay.RightStick.canceled += v => arm.input = Vector2.zero;
            }

            _controls.Gameplay.Pinch.performed += v => pincher.SetPinchPercentage(v.ReadValue<float>());
            _controls.Gameplay.Pinch.canceled += v => pincher.SetPinchPercentage(0);
        }

        public void OnDestroy() {
            if (controls != null)
                controls.Disable();
        }
    }

    PlayerControls controls;
    public Half leftHalf;
    public Half rightHalf;

    public int controlIndex = 0;

    private void Awake() {
        controls = new PlayerControls();
        controls.Enable();
        controls.Global.Start.performed += Start_performed;
        controls.Global.Select.performed += Select_performed;
        controls.Global.ToggleMode.performed += ToggleEasyMode;
        controls.Global.SlowMo.performed += OnSlowMo;
        controls.Global.SlowMo.canceled += OnSlowMoReleased;

        startPlayerX = player.transform.position.x;

        foreach (var device in InputSystem.devices) {
            if (device is Gamepad) {
                AssignDeviceControl(device);
            }
        }

        if (controlIndex < 2) {
            InputUser.onUnpairedDeviceUsed += InputUser_onUnpairedDeviceUsed;
            InputUser.listenForUnpairedDeviceActivity = 2 - controlIndex;
        }

        if (logoHidden) {
            logo.gameObject.SetActive(false);
        }

        UpdateEasyMode();
    }


    [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
    static void ResetDomain() {
        logoHidden = false;
        easyMode = false;
    }

    private void OnSlowMo(InputAction.CallbackContext obj) {
        DOVirtual.Float(timescale, slowmoScale, 0.1f, (f) => {
            Time.timeScale = f;
            Time.fixedDeltaTime = 0.01f * Time.timeScale;
        });
    }

    private void OnSlowMoReleased(InputAction.CallbackContext obj) {
        DOVirtual.Float(slowmoScale, timescale, 0.1f, (f) => {
            Time.timeScale = f;
            Time.fixedDeltaTime = 0.01f * Time.timeScale;
        });
    }

    private void Select_performed(InputAction.CallbackContext obj) {
        gamePads.SetActive(!gamePads.activeSelf);
    }

    private void ToggleEasyMode(InputAction.CallbackContext obj) {
        easyMode = !easyMode;

        UpdateEasyMode();
    }

    private void UpdateEasyMode() {
        player.frictionJoint.enabled = easyMode;
    }

    private void AssignDeviceControl(InputDevice device) {
        if (controlIndex >= 2) return;

        Debug.Log($"Assigning device: {device} ({device.displayName}) to player id: {controlIndex}");

        // get a new InputUser, now paired with the device
        InputUser user = InputUser.PerformPairingWithDevice(device);
        // Create a new instance of input actions to prevent InputUser from triggering actions on another InputUser.
        PlayerControls controlsForThisUser = new PlayerControls();
        // you must enable the controls to use them
        controlsForThisUser.Enable();

        // the real work is done for us in InputUser
        user.AssociateActionsWithUser(controlsForThisUser);

        if (controlIndex == 0) {
            leftHalf.SetupControls(controlsForThisUser);
        } else if (controlIndex == 1) {
            rightHalf.SetupControls(controlsForThisUser);
        }

        controlIndex++;
    }

    private void OnDestroy() {
        controls.Disable();
        leftHalf.OnDestroy();
        rightHalf.OnDestroy();
    }

    void BeginJoining() {
        InputUser.listenForUnpairedDeviceActivity = 2;
    }

    void EndJoining() {
        InputUser.listenForUnpairedDeviceActivity = 0;
    }

    private void InputUser_onUnpairedDeviceUsed (InputControl control, InputEventPtr eventPtr ) {
        // Ignore anything but button presses.
        if (!(control is ButtonControl))
            return;
        Debug.Log("Unpaired device detected" + control.device.displayName + ", activity count: " + InputUser.listenForUnpairedDeviceActivity);

        AssignDeviceControl(control.device);

        InputUser.listenForUnpairedDeviceActivity--;
    }

    private void Start_performed(InputAction.CallbackContext obj) {
        Restart();
    }

    // Update is called once per frame
    void Update()
    {
        if (Keyboard.current[Key.Escape].wasPressedThisFrame) {
            Application.Quit();
        }

        if (Keyboard.current[Key.E].wasPressedThisFrame) {
            player.transform.position = new Vector3(114, 1, 0);
        }

        if (!logoHidden) {
            if (Mathf.Abs(player.transform.position.x - startPlayerX) > 1) {
                logoHidden = true;
                logo.CrossFadeAlpha(0f, 0.5f, false);
            }
        }
    }

    public void Restart() {
        SceneManager.LoadScene(0);
    }
}
