using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class WaveFormMover : MonoBehaviour {
    public Transform waveFormWrapperTransform; // For speed and convenience.
    public float minNoteStep; // What is the default note length.
    public Transform waveFormWrapper; // For speed and convenience.
    public float yOffset; // For single block.
    public float simultaneousYOffset; // For for two blocks together.
    public GameObject singleBlockPrefab; // Use this to build wave block.
    public GameObject simultaneousBlockPrefab; // Use this to build wave block.
    public Transform waveFormSocketWrapper; // For speed and convenience.
    public GameOverControl gameOverControl; // For speed and convenience.
    public SingleMusicPattern [] musicPatterns; // All the music patterns.

    private RectTransform waveFormWrapperRectTranform; // For speed and convenience.
    private float xPosition; // Where to place on x.
    private float yPosition; // Where to place on y.
    private float originalYPosition; // Where to reset x.
    private float defaultBlockWidth; // Scale this width with time delta.

    [System.Serializable]
    public class SingleMusicPattern {
        public MusicBlock [] musicBlocks;
    }

    [System.Serializable]
    public class MusicBlock {
        public float relativeStartTime; // When player has to do the input.
        public float relativeEndTime; // When player has to release the input.
        public StaticConstants.WaveBlockType waveBlockType; // What block and where to put.
        [HideInInspector]
        public GameObject blockPrefab; // Use this to build wave block.
        [HideInInspector]
        public float yOffset; // Move up or down.
        [HideInInspector]
        public GameObject musicBlock; // Use this to build wave block.
        [HideInInspector]
        public Transform musicBlockTransform; // Use this to build wave block.
        [HideInInspector]
        public RectTransform musicBlockRectTransform; // Use this to build wave block.
        [HideInInspector]
        public UnityEngine.UI.Image musicBlockImage; // For speed and convenience.
        [HideInInspector]
        public float deltaCoefficient; // Delta between start and end time.

        public void Initialize (ref GameObject singleBlockPrefab, ref GameObject simultaneousBlockPrefab, ref float minNoteStep, ref float defaultBlockWidth, ref Transform waveFormSocketWrapper, ref float yOffset, ref float simultaneousYOffset, Color waveFormTransparancyColor, ref RectTransform waveFormTopSide, ref RectTransform waveFormBottomSide) {
            deltaCoefficient = this.relativeEndTime - this.relativeStartTime;
            this.yOffset = yOffset;
            float yScale; // To scale properly vertically each block.
            float yPivot; // To expand to the correct side.
            switch (waveBlockType) {
                case StaticConstants.WaveBlockType.bottom:
                    this.yOffset = waveFormBottomSide.anchorMin.y;
                    blockPrefab = singleBlockPrefab;
                    yPivot = 1f;
                    yScale = .041f;
                    break;
                case StaticConstants.WaveBlockType.simultaneous:
                    this.yOffset = waveFormBottomSide.anchorMin.y;
                    blockPrefab = simultaneousBlockPrefab;
                    yPivot = 1f;
                    yScale = .069f;
                    break;
                default:
                    blockPrefab = singleBlockPrefab;
                    this.yOffset = waveFormTopSide.anchorMin.y;
                    yPivot = 0f;
                    yScale = .041f;
                    break;
            }
            musicBlock = Instantiate (blockPrefab);
            musicBlockTransform = musicBlock.transform;
            musicBlockTransform.SetParent (waveFormSocketWrapper, true);
            musicBlockRectTransform = musicBlockTransform.GetComponent <RectTransform> ();
            musicBlockImage = musicBlockRectTransform.GetComponent <UnityEngine.UI.Image> ();
            musicBlockImage.color = waveFormTransparancyColor;
            float synchWidthToNoteLength = .625f; // Helper.
            musicBlockRectTransform.pivot = new Vector2 (0f, yPivot);
            musicBlockRectTransform.sizeDelta = new Vector2 (deltaCoefficient * defaultBlockWidth * synchWidthToNoteLength, Screen.height * yScale);

            musicBlockRectTransform.anchoredPosition = new Vector2 (0f, 0f);
            Vector2 newPosition = new Vector2 (minNoteStep * this.relativeStartTime, this.yOffset); // For speed and convenience.
            musicBlockRectTransform.anchorMin = newPosition;
            musicBlockRectTransform.anchorMax = newPosition;
            if (waveBlockType == StaticConstants.WaveBlockType.simultaneous) {
                Transform secondBlockTransform = musicBlockTransform.GetChild (0);
                secondBlockTransform.SetParent (waveFormSocketWrapper, true);
                RectTransform secondBlockRectTransform = secondBlockTransform.GetComponent <RectTransform> ();
                UnityEngine.UI.Image secondBlockImage = secondBlockTransform.GetComponent <UnityEngine.UI.Image> ();
                secondBlockImage.color = waveFormTransparancyColor;
                secondBlockRectTransform.pivot = new Vector2 (0f, 0f);
                secondBlockRectTransform.sizeDelta = new Vector2 ((deltaCoefficient * defaultBlockWidth * synchWidthToNoteLength), Screen.height * yScale);
                secondBlockRectTransform.anchoredPosition = new Vector2 (0f, 0f);
                newPosition = new Vector2 (minNoteStep * this.relativeStartTime, waveFormTopSide.anchorMin.y); // For speed and convenience.
                secondBlockRectTransform.anchorMin = newPosition;
                secondBlockRectTransform.anchorMax = newPosition;
            }
        }
    }

    [HideInInspector]
    public float simultaneousHeightDelta; // To know, how high must generated blocks be.
    [HideInInspector]
    public float singleHeightDelta; // To know, how high must generated blocks be.

    public RectTransform waveFormTopSide; // For speed and convenience.
    public RectTransform waveFormBottomSide; // For speed and convenience.

    public void Initialize () {
        waveFormWrapperRectTranform = waveFormWrapperTransform.GetComponent <RectTransform> ();
        originalYPosition = waveFormWrapperRectTranform.anchorMin.y;
        yPosition = originalYPosition;
        xPosition = 1f;
        defaultBlockWidth = minNoteStep * DGA.control.scaleToMovementSlowDown * (Screen.width / 1280f);

        for (int i = 0; i < musicPatterns.Length; ++i)
            for (int i2 = 0; i2 < musicPatterns [i].musicBlocks.Length; ++i2)
                musicPatterns [i].musicBlocks [i2].Initialize (ref singleBlockPrefab, ref simultaneousBlockPrefab, ref minNoteStep, ref defaultBlockWidth, ref waveFormSocketWrapper, ref yOffset, ref simultaneousYOffset, DGA.control.waveFormTransparancyColor, ref waveFormTopSide, ref waveFormBottomSide);

        for (int i = musicPatterns [0].musicBlocks.Length - 1; i >= 0; --i) {
            MusicBlock currentMusicBlock = musicPatterns [0].musicBlocks [i];
            if (currentMusicBlock.waveBlockType == StaticConstants.WaveBlockType.simultaneous) {
                simultaneousHeightDelta = currentMusicBlock.musicBlockRectTransform.sizeDelta.y;
                break;
            }
        }

        for (int i = musicPatterns [0].musicBlocks.Length - 1; i >= 0; --i) {
            MusicBlock currentMusicBlock = musicPatterns [0].musicBlocks [i];
            if (currentMusicBlock.waveBlockType == StaticConstants.WaveBlockType.top || currentMusicBlock.waveBlockType == StaticConstants.WaveBlockType.bottom) {
                singleHeightDelta = currentMusicBlock.musicBlockRectTransform.sizeDelta.y;
                break;
            }
        }
    }

    private void MoveWaveForm () {
        if (xPosition > -2.4f) {
            Vector2 newAnchoredPosition = new Vector2 (xPosition, yPosition);
            waveFormWrapperRectTranform.anchorMin = newAnchoredPosition;
            waveFormWrapperRectTranform.anchorMax = newAnchoredPosition;
            xPosition -= (DGA.control.clampedDeltaTime * DGA.control.tempo * .125f) * (1280f / Screen.width);
        } else {
            xPosition = 1f;
            yPosition = originalYPosition;
            for (int i = waveFormSocketWrapper.childCount - 1; i >= 0; --i)
                Destroy (waveFormSocketWrapper.GetChild (i).gameObject);
            GenerateWaveform ();
        }
    }

    public void Update () {
        if (DGA.control.gameStarted) {
            MoveWaveForm ();
            DetectActiveNote ();
            if (closestNoteIndex != 0 && closestNoteIndex != musicPatterns [0].musicBlocks.Length - 1)
                CalculatePlayerScore ();
        }
    }

    public WaveGenerator waveGenerator; // For speed and convenience.
    [HideInInspector]
    public float playerScore; // Goal of the game for competition.
    private float singleScoreStep = 10; // This is the score which is goint to be multiplied by the precision.
    public float lifeScore = 1000f; // It grows, if player is good, falls, if bad. If it reaches zero, then game over.
    private float singleLifeScoreStep = 1f; // This is the score which is goint to be multiplied by the precision.

    public UnityEngine.UI.Text scoreText; // For speed and convenience.
    public UnityEngine.UI.Text lifeScoreText; // For speed and convenience.

    private void CalculatePlayerScore () {
        playerScore += singleScoreStep * (musicPatterns [0].musicBlocks [closestNoteIndex].waveBlockType == waveGenerator.previousWaveBlockType ? 2f : 0f);
        playerScore = Mathf.Clamp (playerScore, 0f, 2000000f);
        lifeScore += singleLifeScoreStep * (musicPatterns [0].musicBlocks [closestNoteIndex].waveBlockType == waveGenerator.previousWaveBlockType ? 1.1f : -.8f);
        lifeScore = Mathf.Clamp (lifeScore, 0f, 2000000f);
        scoreText.text = " " + Mathf.FloorToInt (playerScore).ToString ();
        lifeScoreText.text = " " + Mathf.FloorToInt (lifeScore).ToString ();
        if (lifeScore < StaticConstants.approximationFloat)
            gameOverControl.StartFade ();
    }

    public Transform startLine; // For speed and convenience.
    private int closestNoteIndex = 0; // To find the note, which is the closest to the middle.

    private void DetectActiveNote () {
        closestNoteIndex = 0;
        for (int i = 1; i < musicPatterns [0].musicBlocks.Length; ++i) {
            if ((musicPatterns [0].musicBlocks [i].musicBlockTransform.position - startLine.position).sqrMagnitude < (musicPatterns [0].musicBlocks [i - 1].musicBlockTransform.position - startLine.position).sqrMagnitude)
                closestNoteIndex = i;
        }
    }

    public int patternSize = 100; // How many sockets to create.

    private void GenerateWaveform () {
        musicPatterns [0].musicBlocks = new MusicBlock [patternSize];
        for (int i = patternSize - 1; i >= 0; --i) {
            musicPatterns [0].musicBlocks [i] = new MusicBlock ();
            MusicBlock currentMusicBlock = musicPatterns [0].musicBlocks [i]; // For speed and convenience.
            currentMusicBlock.relativeStartTime = i;
            currentMusicBlock.relativeEndTime = i + 1;
            int waveBlockTypeIndex = Mathf.FloorToInt (Mathf.Clamp (Random.Range (0f, 3f), 0f, 2f));
            switch (waveBlockTypeIndex) {
                case 0:
                    currentMusicBlock.waveBlockType = StaticConstants.WaveBlockType.top;
                    break;
                case 1:
                    currentMusicBlock.waveBlockType = StaticConstants.WaveBlockType.bottom;
                    break;
                case 2:
                    currentMusicBlock.waveBlockType = StaticConstants.WaveBlockType.simultaneous;
                    break;
                default:
                    break;
            }
        }
        Initialize ();
    }

    public void Start () {
        playerScore = 0;
        lifeScoreText.text = " " + Mathf.FloorToInt (lifeScore).ToString ();
        GenerateWaveform ();
    }
}
