using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;

public class RayShooter : MonoBehaviour
{
    [Header("References")]
    public GameObject rayPrefab;
    public LineRenderer visualizer;
    public Image powerBar; 

    [Header("Properties")]
    public float visualizerYSpeed = 0.1f;
    public float visualizeWidth = 4;
    public float highFreq = 1400;
    public float lowFreq = 800;

    [HideInInspector]
    public bool AllowShoot = false;

    Vector2 lastPoint = Vector2.zero;
    Coroutine visualizerRoutine;
    Vector2 visualizerInitPos;

    void Start()
    {
        visualizerInitPos = (Vector2)visualizer.transform.position;
    }

    bool cancelDrag = false;
    bool dragging = false;
    public void OnBeginDrag(BaseEventData data)
    {
        if (!AllowShoot)
            return;

        PointerEventData pdata = data as PointerEventData;
        Vector2 point = Camera.main.ScreenToWorldPoint(pdata.position);

        //Clamp point
        point.x = Mathf.Clamp(point.x, -visualizeWidth, visualizeWidth);
        point.y = 0;

        //Debug.DrawRay(point, Vector3.up * 1f, Color.blue, 0.1f);        
        lastPoint = point;

        //Init visualizer
        visualizer.numPositions = 1;
        visualizer.transform.position = visualizerInitPos;
        visualizer.SetPosition(0, Vector3.zero);
        visualizerRoutine = StartCoroutine(Visualize());
        deltaY = 0;
        lastDragTime = Time.time;
        currentFreq = 0;

        dragging = true;
        cancelDrag = false;

        print("Begin Drag");

        
    }

    float currentFreq = 0;
    float lastDragTime = 0;
    int quantized = 0;
    public void OnDrag(BaseEventData data)
    {
        if (!AllowShoot)
            return;

        if (cancelDrag || !dragging)
            return;

        PointerEventData pdata = data as PointerEventData;
        Vector2 point = Camera.main.ScreenToWorldPoint(pdata.position);

        float dt = Time.time - lastDragTime;
        print("dt: " + dt);

        if (Mathf.Approximately(Mathf.Abs(pdata.delta.x), 0))
            return;
        float frq = Mathf.Abs(pdata.delta.x);
        currentFreq = currentFreq * 0.95f + frq * 0.05f; //Moving average
        lastDragTime = Time.time;

        float df = Mathf.InverseLerp(lowFreq, highFreq, currentFreq) * 2;
        int quant = Mathf.Clamp(Mathf.RoundToInt(df), 0, 2);
        print("FREQ: " + currentFreq + ", DF: " + df + ", QUANT: " + quant);
        quantized = quant;

        //Clamp point        
        point.x = Mathf.Clamp(point.x, -visualizeWidth, visualizeWidth);
        point.y = visualizerInitPos.y;

        //Add tween points
        int stepCount = Mathf.RoundToInt(deltaY / visualizerYSpeed);
        for (int i = 0; i < stepCount; i++)
        {
            Vector2 p = new Vector2();
            float t = Mathf.Sqrt((float)(i) / stepCount);
            p.x = Mathf.SmoothStep(lastPoint.x, point.x, t);
            p.y = point.y - visualizer.transform.position.y;
            p.y = Mathf.Lerp(p.y + deltaY, p.y, (float)i / stepCount);
            visualizer.numPositions++;
            visualizer.SetPosition(visualizer.numPositions - 1, p);
        }

        lastPoint = point;

        deltaY = 0;

        visualizer.numPositions++;
        visualizer.SetPosition(visualizer.numPositions - 1, point - (Vector2)visualizer.transform.position);
    }

    public void OnEndDrag(BaseEventData data)
    {
        if (!AllowShoot)
            return;

        if (cancelDrag || !dragging)
            return;

        dragging = false;

        PointerEventData pdata = data as PointerEventData;
        Vector2 point = Camera.main.ScreenToWorldPoint(pdata.position);

        //Clamp point
        point.x = Mathf.Clamp(point.x, -visualizeWidth, visualizeWidth);
        point.y = visualizerInitPos.y;

        StopCoroutine(visualizerRoutine);

        print("End Drag");

        shootRay();

        currentFreq = 0;
    }

    float deltaY = 0;

    IEnumerator Visualize()
    {
        yield return null;
        while (true)
        {
            Vector2 pos = visualizer.transform.position;
            pos.y += visualizerYSpeed;
            deltaY += visualizerYSpeed;
            visualizer.transform.position = pos;

            yield return new WaitForEndOfFrame();
        }
    }

    Color currentPowerColor = new Color(0, 0, 0, 0);
    
    void Update()
    {
        if (dragging)
        {         
            currentPowerColor = Config.RayColors[quantized];
        }
        else
        {
            currentPowerColor = new Color(0, 0, 0, 0);
        }
        powerBar.color = Color.Lerp(powerBar.color, currentPowerColor, Time.deltaTime * 4);

        if (Input.GetKeyUp(KeyCode.R))
            shootRay(Config.RayColor.Red);
        else if (Input.GetKeyUp(KeyCode.G))
            shootRay(Config.RayColor.Green);
        else if (Input.GetKeyUp(KeyCode.B))
            shootRay(Config.RayColor.Blue);
    }


    void shootRay(Config.RayColor? chosenColor = null)
    {
        int cnt = 1;
        if (chosenColor == null)
        {
            cnt = quantized + 1;
        }
        else
        {
            cnt = (int)chosenColor + 1;
        }
        visualizer.numPositions = 0;

        LightRay ray = Instantiate<GameObject>(rayPrefab).GetComponent<LightRay>();
        ray.transform.position = transform.position;
        ray.SetColor((Config.RayColor)(cnt - 1));
        ray.SetDirection(Vector2.up);
        SineMotion osc = ray.GetComponentInChildren<SineMotion>();
        osc.omegaX = cnt * 20;

        ray.gameObject.SetActive(true);

        AllowShoot = false;
    }
}
