using System.Linq;
using Assets.Scripts;
using Assets.Scripts.Battle;
using Assets.Scripts.Core;
using Assets.Scripts.Events;
using Assets.Scripts.Player;
using Assets.Scripts.UI;
using UnityEngine;
using System.Collections;

public class GameCore : MonoBehaviour, ICore {

    [SerializeField] private GameObject _travelScreen;
    [SerializeField] private GameObject _battleScreen;
    [SerializeField] private GameObject _eventScreen;

    private TravelController _travelController;

    public static GameCore Instance { get; private set; }
    public GameEvent CurrentEvent { get { return _curEvent; } }

    private RoadMap _roadMap;
    private PlayerStats _playerStats;
    private BattleEngine _battle;
    private GameEvent _curEvent;

    private float _timeToEvent;
    private bool _hold;
    private float _holdTimer;

    [HideInInspector] public IEventUI EventUI;
    [HideInInspector] public IBattleUI BattleUI;
    private bool _firstEvent = true;
    private bool _gameOver;

    public delegate void OptionPicked(int n);

    protected void Awake(){
        Instance = this;

        _playerStats = new PlayerStats();
        _roadMap = new RoadMap();
        _timeToEvent = 0;
        _travelController = _travelScreen.GetComponent<TravelController>();
    }

    protected void Start(){
        _battleScreen.SetActive(false);
        MusicController.Instance.SetAct(Act.Act_1);
    }

    protected void Update(){
        if (IsWaitingForGameEvent()) {
            if (_holdTimer > 0) {
                _holdTimer = Mathf.Max(0, _holdTimer - Time.deltaTime);
            } else {
                _timeToEvent -= Time.deltaTime * (IsFastForward ? 10 : 1);
                if (_timeToEvent <= 0) {
                    AdvanceStory();
                }
            }
        }

        UpdateBattle();
    }

    private bool IsFastForward {
#if UNITY_EDITOR
        get { return Input.GetKey(KeyCode.Space); }
#else
        get { return false; }
#endif
    }

    private bool IsWaitingForGameEvent() {
        return !_hold && _curEvent == null && _battle == null;
    }

    public bool IsGameOver() {
        if (_playerStats == null) {
            Debug.LogError("No player stats");
        }
        if (_playerStats.LivingCharacters == null) {
            Debug.LogError("No living characters");
        }
        return _playerStats.LivingCharacters.Length == 0;
    }

    private void AdvanceStory() {
        if (IsGameOver()) {
            GameOver();
        } else {
            _timeToEvent = Random.Range(6.5f, 10.0f);
            _roadMap.Advance(this);
        }
    }

    private void SetScreen(GameObject screen) {
        EventUI.Clear();
        BattleUI.Clear();
        foreach (var s in new[] { _battleScreen, _travelScreen, _eventScreen }) {
            if (s != null) {
                s.SetActive(s == screen);
            }
        }
    }

    private void UpdateBattle () {
        if (_battle != null) {
            if (_battle.IsBattleOver()) {
                EndBattle();
            } else {
                _battle.Update(Time.deltaTime);
            }
        }
    }

    public void StartBattle (params Mob[] enemies) {
        HoldTimerFor(1);
        StopAllCoroutines();
        WaitForFade(() => {
            SetScreen(_battleScreen);
            _battle = new BattleEngine(_playerStats.PlayerCharacters, enemies);
            BattleUI.StartBattle(_battle,(int)_playerStats.CurrentAct);
            MusicController.Instance.StartBattle(enemies.Any(e => e.Name == "Dracula" || e.Name == "Necromancer"));
        });
    }

    private void EndBattle() {
        _battle = null;
        _hold = true;
        WaitForFade(() => {
            _hold = false;
            MusicController.Instance.EndBattle();
            if (IsGameOver()) {
                GameOver();
            } else {
                if (_curEvent != null) {
                    SetScreen(_eventScreen);
                    _curEvent.ResumeEvent();
                } else {
                    SetScreen(_travelScreen);
                    _travelController.Travel(0.5f);
                }
            }
        });
    }

    private void StartEvent (GameEvent e) {
        if (!e.CanTrigger(_playerStats)) {
            _timeToEvent = 0;
            return;
        }

        _curEvent = e;
        if (_curEvent != null) {
            _travelController.Stop(0);
            WaitForFade(() => {
                if (_firstEvent) {
                    FadeController.Instance.FadeIn(2.0f);
                }
                SetScreen(_eventScreen);
                EventUI.SetupAvatars(_playerStats.PlayerCharacters);
                _curEvent.StartEvent(this, EventUI);
            }, _firstEvent ? 0 : 1.0f, _firstEvent ? 0 : 0.5f);
        }
    }

    public void SuspendEvent () {
        EventUI.Clear();
    }
    
    public void EndEvent() {
        _curEvent = null;
        WaitForFade(() => {
            EventUI.Clear();
            SetScreen(_travelScreen);

            if (!IsGameOver()) {
                _travelController.Travel(1f);
            }

            if (_firstEvent) {
                FadeController.Instance.FadeIn(6.0f);
                _travelController.Travel(0f);
                _firstEvent = false;
            }
        });
    }

    private delegate void ActionToDo();

    private void WaitForFade (ActionToDo a, float fadeLen = 1.0f, float fadePad = 0.5f) {
        if (fadeLen > 0 || fadePad > 0) {
            StartCoroutine(DoWaitForFade(a, fadeLen, fadePad));
        } else {
            a();
        }
    }

    private IEnumerator DoWaitForFade(ActionToDo a, float fadeLen = 1.0f, float fadePad = 0.5f) {
        float mult = IsFastForward ? 0.1f : 1.0f;

        FadeController.Instance.FadeOut(fadeLen*mult);
        yield return new WaitForSeconds((fadeLen + fadePad)*mult);
        FadeController.Instance.FadeIn(fadeLen*mult);
        a();
    }

    public PlayerStats PlayerStats {
        get { return _playerStats; }
    }

    public void StartAct(Act act) {
        Debug.Log("Setting act: " + act);

        ActionToDo action = () => {
            MusicController.Instance.SetAct(act);
            _travelController.SetAct(act);
            string[] names = {"At the Footsteps of Midnight", "Beyond Desolation, Beyond Salvation", "The Ancient Crown of Blood", "Dawn"};
            EventUI.ShowActTitle(act.ToString().Replace('_', ' '), names[(int) act]);
            _playerStats.CurrentAct = act;
        };
        if (act == Act.Act_1) {
            HoldTimerFor(4.0f);
            action();
        } else {
            HoldTimerFor(6.0f);
            WaitForFade(action, 2.0f, 0.5f);
        }
    }

    private void HoldTimerFor(float t) {
        _holdTimer += t;
    }

    public void StartRandomEvent() {
        StartEvent(EventFactory.Instance.GetRandomEvent(_playerStats));
    }

    public void StartSpecificEvent(string name) {
        StartEvent(EventFactory.Instance.GetSpecificEvent(name));
    }

    public void StartOneOfEvents (string[] eventNames) {
        var events = eventNames.Select(e => EventFactory.Instance.GetSpecificEvent(e)).ToList();
        var pick = events.Where(e => e.CanTrigger(_playerStats)).ToList().RandomElement();
        StartEvent(pick);
    }

    public void OnTagSent(string tag) {
        switch (tag) {
            case "cityApproaching":
                HoldNextEvent();
                _travelController.SpawnInn(ForceNextEvent);
                return;
        }
    }

    public void HoldNextEvent() {
        _timeToEvent = 99999;
    }

    public void ForceNextEvent() {
        _timeToEvent = 0;
    }

    private void GameOver () {
        if (!_gameOver) {
            _gameOver = true;
            StartSpecificEvent("GameOver");
        }
    }

    public void GameComplete () {
        _hold = true;
    }

    public void GoToMainMenu () {
        WaitForFade(() => Application.LoadLevel(0));
    }

    public void GoToCredits () {
        WaitForFade(() => Application.LoadLevel(2));
    }
}
