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

public class GameState : MonoBehaviour
{
    public static GameState instance;

    public int currentLevel = 0;

    public float roomCreationCounter;
    public int roomsCreated;

    public List<MovementController> players;

    public bool isEndGame = false;

    public List<FadedRoom> fadedRooms;

    private bool closeToLoss;
    public bool CloseToLoss
    {
        get
        {
            return closeToLoss;
        }
        set
        {
            bool changed = false;

            changed = (closeToLoss != value);

            closeToLoss = value;

            if (changed)
            {
                if (closeToLoss)
                {
                    //Play!
                    SoundManager.instance.Creak();
                }
                else
                {
                    //Stop!
                    SoundManager.instance.StopCreak();
                }
            }
        }
    }

    /// <summary>
    /// This stores the moment calculation of each level as it was the moment the level was turned off
    /// </summary>
    public List<float> finalLevelMoment;

    public float offsetX;

    private void Awake()
    {
        instance = this;
    }

    private void Start()
    {
        Time.timeScale = 1;
    }

    Vector3 desiredPosition;

    bool firstRun = true;

    private void Update()
    {
        if (isEndGame)
        {
            return;
        }

        if (firstRun)
        {
            NextLevel();
            desiredPosition = Camera.main.transform.position;
            firstRun = false;
        }

        if (Input.GetKeyUp(KeyCode.L))
        {
            NextLevel();
        }

        roomCreationCounter -= Time.deltaTime;

        if (!fadedRooms.Any() && roomCreationCounter < 5)
        {
            //Generate Fadies
            int maxRooms = Mathf.Min(5, (1 + ((currentLevel - 1) * 2)));
            if ((GameState.instance.roomsCreated == maxRooms) && (currentLevel+1 == GameSettings.settings.winLevel))
            {
               //Game about to end!
            }
            else GenerateFaded();
        }


        if (roomCreationCounter < 0)
        {
            //It's room time!
            GenerateRooms();
        }

        var roomWidth = roomsCreated * GameSettings.settings.roomWidth * 1.25f;

        float newZ = Mathf.Min(Camera.main.transform.position.z, -(roomWidth / Camera.main.aspect) * 0.5f / Mathf.Tan(Camera.main.fieldOfView * 0.5f * Mathf.Deg2Rad));

        desiredPosition = new Vector3(desiredPosition.x, desiredPosition.y, newZ );

        Camera.main.transform.position = Vector3.Lerp(Camera.main.transform.position, desiredPosition, 2 * Time.deltaTime);
    }

    private void CalculateOffsetX(){
        offsetX = (GameSettings.settings.roomWidth * GameSettings.settings.roomPlacementOffset);
        if (currentLevel == 1)
        {
            offsetX = 0;
        }
        else if (currentLevel % 2 == 0)
        { //alternate offset between levels
            offsetX = -offsetX;
        }
    }

    public void NextLevel()
    {
        if (currentLevel + 1 >= GameSettings.settings.winLevel)
        {
            //Yaay and all that
            isEndGame = true;
            UIManager.instance.Win();
            return;
        }

        //Do one final moment calculation
        if (currentLevel > 0)
        {
            //Make it harder
            GameSettings.settings.disbalanceSpeed += GameSettings.settings.difficultyRamp;

            float moment = House.instance.furnishings.Where(f => !f.GetComponent<MovementController>()).Sum(f => f.Moment);
            finalLevelMoment.Add(moment);

            //And turn off all the furniture - by removing the scripts
            //House.instance.furnishings.Where(f => !f.GetComponent<MovementController>()).ToList().ForEach(f => Destroy(f));
            House.instance.furnishings.
                 Where(f => !f.GetComponent<MovementController>() && !f.IsBeingCarried).ToList().ForEach(f => f.enabled = false);

            House.instance.furnishings.RemoveAll(f => !f.GetComponent<MovementController>() && !f.IsBeingCarried);

            desiredPosition += new Vector3(0, GameSettings.settings.roomHeight, 0);

            //Attempt to transform the player
            //players.Where(player => player.GetComponent<CharacterAgeBehaviour>().AttemptAgeCharacter()).ToList().ForEach(player => player = player.GetComponent<CharacterAgeBehaviour>().newMovementController);
            //lambda this if you can
            List<int> toDelete = new List<int> ();
            for (int i = players.Count-1; i >=0; i--){
                MovementController player = players[i];
                CharacterAgeBehaviour playerAger = player.GetComponent<CharacterAgeBehaviour>();
                if (playerAger.AttemptAgeCharacter()){
                    MovementController newPlayer = playerAger.newMovementController;
                    players[i].gameObject.SetActive(false);
                    players[i] = newPlayer;
                }
            }

            //Teleport the player
            players.Where (p => p.GetComponent<MovementController>().IsHoldingItem()).ToList().ForEach(p => p.GetComponent<MovementController>().enabled = true);
            players.ForEach(player => player.transform.localPosition = new Vector3(0, player.transform.localPosition.y + GameSettings.settings.roomHeight - GameSettings.settings.roomHeightBuffer, player.transform.localPosition.z));
            players.ForEach(player => player.transform.localRotation = Quaternion.identity);
            players.ForEach(player =>
            {
                if (player.IsHoldingItem())
                {
                    player.heldItem.localRotation = Quaternion.identity;
                }
            });
        }
        
        currentLevel++;
        roomsCreated = 0;

        GenerateRooms();
    }

    private void GenerateFaded()
    {
        int maxRooms = Mathf.Min(5, (1 + ((currentLevel - 1) * 2)));

        //calculate how far left/right to push rooms IN THE FUTURE.
        float fadedOffsetX = (GameSettings.settings.roomWidth * GameSettings.settings.roomPlacementOffset);
        float nextLevel = currentLevel + 1; //should NEVER be 1
        if (nextLevel % 2 == 0)
        { //alternate offset between levels
            fadedOffsetX = -fadedOffsetX;
        }

        //Check whether we should create the room
        if (roomsCreated >= maxRooms)
        {

            //Generate for next level
            GenerateFaded(new Vector3(fadedOffsetX, (currentLevel + 1) * GameSettings.settings.roomHeight, 0));
        }
        else
        {
            float roomX = (GameSettings.settings.roomWidth / 2); //This is the first room

            roomX += (roomsCreated - 1) * GameSettings.settings.roomWidth / 2; //And other rooms
            roomX += GameSettings.settings.roomWidth / 2; //And us

            //Generate two rooms
            GenerateFaded(new Vector3(-roomX + offsetX, currentLevel * GameSettings.settings.roomHeight, 0));
            GenerateFaded(new Vector3(roomX + offsetX, currentLevel * GameSettings.settings.roomHeight, 0));
        }
    }

    /// <summary>
    /// Generates an appropriate number of rooms given the game state
    /// /// </summary>
    private void GenerateRooms()
    {
        int maxRooms = Mathf.Min(5, (1 + ((currentLevel - 1) * 2)));

        //Remove faded
        fadedRooms.ForEach(f => Destroy(f.gameObject));
        fadedRooms.Clear();

        CalculateOffsetX();

        //Check whether we should create the room
        if (roomsCreated >= maxRooms)
        {
            //Put the stairs instead
            NextLevel();
            roomCreationCounter = GameSettings.settings.roomCreationPeriod * GameSettings.settings.singleRoomTimeMultiplier;
        }
        else if (roomsCreated == 0)
        {
            //Put the first room in
            GenerateRoom(new Vector3(offsetX, currentLevel * GameSettings.settings.roomHeight, 0), GameSettings.settings.GetRandomBlueprint(currentLevel));
            roomCreationCounter = GameSettings.settings.roomCreationPeriod * GameSettings.settings.singleRoomTimeMultiplier;
        }
        else
        {
            float roomX = (GameSettings.settings.roomWidth * 0.5f); //This is the first room

            roomX += (roomsCreated - 1) * GameSettings.settings.roomWidth * 0.5f; //And other rooms
            roomX += GameSettings.settings.roomWidth * 0.5f; //And us

            //Generate two rooms
            GenerateRoom(new Vector3(-roomX + offsetX, currentLevel * GameSettings.settings.roomHeight, 0), GameSettings.settings.GetRandomBlueprint(currentLevel));
            GenerateRoom(new Vector3(roomX + offsetX, currentLevel * GameSettings.settings.roomHeight, 0), GameSettings.settings.GetRandomBlueprint(currentLevel));

            roomCreationCounter = GameSettings.settings.roomCreationPeriod;
        }
    }

    private void GenerateFaded(Vector3 localPosition)
    {
        var fade = GameObject.Instantiate(GameSettings.settings.fadedRoomPrefab, House.instance.transform, false);
        fade.transform.localPosition = localPosition;

        fade.GetComponent<FadedRoom>().StartTimer(roomCreationCounter);

        fadedRooms.Add(fade);
    }

    public static GameObject FindGameObjectInChildWithTag(GameObject parent, string tag)
    {
        Transform t = parent.transform;

        for (int i = 0; i < t.childCount; i++)
        {
            if (t.GetChild(i).gameObject.tag == tag)
            {
                return t.GetChild(i).gameObject;
            }

        }

        return null;
    }

    private void GenerateRoom(Vector3 localPosition, RoomBlueprint blueprint)
    {
        var room = GameObject.Instantiate(GameSettings.settings.emptyRoomPrefab, House.instance.transform, false);
        room.transform.localPosition = localPosition;

        //And background support
        if (blueprint.background)
        {
            GameObject back = FindGameObjectInChildWithTag(room, GameSettings.settings.backTag);
            back.GetComponent<Renderer>().material = blueprint.background;
        }

        //Now generate items
        int itemsToCreate = Random.Range(blueprint.minFurniture, blueprint.maxFurniture + 1);

        for(int i=0;i < itemsToCreate; i++)
        {
            var chosenFurniture = blueprint.potentialFurniture[Random.Range(0, blueprint.potentialFurniture.Count - 1)];

            if (chosenFurniture.immovable)
            {
                //Roll the dice
                if (Random.Range(0, 100) > GameSettings.settings.percentageCheckForSpecialFurniture)
                {
                    //Reroll the item
                    i--;
                    continue;
                }
            }

            var furniture = GameObject.Instantiate(
                blueprint.potentialFurniture[Random.Range(0, blueprint.potentialFurniture.Count)], room.transform, false);

            Vector3 position = new Vector3(Random.Range(-GameSettings.settings.roomFurnitureWidth / 2, GameSettings.settings.roomFurnitureWidth / 2), -2, 0);


            furniture.transform.localPosition = position;
            furniture.transform.SetParent(House.instance.transform, true);
            Vector3 furniPos = furniture.transform.localPosition;

        }

        roomsCreated++;
    }

}
