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

public class LevelController : MonoBehaviour {

    public GameObject segmentsParent, notesParent, pauseCanvas;
    public GameObject redTargetPrefab, greenTargetPrefab, blueTargetPrefab;
    public GameObject segmentPrefab, notePrefab;
    public Text movesText, targetsHitText;
    private List<SegmentController> m_segments;
    public int frequency;
    public float amplitude;
    public bool fullWave;
    public int horizontalDragThreshold;
    public Sprite tapButton;

    // UI
    public SpriteRenderer background;
    public Button pauseButton;
    public Sprite[] colorBackgrounds;
    public Button redButton, greenButton, blueButton;
    public Sprite redButtonSprite, greenButtonSprite, blueButtonSprite, whiteButtonSprite;

    private int m_remainingMoves, m_targetsHit, m_targetsMissed, m_totalTargets, m_level;
    private Camera m_camera;
    private bool m_isAtenuating, m_isPaused;
    private static float segmentRatio = 4.8f;
    private NoteColor m_currentColor;
    private List<NoteController> m_listNotes;

    private void Awake() {
        m_camera = GameObject.Find("Main Camera").GetComponent<Camera>();
    }

    void Start() {
        m_segments = new List<SegmentController>();
        m_targetsHit = m_targetsMissed = 0;
        for (int i = 0; i < 360; i++) {
            GameObject segment = Instantiate(segmentPrefab, segmentsParent.transform);
            if (i == 42) segment.GetComponent<SpriteRenderer>().sprite = tapButton; // the answer to life, universe and everything
            segment.transform.localPosition = new Vector3(i / segmentRatio, 0, 0);
            m_segments.Add(segment.GetComponent<SegmentController>());
        }
        setupLevel();
        changeCurrentColor((int)NoteColor.RED);
    }

    void setupLevel() {
        m_level = PlayerPrefs.HasKey("currentLevel") ? PlayerPrefs.GetInt("currentLevel") : 1;
        m_remainingMoves = PlayerPrefs.HasKey("moves") ? PlayerPrefs.GetInt("moves") : 3;
        m_totalTargets = PlayerPrefs.HasKey("targets") ? PlayerPrefs.GetInt("targets") : 6;

        //////////////////////////////////////
        // Setting m_totalTargets to 3 for testing
        //m_totalTargets = 3;
        //////////////////////////////////////

        // Setup notes, music, etc
        switch (m_level) {
            case 1:
                SoundController.Instance.playMusic("Stage1");
                break;
            case 2:
                SoundController.Instance.playMusic("Stage2");
                break;
            case 3:
                SoundController.Instance.playMusic("Stage3.1");
                break;
        }
        m_listNotes = new List<NoteController>();

        switch (m_level) {
            case 1:
                initTargetsLevel1();
                break;
            case 2:
                initTargetsLevel2();
                break;
            case 3:
                initTargetsLevel3();
                break;
        }

        //For each level, collect all notes and init
        foreach (NoteController n in m_listNotes) {
            if (n != null) {
                n.init();
                n.noteCollected.AddListener(noteCollected);
                n.noteMissed.AddListener(noteMissed);
                switch (m_level) {
                    case 1:
                        n.speed = 6;
                        break;
                    case 2:
                        n.speed = 9;
                        break;
                    case 3:
                        n.speed = 12;
                        break;
                }
            }
        }
    }

    void initTargetsLevel1() {
        StartCoroutine(waitAndSpawn(redTargetPrefab, 6, 2));
        StartCoroutine(waitAndSpawn(redTargetPrefab, -4, 2.6f));
        StartCoroutine(waitAndSpawn(blueTargetPrefab, -3, 5.5f));
        StartCoroutine(waitAndSpawn(blueTargetPrefab, 2, 6f));
        StartCoroutine(waitAndSpawn(greenTargetPrefab, 8, 8.7f));
        StartCoroutine(waitAndSpawn(greenTargetPrefab, -10, 9f));
    }

    void initTargetsLevel2() {
        StartCoroutine(waitAndSpawn(greenTargetPrefab, 8, 1.5f));
        StartCoroutine(waitAndSpawn(greenTargetPrefab, -5, 1.75f));
        StartCoroutine(waitAndSpawn(greenTargetPrefab, 3, 2f));
        StartCoroutine(waitAndSpawn(redTargetPrefab, -10, 4.5f));
        StartCoroutine(waitAndSpawn(redTargetPrefab, 6, 4.8f));
        StartCoroutine(waitAndSpawn(blueTargetPrefab, -2, 6.5f));
        StartCoroutine(waitAndSpawn(greenTargetPrefab, -10, 9.5f));
        StartCoroutine(waitAndSpawn(greenTargetPrefab, 7, 9.75f));
        StartCoroutine(waitAndSpawn(greenTargetPrefab, -4, 10f));
    }

    void initTargetsLevel3() {
        StartCoroutine(waitAndSpawn(blueTargetPrefab, 3, 3.7f));
        StartCoroutine(waitAndSpawn(redTargetPrefab, -8, 7f));
        StartCoroutine(waitAndSpawn(redTargetPrefab, 5, 7.25f));
        StartCoroutine(waitAndSpawn(redTargetPrefab, -2, 7.5f));
        StartCoroutine(waitAndSpawn(greenTargetPrefab, 6, 10.5f));
        StartCoroutine(waitAndSpawn(greenTargetPrefab, -4, 10.8f));
        StartCoroutine(waitAndSpawn(greenTargetPrefab, 1, 11.2f));
        StartCoroutine(waitAndSpawn(redTargetPrefab, 10, 12.7f));
        StartCoroutine(waitAndSpawn(redTargetPrefab, -8, 13f));
        StartCoroutine(waitAndSpawn(greenTargetPrefab, 5, 16f));
        StartCoroutine(waitAndSpawn(greenTargetPrefab, -3, 16.25f));
        StartCoroutine(waitAndSpawn(greenTargetPrefab, 1, 16.5f));
        StartCoroutine(waitAndSpawn(blueTargetPrefab, 10, 21.5f));
        StartCoroutine(waitAndSpawn(blueTargetPrefab, -8, 21.7f));
        StartCoroutine(waitAndSpawn(blueTargetPrefab, 6, 22f));
    }


    IEnumerator waitAndSpawn(GameObject prefab, float y, float time) {
        GameObject newNote = Instantiate(prefab);
        newNote.transform.position = new Vector3(40, y, 0);
        NoteController note = newNote.GetComponent<NoteController>();
        m_listNotes.Add(note);
        newNote.SetActive(false);
        yield return new WaitForSeconds(time);
        newNote.SetActive(true);
        Animator anim = note.GetComponent<Animator>();
        if (note.noteColor == m_currentColor) anim.Play("target_white_spin");
        else anim.Play("target_white_out");
    }

    void Update() {
        if (Input.GetMouseButton(0) && !m_isAtenuating && !detectUIClick()) {
            var worldPosition = m_camera.ScreenToWorldPoint(Input.mousePosition);
            if (worldPosition.x < horizontalDragThreshold) {
                amplitude = Mathf.RoundToInt(worldPosition.y);
                amplitude = Mathf.Clamp(amplitude, -12, 12);
                for (int i = 0; i < 360 / frequency / 2; i++) {
                    m_segments[i].transform.localPosition =
                        new Vector3(
                            i / segmentRatio,
                            sineWave(i, Mathf.Lerp(amplitude, 0, i / 540f)),
                            0);
                    if (!m_segments[i].isTranslucent) m_segments[i].setTranslucent();
                }
            }
        }
        if (Input.GetMouseButtonUp(0) && amplitude != 0) {
            m_isAtenuating = true;
            SoundController.Instance.playSfx("slingshot");
            StartCoroutine(atenuateSignal());

        }

        if (Input.GetKeyDown(KeyCode.Escape)) {
            SceneManager.LoadScene("level");
        }
        movesText.text = "Moves: " + m_remainingMoves;
        targetsHitText.text = "Targets hit: " + m_targetsHit;
        if (m_targetsHit == m_totalTargets) StageWon();
        else if (m_remainingMoves == 0) StageLost();
    }

    float sineWave(int time, float amplitude) {
        return amplitude * Mathf.Sin(time * Mathf.PI / 180f * frequency);
    }

    IEnumerator atenuateSignal() {
        int start, end;
        // first pass
        for (int i = 0; i < 360 / frequency; i++) {
            m_segments[i].transform.localPosition =
                new Vector3(
                    i / segmentRatio,
                    sineWave(i, Mathf.Lerp(amplitude, 0, i / 540f)),
                    0);
            if (m_segments[i].isTranslucent) m_segments[i].setOpaque();
        }
        start = 0;
        end = 360 / frequency;

        // propagate and atenuate
        while (amplitude != 0) {
            for (int i = 0; i < 360; i++) {
                if(i >= start && i <= end) {
                    m_segments[i].transform.localPosition =
                        new Vector3(
                            i / segmentRatio,
                            sineWave(i - start, amplitude),
                            0);
                }
                else {
                    m_segments[i].transform.localPosition =
                        new Vector3(
                            i / segmentRatio,
                            0,
                            0);
                }
            }
            if (amplitude > 0) amplitude -= 0.25f;
            else amplitude += 0.25f;
            start += (int)(segmentRatio + 2.5f);
            end += (int)(segmentRatio + 2.5f);
            yield return null;
        }
        for (int i = 0; i < 360; i++) {
            m_segments[i].transform.localPosition =
                new Vector3(i / segmentRatio, 0, 0);
        }
        m_remainingMoves--;
        m_isAtenuating = false;
    }

    public void StageWon() {
        Debug.Log("pega poha");
        PlayerPrefs.SetInt("gameWon", 1);
        SceneManager.LoadScene("ending");
    }

    public void StageLost() {
        Debug.Log("no céu tem pão?");
        PlayerPrefs.SetInt("gameWon", 0);
        SceneManager.LoadScene("ending");
    }

    public void goToMenu() {
        Time.timeScale = 1;
        SoundController.Instance.stopMusic();
        SceneManager.LoadScene("menu");
    }

    public void restartLevel() {
        Time.timeScale = 1;
        SoundController.Instance.stopMusic();
        SceneManager.LoadScene("level");
    }

    public void togglePause() {
        if (!m_isPaused) {
            Time.timeScale = 0;
            SoundController.Instance.pauseMusic();
            pauseCanvas.SetActive(true);
            m_isPaused = true;
        }
        else {
            Time.timeScale = 1;
            SoundController.Instance.unpauseMusic();
            pauseCanvas.SetActive(false);
            m_isPaused = false;
        }
    }

    bool detectUIClick() {
        // Detect if the click was over some UI element
        var pointer = new PointerEventData(EventSystem.current);
        pointer.position = Input.mousePosition;
        var raycastResults = new List<RaycastResult>();
        EventSystem.current.RaycastAll(pointer, raycastResults);
        return raycastResults.Count > 0;
    }

    IEnumerator noteSpawner() {
        yield return new WaitForSeconds(2f);
        int count = m_totalTargets;
        while (count > 0) {
            GameObject note = Instantiate(notePrefab);
            Vector3 position = new Vector3(43, 0, 0);
            if (Random.Range(0f, 1f) > 0.5f) position.y = 10f;
            else position.y = -10f;
            note.transform.position = position;
            NoteController controller = note.GetComponent<NoteController>();
            controller.init();
            controller.noteCollected.AddListener(noteCollected);
            controller.noteMissed.AddListener(noteMissed);
            controller.setColor((NoteColor)Random.Range(0, 3));
            yield return new WaitForSeconds(2f);
            m_listNotes.Add(controller);
            count--;
        }
    }

    void noteCollected(NoteController target) {
        m_targetsHit++;
        m_listNotes.Remove(target);
    }

    void noteMissed(NoteController target) {
        m_targetsMissed++;
        m_listNotes.Remove(target);
    }

    public void changeCurrentColor(int color) {
        m_currentColor = (NoteColor)color;
        for (int i = 0; i < 360; i++) m_segments[i].setColor(m_currentColor);
        background.sprite = colorBackgrounds[color];
        switch (color) {
            case 0:
                redButton.image.sprite = whiteButtonSprite;
                greenButton.image.sprite = greenButtonSprite;
                blueButton.image.sprite = blueButtonSprite;
                break;
            case 1:
                redButton.image.sprite = redButtonSprite;
                greenButton.image.sprite = whiteButtonSprite;
                blueButton.image.sprite = blueButtonSprite;
                break;
            case 2:
                redButton.image.sprite = redButtonSprite;
                greenButton.image.sprite = greenButtonSprite;
                blueButton.image.sprite = whiteButtonSprite;
                break;
        }

        foreach(NoteController n in m_listNotes) {
            if (n != null) {
                Animator anim = n.GetComponent<Animator>();
                if (anim != null) {
                    if (n.noteColor == m_currentColor) anim.Play("target_white_spin");
                    else anim.Play("target_white_out");
                }
            }
        }
    }
}
