using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using Mirror;

public class PlayerController : NetworkBehaviour
{
    // =======================
    // Movement Parameters
    // =======================
    [Header("Movement Parameters")]
    public float moveSpeed = 5f;
    public float acceleration = 10f;
    public float deceleration = 10f;

    // =======================
    // Jump Parameters
    // =======================
    [Header("Jump Parameters")]
    public float maxJumpHeight = 4f;
    public float timeToJumpApex = 0.4f;
    public int extraJumps = 1;

    // =======================
    // Jump Timing
    // =======================
    [Header("Jump Timing")]
    public float coyoteTime = 0.2f;
    public float jumpBufferTime = 0.2f;

    // =======================
    // Fall Parameters
    // =======================
    [Header("Fall Parameters")]
    public float maxFallSpeed = 20f;

    // =======================
    // Ledge Detection
    // =======================
    [Header("Ledge Detection")]
    public Transform groundCheck;
    public float groundCheckRadius = 0.1f;
    public LayerMask groundLayer;
    public Transform wallCheck;
    public float wallCheckDistance = 0.1f;

    // =======================
    // Private Variables
    // =======================
    private Rigidbody2D rb;
    private bool isFacingRight = true;

    private bool isGrounded;
    private bool isTouchingWall;
    private bool canDoubleJump;
    private float coyoteTimeCounter;
    private float jumpBufferCounter;

    private float jumpVelocity;
    private float gravity;

    // Input variables
    private float moveInput;
    private bool jumpPressed;
    private bool jumpHeld;

    // =======================
    // Mobile Control Variables
    // =======================
    [Header("Mobile Controls")]
    public bool isLeftPressed = false;
    public bool isRightPressed = false;
    public bool isJumpPressed = false;

    // UI References
    private Canvas mobileCanvas;
    private Button leftButton;
    private Button rightButton;
    private Button jumpButton;

    void Start()
    {
        rb = GetComponent<Rigidbody2D>();

        // Calculate gravity and jump velocity based on desired jump height and time to apex
        gravity = (-2 * maxJumpHeight) / Mathf.Pow(timeToJumpApex, 2);
        rb.gravityScale = gravity / Physics2D.gravity.y; // Adjust gravity scale relative to Unity's default gravity
        jumpVelocity = Mathf.Abs(gravity) * timeToJumpApex;

        // Only set up mobile controls for the local player
        if (isLocalPlayer)
        {
            SetupMobileControls();
        }
    }

    void SetupMobileControls()
    {
        // Create Canvas if it doesn't exist
        GameObject canvasObject = GameObject.Find("MobileCanvas");
        if (canvasObject == null)
        {
            canvasObject = new GameObject("MobileCanvas");
            canvasObject.layer = LayerMask.NameToLayer("UI");
            mobileCanvas = canvasObject.AddComponent<Canvas>();
            mobileCanvas.renderMode = RenderMode.ScreenSpaceOverlay;
            CanvasScaler scaler = canvasObject.AddComponent<CanvasScaler>();
            scaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
            scaler.referenceResolution = new Vector2(1080, 1920);
            scaler.matchWidthOrHeight = 0.5f;
            canvasObject.AddComponent<GraphicRaycaster>();
        }
        else
        {
            mobileCanvas = canvasObject.GetComponent<Canvas>();
        }

        // Define positions relative to screen size
        Vector2 leftPos = new Vector2(Screen.width * 0.1f, Screen.height * 0.1f);
        Vector2 rightPos = new Vector2(Screen.width * 0.2f, Screen.height * 0.1f);
        Vector2 jumpPos = new Vector2(Screen.width * 0.9f, Screen.height * 0.1f);

        // Create Buttons with relative positions
        leftButton = CreateButton("LeftButton", "←", leftPos);
        rightButton = CreateButton("RightButton", "→", rightPos);
        jumpButton = CreateButton("JumpButton", "↑", jumpPos);

        // Set Parent
        leftButton.transform.SetParent(mobileCanvas.transform, false);
        rightButton.transform.SetParent(mobileCanvas.transform, false);
        jumpButton.transform.SetParent(mobileCanvas.transform, false);
    }

    Button CreateButton(string name, string buttonText, Vector2 position)
    {
        // Create Button GameObject
        GameObject buttonObject = new GameObject(name);
        buttonObject.layer = LayerMask.NameToLayer("UI");
        Button button = buttonObject.AddComponent<Button>();
        Image buttonImage = buttonObject.AddComponent<Image>();
        buttonImage.color = new Color(1, 1, 1, 0.5f); // Semi-transparent white

        // Set RectTransform
        RectTransform rect = button.GetComponent<RectTransform>();
        rect.sizeDelta = new Vector2(80, 80);
        rect.anchorMin = new Vector2(0, 0);
        rect.anchorMax = new Vector2(0, 0);
        rect.pivot = new Vector2(0.5f, 0.5f);
        rect.anchoredPosition = position;

        // Add Text
        GameObject textObject = new GameObject("Text");
        textObject.transform.SetParent(buttonObject.transform, false);
        Text text = textObject.AddComponent<Text>();
        text.text = buttonText;
        text.font = Resources.GetBuiltinResource<Font>("Arial.ttf");
        text.alignment = TextAnchor.MiddleCenter;
        text.color = Color.black;

        RectTransform textRect = text.GetComponent<RectTransform>();
        textRect.anchorMin = Vector2.zero;
        textRect.anchorMax = Vector2.one;
        textRect.sizeDelta = Vector2.zero;

        // Add Event Trigger for Pointer Down and Pointer Up
        EventTrigger trigger = buttonObject.AddComponent<EventTrigger>();

        // Pointer Down Event
        EventTrigger.Entry pointerDown = new EventTrigger.Entry();
        pointerDown.eventID = EventTriggerType.PointerDown;
        pointerDown.callback.AddListener((data) => { OnButtonDown(name); });
        trigger.triggers.Add(pointerDown);

        // Pointer Up Event
        EventTrigger.Entry pointerUp = new EventTrigger.Entry();
        pointerUp.eventID = EventTriggerType.PointerUp;
        pointerUp.callback.AddListener((data) => { OnButtonUp(name); });
        trigger.triggers.Add(pointerUp);

        return button;
    }

    void OnButtonDown(string buttonName)
    {
        switch (buttonName)
        {
            case "LeftButton":
                isLeftPressed = true;
                leftButton.GetComponent<Image>().color = Color.gray; // Visual feedback
                break;
            case "RightButton":
                isRightPressed = true;
                rightButton.GetComponent<Image>().color = Color.gray;
                break;
            case "JumpButton":
                isJumpPressed = true;
                jumpHeld = true; // To allow variable jump height while holding
                jumpButton.GetComponent<Image>().color = Color.gray;
                break;
        }
    }

    void OnButtonUp(string buttonName)
    {
        switch (buttonName)
        {
            case "LeftButton":
                isLeftPressed = false;
                leftButton.GetComponent<Image>().color = new Color(1, 1, 1, 0.5f); // Reset color
                break;
            case "RightButton":
                isRightPressed = false;
                rightButton.GetComponent<Image>().color = new Color(1, 1, 1, 0.5f);
                break;
            case "JumpButton":
                isJumpPressed = false;
                jumpHeld = false;
                jumpButton.GetComponent<Image>().color = new Color(1, 1, 1, 0.5f);
                break;
        }
    }

    void Update()
    {
        if (!isLocalPlayer) return;

        // Handle input
        HandleInput();

        // Update timers
        if (isGrounded)
        {
            coyoteTimeCounter = coyoteTime;
            canDoubleJump = extraJumps > 0;
        }
        else
        {
            coyoteTimeCounter -= Time.deltaTime;
        }

        if (jumpPressed)
        {
            jumpBufferCounter = jumpBufferTime;
            jumpPressed = false; // Reset jumpPressed after buffering
        }
        else
        {
            if (jumpBufferCounter > 0)
                jumpBufferCounter -= Time.deltaTime;
        }

        // Handle jumping with Jump Buffering and Coyote Time
        if (jumpBufferCounter > 0f && coyoteTimeCounter > 0f)
        {
            PerformJump();
            jumpBufferCounter = 0f; // Reset jump buffer after jumping
        }
    }

    void FixedUpdate()
    {
        if (!isLocalPlayer) return;

        // Check ground and wall status
        isGrounded = Physics2D.OverlapCircle(groundCheck.position, groundCheckRadius, groundLayer);
        isTouchingWall = Physics2D.Raycast(wallCheck.position, transform.right, wallCheckDistance, groundLayer);

        // Apply horizontal movement with acceleration and deceleration
        float targetSpeed = moveInput * moveSpeed;
        float speedDifference = targetSpeed - rb.velocity.x;
        float accelRate = (Mathf.Abs(targetSpeed) > 0.01f) ? acceleration : deceleration;
        float movement = Mathf.Pow(Mathf.Abs(speedDifference) * accelRate, 0.9f) * Mathf.Sign(speedDifference);
        rb.AddForce(movement * Vector2.right);

        // Clamp fall speed
        if (rb.velocity.y < -maxFallSpeed)
        {
            rb.velocity = new Vector2(rb.velocity.x, -maxFallSpeed);
        }

        // Variable jump height
        if (rb.velocity.y > 0 && !jumpHeld)
        {
            rb.velocity = new Vector2(rb.velocity.x, rb.velocity.y * 0.5f);
        }

        // Flip character based on movement direction
        if (moveInput > 0 && !isFacingRight)
            Flip();
        else if (moveInput < 0 && isFacingRight)
            Flip();
    }

    /// <summary>
    /// Handles player input, including mobile buttons.
    /// </summary>
    void HandleInput()
    {
        moveInput = 0f;

        // Mobile button inputs
        if (isLeftPressed)
        {
            moveInput -= 1f;
        }
        if (isRightPressed)
        {
            moveInput += 1f;
        }

        // Handle jump button
        if (isJumpPressed)
        {
            jumpPressed = true;
        }
    }

    /// <summary>
    /// Executes the jump logic, including double jumps.
    /// </summary>
    void PerformJump()
    {
        if (isGrounded || coyoteTimeCounter > 0f)
        {
            rb.velocity = new Vector2(rb.velocity.x, jumpVelocity);
            // Optionally, trigger jump animation or sound here
        }
        else if (canDoubleJump && !isGrounded)
        {
            rb.velocity = new Vector2(rb.velocity.x, jumpVelocity);
            canDoubleJump = false;
        }
    }

    /// <summary>
    /// Flips the character's orientation.
    /// </summary>
    void Flip()
    {
        isFacingRight = !isFacingRight;
        Vector3 localScale = transform.localScale;
        localScale.x *= -1;
        transform.localScale = localScale;
    }

    /// <summary>
    /// Visualize ground and wall checks in the Scene view.
    /// </summary>
    void OnDrawGizmosSelected()
    {
        if (groundCheck != null)
        {
            Gizmos.color = Color.green;
            Gizmos.DrawWireSphere(groundCheck.position, groundCheckRadius);
        }

        if (wallCheck != null)
        {
            Gizmos.color = Color.red;
            Gizmos.DrawLine(wallCheck.position, wallCheck.position + transform.right * wallCheckDistance);
        }
    }
}



Find buttons with “GameObject.Find” function