using System;
using System.IO.Ports;
using UnityEngine;

public class espwrite_inverse_tags : MonoBehaviour
{
    [SerializeField]
    private GameObject Index;

    [SerializeField]
    private GameObject physicsObject;

    private string portName;
    private SerialPort ESP32_port;
    private bool isDebugMode = false;

    double time;
    public double index_val;

    [Range(0.035f, 1f)]
    public double timeDelay = 0.045f;

    private int idx_min = 36;

    [Header("Index thimble management")]
    [Range(36, 255)]
    public int idx_max = 255;

    public float maxDistance = 0.7071069f; // Maximum distance from the plane to the object top
    public float minDistance = 0.187107f; // Minimum distance from the plane to the object top

    byte[] outBuffer = new byte[1];
    private float previousYPosition = 0f;

    public void SetCOMPort(string selectedPort)
    {
        portName = selectedPort;
        if (!string.IsNullOrEmpty(portName) && !isDebugMode)
        {
            ESP32_port = new SerialPort(portName, 115200);
            try
            {
                ESP32_port.Open();
                Debug.Log("COM port opened successfully: " + portName);
                this.enabled = true;
            }
            catch (Exception e)
            {
                Debug.LogError("Failed to open COM port: " + e.Message);
                this.enabled = false;
            }
        }
        else
        {
            Debug.Log("Debug mode or empty port. No COM port opened.");
            this.enabled = true;
        }
    }

    public void SetDebugMode(bool debug)
    {
        isDebugMode = debug;
    }

    void Start()
    {
        time = 0f;
        timeDelay = 0.045f;
        if (Index != null)
        {
            previousYPosition = physicsObject.transform.position.y;
        }
        this.enabled = false;
    }

    void Update()
    {
        if (!isDebugMode && (ESP32_port == null || !ESP32_port.IsOpen))
        {
            Debug.LogWarning("ESP32_port is null or not open.");
            return;
        }

        time += 1f * Time.deltaTime;
        if (time >= timeDelay)
        {
            time = 0f;
            var interactionManager = Index?.GetComponent<fingertip_interaction_manager_tags>();
            if (interactionManager == null)
            {
                Debug.LogError("fingertip_interaction_manager component not found on Index GameObject.");
                return;
            }

            float currentYPosition = physicsObject.transform.position.y;
            if (currentYPosition > previousYPosition)
            {
                if (interactionManager.isColliding)
                {

                    float displacement = (float)interactionManager.getDisplacement();
                    displacement = Mathf.Clamp(displacement, 0, maxDistance);
                    index_val = map(displacement, 0, maxDistance, idx_min, idx_max);

                    Debug.Log("Index value (inverted force): " + index_val);
                }
                else
                {
                    // No collision, set to minimum force
                    index_val = idx_min;
                }
            }
            else
            {
                // Ball is ascending, set to minimum
                index_val = idx_min;
            }

            // Ensure index_val is within range
            index_val = Mathf.Clamp((float)index_val, idx_min, idx_max);

            int index_val_int = System.Convert.ToInt32(System.Math.Round(index_val));
            Serial_Data_Write(index_val_int);
            // Debug.Log("Index value (inverted force): " + index_val_int);
            previousYPosition = currentYPosition;
        }
    }

    void OnDisable()
    {
        Serial_Data_Write(36);
        ClosePort();
    }

    void OnApplicationQuit()
    {
        Serial_Data_Write(36);
        ClosePort();
    }

    void ClosePort()
    {
        if (ESP32_port != null && ESP32_port.IsOpen)
        {
            ESP32_port.Close();
            ESP32_port.Dispose();
            Debug.Log("COM port closed.");
        }
    }

    void Serial_Data_Write(int motor_idx_val)
    {
        if (!isDebugMode && ESP32_port != null && ESP32_port.IsOpen)
        {
            outBuffer[0] = System.Convert.ToByte(motor_idx_val);
            ESP32_port.Write(outBuffer, 0, outBuffer.Length);
        }
        else if (isDebugMode)
        {
            // Debug.Log("Debug Mode: Value calculated but not sent to COM port: " + motor_idx_val);
        }
        else
        {
            Debug.LogWarning("Attempted to write to a closed or uninitialized COM port.");
        }
    }

    public void UpdateThimbleMin(double sliderValue)
    {
        idx_min = Mathf.RoundToInt((float)map(sliderValue, 0, 1, 36, idx_max));
    }

    public void UpdateThimbleMax(double sliderValue)
    {
        idx_max = Mathf.RoundToInt((float)map(sliderValue, 0, 1, idx_min, 255));
    }

    public bool IsPortOpen()
    {
        return ESP32_port != null && ESP32_port.IsOpen;
    }

    double map(double s, double a1, double a2, double b1, double b2)
    {
        return b1 + (s - a1) * (b2 - b1) / (a2 - a1);
    }

}