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

public class HomeBuilder : MonoBehaviour {

    public Transform root;
    public GameObject[] modules;
    public ModuleModel[] mandatoryModules;
    public GameObject wall, wallWithDoor;

    [SerializeField]
    public int m, n;

    [SerializeField]
    public float sideLength;
    private HomeModel homeModel;

	void Start ()
    {
        ModuleModel[] moduleModels = new ModuleModel[modules.Length];
        Dictionary<ModuleModel, GameObject> modulesMap = new Dictionary<ModuleModel, GameObject>();
        for (int k = 0; k < modules.Length; k++)
        {
            moduleModels[k] = modules[k].GetComponent<ModuleModel>();
            modulesMap.Add(moduleModels[k], modules[k]);
        }
        homeModel = new HomeModel(moduleModels, 4, 3, mandatoryModules);


        int i, j;
        HashSet<ModuleModel> alreadyInstantiated = new HashSet<ModuleModel>();
        for (i=0; i<m; i++)
        {
            for (j=0; j < n; j++)
            {
                GameObject goToInstantiate;
                ModuleModel modelToInstantiate = homeModel.homeMatrix[i, j];
                if (!modulesMap.TryGetValue(modelToInstantiate, out goToInstantiate))
                {
                    throw new Exception("Can't find the GO to instantiate");
                }
                if(!alreadyInstantiated.Contains(modelToInstantiate))
                {
                    Instantiate(goToInstantiate, new Vector3(sideLength * i, 0f, sideLength * j), Quaternion.identity, this.root.transform);
                    alreadyInstantiated.Add(modelToInstantiate);
                }
            }
        }

        for (int x = 0; x < m; x++)
        {
            Edge e = homeModel.GetEdge(i, 0, Cardinals.S); // check south for first row
            this.InstantiateEdge(e, x, 0, Cardinals.S, this.root.transform);
        }
        for (int y = 0; y < n; y++)
        {
            Edge e = homeModel.GetEdge(0, y, Cardinals.W); // check West
            this.InstantiateEdge(e, 0, y, Cardinals.W, this.root.transform);
            for (int x = 0; x < m; x++)
            {
                e = homeModel.GetEdge(x, y, Cardinals.E); // check East
                this.InstantiateEdge(e, x, y, Cardinals.E, this.root.transform);
                e = homeModel.GetEdge(x, y, Cardinals.N); // check North
                this.InstantiateEdge(e, x, y, Cardinals.N, this.root.transform);
            }
        }
        PubSubService<HomeBuilder>.GetInstance().Publish(this);
    }

    internal Vector3 getModuleCoordinates(string moduleName)
    {
        moduleName += "(Clone)";
        foreach(Transform module in this.root)
        {
            if (module.name == moduleName) return module.transform.position;
        }
        throw new Exception("Can't find required module");
    }

    private void InstantiateEdge(Edge e, float x, float y, Cardinals c, Transform parent)
    {
        GameObject prefab;
        switch(e)
        {
            case Edge.Wall:
                prefab = wall;
                break;
            case Edge.Door:
                prefab = wallWithDoor;
                break;
            case Edge.None:
                return;
            default: throw new Exception("No suitable prefab for Edge " + e);
        }

        Vector3 edgeOffset;
        Quaternion edgeRotation;
        switch (c)
        {
            case Cardinals.N:
                edgeOffset = new Vector3(0f, 0f, sideLength / 2f);
                edgeRotation = Quaternion.Euler(0, 180, 0);
                break;
            case Cardinals.S:
                edgeOffset = new Vector3(0f, 0f, -sideLength / 2f);
                edgeRotation = Quaternion.Euler(0, 180, 0);
                break;
            case Cardinals.E:
                edgeOffset = new Vector3(sideLength / 2f, 0f, 0f);
                edgeRotation = Quaternion.Euler(0,-90,0);
                break;
            case Cardinals.W:
                edgeOffset = new Vector3(-sideLength / 2f, 0f, 0f);
                edgeRotation = Quaternion.Euler(0, -90, 0);
                break;
            default: throw new Exception("No suitable positions for Cardinal " + c);
        }
        Instantiate(prefab, new Vector3(x*sideLength, 0f, y*sideLength) + edgeOffset, edgeRotation, parent);
    }
	
}
