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

public class ObstacleCircle : MonoBehaviour
{
    [SerializeField] private GameObject obstacle = null;
    [SerializeField] private int numberOfUnits = 0;
    [SerializeField] private float[] rotationOffsets = null;
    [SerializeField] private float[] rotationTimes = null;
    // The values are read from 0 to 1
    [SerializeField] private AnimationCurve[] rotationSpeedCurve = null;
    // 'o' means obstacle, followed by the number of adjacent of them.
    // 'e' means empty or no obstacle, followed by the number of empty units
    // the sum of all numbers in an string should be equal to the numberOfUnits field;
    [SerializeField] private string[] levelEditor = null;

    private float initialRotationY;
    private ArrayList obstacles = new ArrayList();
    private int nextLevelIndex = 0;
    private int rotationSpeedMultiplier = 40;
    private float time = 0f;
    private float defaultRotationTime = 2.0f;
    private float defaultRotationSpeed = 1f;

    private bool keepRotating = true;

    private void Awake()
    {
        initialRotationY = transform.localEulerAngles.y;

        // If rotationOffsets is not all initialized
        if (rotationOffsets.Length < levelEditor.Length)
        {
            if (rotationOffsets.Length == 0)
            {
                rotationOffsets = new float[levelEditor.Length];
                for (int i = 0; i < levelEditor.Length; i++)
                    rotationOffsets[i] = 0f;
            }
            else
            {
                float[] tmp = rotationOffsets;
                rotationOffsets = new float[levelEditor.Length];
                tmp.CopyTo(rotationOffsets, 0);
                for (int i = tmp.Length; i < levelEditor.Length; i++)
                    rotationOffsets[i] = 0f;
            }
        }

        // If rotationTimes is not all initialized
        if (rotationTimes.Length < levelEditor.Length)
        {
            if (rotationTimes.Length == 0)
            {
                rotationTimes = new float[levelEditor.Length];
                for (int i = 0; i < levelEditor.Length; i++)
                    rotationTimes[i] = defaultRotationTime;
            }
            else
            {
                float[] tmp = rotationTimes;
                rotationTimes = new float[levelEditor.Length];
                tmp.CopyTo(rotationTimes, 0);
                for (int i = tmp.Length; i < levelEditor.Length; i++)
                    rotationTimes[i] = defaultRotationTime;
            }
        }

        // If rotationSpeedCurve is not all initialized
        if (rotationSpeedCurve.Length < levelEditor.Length)
        {
            if (rotationSpeedCurve.Length == 0)
            {
                rotationSpeedCurve = new AnimationCurve[levelEditor.Length];
                for (int i = 0; i < levelEditor.Length; i++)
                {
                    rotationSpeedCurve[i] = new AnimationCurve();
                    rotationSpeedCurve[i].AddKey(new Keyframe(0f, defaultRotationSpeed));
                    rotationSpeedCurve[i].AddKey(new Keyframe(1f, defaultRotationSpeed));
                }
            }
            else
            {
                AnimationCurve[] tmp = rotationSpeedCurve;
                rotationSpeedCurve = new AnimationCurve[levelEditor.Length];
                tmp.CopyTo(rotationSpeedCurve, 0);
                for (int i = tmp.Length; i < levelEditor.Length; i++)
                {
                    rotationSpeedCurve[i] = new AnimationCurve();
                    rotationSpeedCurve[i].AddKey(new Keyframe(0f, defaultRotationSpeed));
                    rotationSpeedCurve[i].AddKey(new Keyframe(1f, defaultRotationSpeed));
                }
            }
        }
    }

    private void Start()
    {
        transform.localEulerAngles += new Vector3(0f, rotationOffsets[0], 0f);
        if (levelEditor.Length != 0)
            MakeNextLevel();
        GameEvents.instance.onNextLevelEvent += NextLevel;
        GameEvents.instance.onGameRestartEvent += Restart;
        GameEvents.instance.onGameLostEvent += StopRotating;
        GameEvents.instance.onGameRestartEvent += ContinueRotating;
        GameEvents.instance.onGameRestartEvent += ResetTime;
    }

    private void FixedUpdate()
    {
        // Rotate the obstacles based on the animation curve in rotationTime of that level
        if (keepRotating)
        {
            time += Time.deltaTime;
            if (time > rotationTimes[CurrentLevelIndex()])
                time = 0f;
            transform.localEulerAngles += new Vector3(0f, rotationSpeedCurve[CurrentLevelIndex()].Evaluate(time / rotationTimes[CurrentLevelIndex()]) * rotationSpeedMultiplier * Time.deltaTime, 0f);
        }
        else
        {
            transform.localEulerAngles += new Vector3(0f,  rotationSpeedMultiplier / 2 * Time.deltaTime, 0f);
        }
    }

    private void MakeNextLevel()
    {
        string level = levelEditor[nextLevelIndex];
        char[] legalChars = { 'o', 'e' };
        string leftover = level;
        int currentUnitIndex = 0;
        int endIndex;
        while(leftover.IndexOfAny(legalChars, 1) != -1)
        {
            endIndex = leftover.IndexOfAny(legalChars, 1);
            string set = leftover.Substring(0, endIndex);
            int count = int.Parse(leftover.Substring(1, endIndex - 1));
            if (set[0] == 'o')
            {
                for (int i = 0; i < count; i++)
                {
                    GameObject childObject = Instantiate(obstacle);
                    obstacles.Add(childObject);
                    childObject.transform.parent = gameObject.transform;
                    childObject.transform.localPosition = new Vector3(0, 0, 0);
                    childObject.transform.localEulerAngles = new Vector3(0f, currentUnitIndex * 360f / numberOfUnits, 0f);
                    currentUnitIndex++;
                }
            }
            else if (set[0] == 'e')
            {
                currentUnitIndex += count;
            }
            leftover = leftover.Substring(endIndex);
        }
        if (leftover[0] == 'o')
        {
            for (int i = 0; i < int.Parse(leftover.Substring(1)); i++)
            {
                GameObject childObject = Instantiate(obstacle);
                obstacles.Add(childObject);
                childObject.transform.parent = gameObject.transform;
                childObject.transform.localPosition = new Vector3(0, 0, 0);
                childObject.transform.localEulerAngles = new Vector3(0f, currentUnitIndex * 360f / numberOfUnits, 0f);
                currentUnitIndex++;
            }
        }
        nextLevelIndex = (nextLevelIndex + 1) % levelEditor.Length;
    }

    private void Restart()
    {
        nextLevelIndex = 0;
        NextLevel();
    }

    private void NextLevel()
    {
        foreach (GameObject element in obstacles)
        {
            //obstacles.Remove(element);
            Destroy(element);
        }
        obstacles = new ArrayList();
        transform.localEulerAngles = new Vector3(0f, initialRotationY + rotationOffsets[nextLevelIndex], 0f);
        if (levelEditor.Length != 0)
            MakeNextLevel();
    }

    private int CurrentLevelIndex()
    {
        return (nextLevelIndex - 1 < 0) ? (levelEditor.Length - 1) : (nextLevelIndex - 1);
    }

    private void ResetTime()
    {
        time = 0f;
    }

    private void StopRotating()
    {
        keepRotating = false;
    }

    private void ContinueRotating()
    {
        keepRotating = true;
    }
}
