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

public class LevelReader : MonoBehaviour {

    // Singleton
    public static LevelReader instance;

    // Result of the download
    private string currentLevelXML = "";

    // Current level name for playerprefs
    [HideInInspector]
    public string currentLevelName = "";

    // Prefabs to instanciate in the level
    private GameObject[] prefabs;

    // Association between a name and a prefab
    private Dictionary<string, int> idAssociation;

    // Directory in which prefabs are stocked
    [SerializeField]
    private string prefabDirectory = "Prefabs";

    // 3d matrix of authorized zones for the player to walk on
    private List<List<List<bool>>> authorizedZones;
    // 3d matrix of the gameObjects instanciated
    private List<List<List<GameObject>>> blocks;

    // GameObject parent of the level
    [SerializeField]
    private GameObject playingScene;

    // Spawn of the hero
    private Vector3 heroSpawn;

    // Temporary variables
    private int x, y, z;

    Quaternion correctCameraRotation;


    void Awake () {

        if (instance == null)
            instance = this;

        // Load all prefabs
        prefabs = Resources.LoadAll<GameObject>(prefabDirectory);
        
        // Associate the prefab name to the gameobject

        idAssociation = new Dictionary<string, int>();

        int i = 0;

        foreach(GameObject g in prefabs)
        {
            idAssociation.Add(g.name.ToLower(), i++);
        }	
	}

    public void Start()
    {
        authorizedZones = new List<List<List<bool>>>();
        blocks = new List<List<List<GameObject>>>();

        correctCameraRotation = GameObject.FindGameObjectWithTag("MainCamera").transform.rotation;
    }

    public void SelectLevel(string path)
    {
        StartCoroutine(DownloadLevel(path));
    }

   
    // Download the given level then load it
    IEnumerator DownloadLevel(string path)
    {
        Debug.Log("Try to read : " + path);

        WWW www = new WWW(path.Trim());

        yield return www;

        Debug.Log("lvl : " + www.text);

        currentLevelXML = www.text;

        LoadLevel();

        StateMachine.instance.UpdateState(StateMachine.STATE.PLAY);
        
    }


    // Read the level file and instanciate prefabs
    private void LoadLevel()
    {
        XmlDocument document = new XmlDocument();

        //Debug.Log(currentLevelXML);

        document.LoadXml(currentLevelXML);

        x = 0;
        y = 0;
        z = 0;

        heroSpawn = new Vector3();

        //Debug.Log("Reading level");

        authorizedZones.Clear();
        blocks.Clear();

        foreach (XmlNode layer in document.DocumentElement.ChildNodes)
        {
            if (layer.Name.ToLower() == "layer")
            {
                z = 0;
                List<List<bool>> l2 = new List<List<bool>>();
                List<List<GameObject>> b2 = new List<List<GameObject>>();
                foreach (XmlNode slice in layer)
                {
                    if (slice.Name.ToLower() == "slice")
                    {
                        x = 0;
                        List<bool> l1 = new List<bool>();
                        List<GameObject> b1 = new List<GameObject>();
                        foreach (XmlNode block in slice)
                        {
                            if (block.Name.ToLower() == "block")
                            {
                                string blockName = ((XmlAttribute)block.Attributes.GetNamedItem("prefab") != null) ? ((XmlAttribute)block.Attributes.GetNamedItem("prefab")).Value.ToLower() : "empty";
                                if (!idAssociation.ContainsKey(blockName)) blockName = "empty";
                                float blockRotation = ((XmlAttribute)block.Attributes.GetNamedItem("rotation") != null) ? float.Parse(((XmlAttribute)block.Attributes.GetNamedItem("rotation")).Value) : 0;
                                if((XmlAttribute)block.Attributes.GetNamedItem("rotation") == null)
                                {
                                    float[] values = { 0, 90, 180, -90 };
                                    blockRotation = values[UnityEngine.Random.Range(0, 4)];
                                }
                                //Debug.Log(blockName);
                                //Debug.Log(blockRotation);
                                b1.Add(PlaceBlock(blockName.ToLower(), blockRotation));

                                if (((XmlAttribute)block.Attributes.GetNamedItem("spawn") != null) && ((XmlAttribute)block.Attributes.GetNamedItem("spawn")).Value == "true")
                                {
                                    heroSpawn.Set(x, y, z);
                                }

                                bool canWalkOn = true;
                                if ((((XmlAttribute)block.Attributes.GetNamedItem("canWalkOn") != null) && ((XmlAttribute)block.Attributes.GetNamedItem("canWalkOn")).Value == "false"))
                                {
                                    canWalkOn = false;
                                }

                                l1.Add(canWalkOn);

                                //build block beside it
                                x++;
                            }
                        }
                        // Build slice beside it
                        z++;
                        l2.Add(l1);
                        b2.Add(b1);
                    }
                }

                // Build layer over it
                y++;
                authorizedZones.Add(l2);
                blocks.Add(b2);
            }
        }

        // the hero spawn there

        heroSpawn.y = heroSpawn.y + 1.0f;

        spawnHero(null);


        GameData.instance.currentScore = 0;

        StateMachine.instance.UpdateState(StateMachine.STATE.PLAY);
    }

    public GameObject spawnHero(GameObject hero)
    {
        FadeInAll();

        Destroy(hero);

        hero = Instantiate(prefabs[idAssociation["hero"]], heroSpawn, Quaternion.identity) as GameObject;
        
        //Debug.Log(correctCameraRotation);
        //Debug.Log(GameObject.FindGameObjectWithTag("MainCamera").transform.rotation);

        GameObject.FindGameObjectWithTag("MainCamera").transform.rotation = correctCameraRotation;
        //GameObject.FindGameObjectWithTag("MainCamera").transform.localRotation = correctCameraTransform.localRotation;
        GameObject.FindGameObjectWithTag("MainCamera").GetComponent<FollowTarget>().target = hero.GetComponentsInChildren<Transform>()[1].gameObject;
        hero.transform.SetParent(playingScene.transform);

        hero.GetComponent<CharacterControl>().spawnConfig();

        FadeOut(CharacterControl.DIRECTION.NORTH, heroSpawn);

        return hero;
    }


    // Intanciate a prefab at the given position with the given rotation
    private GameObject PlaceBlock(string blockName, float blockRotation)
    {
        GameObject block = (GameObject) Instantiate(prefabs[idAssociation[blockName]], new Vector3(x,y,z), Quaternion.identity);
        block.transform.Rotate(Vector3.up * blockRotation);
        block.transform.SetParent(playingScene.transform);

        return block;
    }

    // check if the given position is a legal block to walk on 
    public bool CanWalkOn(int block, int layer, int slice)
    {
        bool result = false;

        if (block < 0 || slice < 0 || layer < 0)
            return false;

        if (layer < authorizedZones.Count)
        {
            if (slice < authorizedZones[layer].Count)
            {
                if (block < authorizedZones[layer][slice].Count)
                {
                    result = authorizedZones[layer][slice][block];
                }
            }
        }


        return result;

    }


    // Get the gameobject of the block at given position
    public GameObject GetBlockAt(int block, int layer, int slice)
    {
        GameObject result = null;

        if (block < 0 || slice < 0 || layer < 0)
            return result;

        if (layer < blocks.Count)
        {
            if (slice < blocks[layer].Count)
            {
                if (block < blocks[layer][slice].Count)
                {
                    result = blocks[layer][slice][block];
                }
            }
        }


        return result;

    }

    // Chec kif the given position is an empty space
    public bool isEmpty(int block, int layer, int slice)
    {
        GameObject g = GetBlockAt(block, layer, slice);

        //if (g != null)
        //    Debug.Log(g.name.ToLower());

        return (g == null || g.name.Substring(0,5).ToLower() == "empty");
    }

    public void FadeOut(CharacterControl.DIRECTION dir, Vector3 pos)
    {
        for (int layer = 0; layer < blocks.Count; layer++)
        {
            for (int slice = 0; slice < blocks[layer].Count; slice++)
            {
                for (int block = 0; block < blocks[layer][slice].Count; block++)
                {
                    bool needFade = false;
                    switch(dir)
                    {
                        case CharacterControl.DIRECTION.NORTH:
                            if (slice < pos.z)
                                needFade = true;
                            break;
                        case CharacterControl.DIRECTION.EAST:
                            if (block < pos.x)
                                needFade = true;
                            break;
                        case CharacterControl.DIRECTION.SOUTH:
                            if (slice > pos.z)
                                needFade = true;
                            break;
                        case CharacterControl.DIRECTION.WEST:
                            if (block > pos.x)
                                needFade = true;
                            break;
                    }

                    GameObject g = blocks[layer][slice][block];

                    if (g != null)
                    {
                        if (needFade)
                        {
                            g.SetActive(false);
                            //mr.material.color = new Color(mr.material.color.r, mr.material.color.g, mr.material.color.b, 50);
                        }
                        else
                        {
                            if (g.GetComponent<swing>() != null)
                            {
                                //g.GetComponent<swing>().time = 0;
                                //g.GetComponent<swing>().ResetRotation();
                            }
                            g.SetActive(true);
                            //mr.material.color = new Color(mr.material.color.r, mr.material.color.g, mr.material.color.b, 255);
                        }
                    }

                }
            }
        }
    }

    private void FadeInAll()
    {
        for(int layer=0; layer<blocks.Count; layer++)
        {
            for (int slice = 0; slice < blocks[layer].Count; slice++)
            {
                for (int block = 0; block < blocks[layer][slice].Count; block++)
                {
                    if (blocks[layer][slice][block] != null)
                        blocks[layer][slice][block].SetActive(true);
                }
            }
        }
    }
}
