using System;
using System.Collections;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using UnityEngine;
using RelicGGJ;

public class GridController : MonoBehaviour
{

    public TextAsset[] m_levelFile;
    public uint m_width, m_height;
    private GameObject[,,] m_level; // What the fuck even is this bullshit syntax. No, seriously, now there's two commas
    public bool ready = false;

    public float m_updateInterval = 0.5f;
    public float m_timeSinceUpdate = 0.0f;

    private bool m_sonarActive = false;

    public float m_sightFalloffScale = 5;

    SpriteRenderer[] m_sprites;

	
	// Update is called once per frame
	void Update ()
    {
		if (m_timeSinceUpdate < m_updateInterval)
        {
            m_timeSinceUpdate += Time.deltaTime;
        }
        else if (!ready)
        {
            ready = true;
        }
	}

    public GameObject GetObjectAt(float x, float y, gridLayer layer = gridLayer.COUNT)
    {
        if (x < 0 || y < 0 || x >= m_width || y >= m_height /*|| layer < gridLayer.BACKGROUND*/ || layer > gridLayer.COUNT)
        {
            return null;
        }
        /*
        for (int i = 0; i < (int)gridLayer.COUNT; ++i)
        {
            if (m_level[(int)y, (int)x, (int)i])
            {
                Debug.Log(m_level[(int)y, (int)x, (int)i].name);
            }
            else
            {
                Debug.Log("Null");
            }
        }
        */
        if (layer == gridLayer.COUNT)
        {
            for (int i = (int)gridLayer.COUNT - 1; i >= 0; --i)
            {
                //Debug.Log("Layer: " + (int)i);
                //Debug.Log(x.ToString() + " " + y.ToString() + " " + ((int)layer).ToString());
                if (m_level[(int)y, (int)x, (int)i] != null)
                {
                    //Debug.Log("Found: " + m_level[(int)y, (int)x, (int)i].name);
                    return m_level[(int)y, (int)x, (int)i];
                }
            }
            return null;
        }
        else
        {
            return m_level[(int)y, (int)x, (int)layer];
        }
    }

    public void SetObjectAt(float x, float y, GameObject obj, gridLayer layer)
    {
        if (x >= 0 && y >= 0 && x < m_width && y < m_height && layer >= gridLayer.BACKGROUND && layer < gridLayer.COUNT)
        {
            m_level[(int)y, (int)x, (int)layer] = obj;
        }
    }

    public bool CheckValidPosition(float x, float y)
    {
        if (x >= 0 && y >= 0 && x < m_width && y < m_height)
        {
            return true;
        }
        return false;
    }

    public void GameTick(playerAction action)
    { 
        m_timeSinceUpdate = 0.0f;
        ready = false;
        List<GameObject> updated = new List<GameObject>();
        PlayerController.Instance.GameTick();
        updated.Add(PlayerController.Instance.gameObject);
        for (int layer = (int)gridLayer.ITEMS; layer >= (int)gridLayer.BACKGROUND; --layer)
        {
            for (int y = 0; y < m_height; ++y)
            {
                for (int x = 0; x < m_width; ++x)
                {
                    GameObject g = m_level[y, x, layer];

                    if (g != null && !updated.Contains(g))
                    {
                        if (g.activeInHierarchy)
                        {
                            EnemyController controller = g.GetComponent<EnemyController>();
                            if (controller != null)
                            {
                                controller.GameTick();
                                updated.Add(g);
                            }
                        }
                    }

                }
            }
        }
        if (action == playerAction.SONAR)
        {
            m_sonarActive = true;
        }
        UpdateSight();
    }

    public void ReenterLevel(bool fromUp)
    {
        for (int y = 0; y < m_height; ++y)
        {
            for (int x = 0; x < m_width; ++x)
            {
                GameObject g = m_level[y, x, (int)gridLayer.EXITS];
                if (g != null)
                {
                    GridObject gObj = g.GetComponent<GridObject>();
                    if (gObj != null)
                    {
                        if (gObj.m_type == GridObject.objectType.EXIT_UP && fromUp)
                        {
                            PlayerController.Instance.m_position = new Vector2(x, y);
                            PlayerController.Instance.m_level = this;
                            PlayerController.Instance.UpdatePosition();
                        }
                        else if (gObj.m_type == GridObject.objectType.EXIT_DOWN && !fromUp)
                        {
                            PlayerController.Instance.m_position = new Vector2(x, y);
                            PlayerController.Instance.m_level = this;
                            PlayerController.Instance.UpdatePosition();
                        }
                    }
                }
            }
        }
        UpdateSight();
        m_timeSinceUpdate = 0f;
        ready = false;
    }

    public bool Load(enteredFrom source = enteredFrom.NONE)
    {
        bool sizeInitialized = false;
        for (gridLayer i = gridLayer.BACKGROUND; i < gridLayer.COUNT; ++i)
        {
            // Don't spawn a new player if one already exists.
            if (i == gridLayer.PLAYER && PlayerController.Instance != null)
            {
                continue;
            }
            
            string fileText = m_levelFile[(int)i].text;
            fileText.Replace("\r", null);
            string[] lines1 = fileText.Split('\n');
            List<string[]> splitLines = new List<string[]>();
            foreach (string s in lines1)
            {
                if (s != "")
                {
                    splitLines.Add(s.Split(','));
                    //Debug.Log(s);
                }
                
            }

            if (!sizeInitialized)
            {
                // Initialize the level object.
                m_width = (uint)splitLines[0].Length;
                m_height = (uint)splitLines.Count;
                m_level = new GameObject[m_height, m_width, (int)gridLayer.COUNT];
                //Debug.Log(m_height.ToString() + " " + m_width.ToString());
                sizeInitialized = true;
            }
            



            // Process each character in each row.
            for (int y = 0; y < m_height - 1; ++y)
            {
                for (int x = 0; x < m_width - 1; ++x)
                {
                    // Figure out which element the character corresponds to. Anything outside of 1-9 is treated as an 
                    // empty space.
                    GameObject obj;
                    int elementID = 0;
                    elementID = Int32.Parse(splitLines[y][x]);
                    if (elementID < 0)
                    {
                        continue;
                    }

                    // Instantiate the appropriate GameObject from the list.
                    Debug.Log("Layer: " + i);
                    Debug.Log("Element ID: " + elementID);
                    obj = Instantiate(GameManager.Instance.m_layerObjects[(int)i].arr[elementID]);
                    obj.transform.position =
                        new Vector3(x + 1f - (m_width / 2f), -1f - y + (m_height / 2f), (int)(gridLayer.COUNT - i));
                    obj.transform.parent = transform;
                    SetObjectAt(x, y, obj, i);

                    // If the associated object is a GridObject, initialize it.
                    GridObject gObject = obj.GetComponent<GridObject>();
                    if (gObject != null)
                    {
                        //Debug.Log("Source: " + source.ToString() + ", Type: " + gObject.m_type.ToString());
                        // Initialize the object.
                        gObject.Init(i);
                        gObject.m_position = new Vector2(x, y);
                        if (gObject.m_type == GridObject.objectType.EXIT_DOWN && GameManager.Instance.m_levelIndex == 
                            GameManager.Instance.m_levels.Length - 1)
                        {
                            Destroy(obj);
                            continue;
                        }
                        else if (gObject.m_type == GridObject.objectType.EXIT_UP && 
                            GameManager.Instance.m_levelIndex == 0)
                        {
                            Destroy(obj);
                            continue;
                        }
                        if (source == enteredFrom.DOWN && gObject.m_type == GridObject.objectType.EXIT_DOWN)
                        {
                            
                            SetObjectAt(PlayerController.Instance.m_position.x, PlayerController.Instance.m_position.y, 
                                null, gridLayer.PLAYER);
                            PlayerController.Instance.m_level = GameManager.Instance.m_currentLevel;
                            PlayerController.Instance.m_position = new Vector2(x, y);
                            SetObjectAt(x, y, PlayerController.Instance.gameObject, gridLayer.PLAYER);
                            PlayerController.Instance.UpdatePosition();
                        }
                        else if (source == enteredFrom.UP && gObject.m_type == GridObject.objectType.EXIT_UP)
                        { 
                            SetObjectAt(PlayerController.Instance.m_position.x, PlayerController.Instance.m_position.y,
                                null, gridLayer.PLAYER);
                            PlayerController.Instance.m_level = GameManager.Instance.m_currentLevel;
                            PlayerController.Instance.m_position = new Vector2(x, y);
                            SetObjectAt(x, y, PlayerController.Instance.gameObject, gridLayer.PLAYER);
                            PlayerController.Instance.UpdatePosition();
                        }
                    }
                }
            }
        }
        m_sprites = GetComponentsInChildren<SpriteRenderer>();
        UpdateSight();
        m_timeSinceUpdate = 0f;
        ready = false;
        return true;
    }

    void UpdateSight()
    {
        if (PlayerController.Instance != null)
        {
            foreach (SpriteRenderer sr in m_sprites)
            {
                if (sr != null)
                {
                    sr.color = Color.clear;

                    if (PlayerController.Instance.m_level != null)
                    {
                        float sightRadius = PlayerController.Instance.m_sightRadius;
                        if (m_sonarActive)
                        {
                            sightRadius = PlayerController.Instance.m_sonarRadius == 0 ?
                                Mathf.Infinity : PlayerController.Instance.m_sonarRadius;
                        }

                        Vector2 origin = sr.gameObject.transform.position;
                        Vector2 target = PlayerController.Instance.transform.position;

                        float dist = Utility.CalcDistance(target, origin);
                        RaycastHit2D rch2d = Physics2D.Raycast(origin, target - origin, sightRadius);
                        if (rch2d.transform == PlayerController.Instance.transform || dist < 1f)
                        {
                            if (dist <= sightRadius)
                            {
                                sr.color = new Color(1,1,1,1);
                            }
                            else
                            {
                                sr.color = new Color(1, 1, 1, (float)Math.Pow(m_sightFalloffScale / dist - 0.5f, 2));
                            }
                        }
                    }
                }
            }
            m_sonarActive = false;
        }
        
    }

    
}
