using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Events;
using System.Collections;
using System.Collections.Generic;
using UnityTwine;

public enum CycleState
{
    GAME_OVER = 0,
    CROSSROADS = 2,
    INTERVIEW = 3,
    WAIT = 4,
    BAR = 5,
    PAID_SEX = 6,
    LIBRARY = 7,
    CHURCH = 8,
    CASINO = 9,
    JOB_HUNT,
    LEVEL_END = 99
}

[System.Serializable]
public class SpriteScreen
{
    public AudioClip clip;
    public Sprite sprite;
    public CycleState levelState;
}

public class Cycle : FSM<CycleState>
{
    private bool _playerTurn = false;
    private bool _currentPlayerTurn= false;
    private int _jobDelta = 0;
    private const int _maxButtonText = 12;
    private const float _bubbleWaitTime = 4.5f;
    private GameManager _gm;
    private string _output;
    private float _scoreFactor = 1f;
    private float _interviewScoreFactor = 1f;
    private float _interviewScore = 0f;
    private int _cycle = -1;
    private int _jobLevel = 0;
    private TwineStory _activeStory = null;
    private List<string> _nextPassages = new List<string>();
    private List<ActionType> _nextActions = new List<ActionType>();
    private Dictionary<CycleState, int> _playedStates = new Dictionary<CycleState, int>();
    private Dictionary<CycleState, List<Sprite>> _backScreens = new Dictionary<CycleState, List<Sprite>>();
    private Dictionary<CycleState, List<AudioClip>> _clips = new Dictionary<CycleState, List<AudioClip>>();
    public OptionScreen optionScreen;
    public OptionScreen crossRoadsScreen;
    public SpriteScreen[] screens;
    public BackScreen backScreen;
    public TwineStory interviewHandshake;
    public TwineStory interviewSkills;
    public TwineStory interviewBackground;
    public TwineStory[] interviewMindGames;
    public TwineStory[] interviewConclusion;

    protected override void Awake()
    {
        base.Awake();
        foreach (CycleState state in CycleState.GetValues(typeof(CycleState)))
        {
            _backScreens[state] = new List<Sprite>();
            _clips[state] = new List<AudioClip>();
            _playedStates[state] = 0;
        }
        for (int i = 0; i < screens.Length; ++i)
        {
            CycleState state = screens[i].levelState;
            if (screens[i].sprite != null)
                _backScreens[state].Add(screens[i].sprite);
            if (screens[i].clip != null)
                _clips[state].Add(screens[i].clip);
        }
        _output += "                                                                                                    ";
        _output = "";
    }
    private void Start()
    {
        _gm = GameManager.Instance();
        GameStats stats = _gm.Stats;
        //StartGame(stats.asMan);
        StartGame(false);
    }
    public void StartGame(bool male)
    {
        if (male)
            backScreen.SelectMale();
        else
            backScreen.SelectWoman();
        float factor = Random.Range(-0.05f, 0.05f);
        backScreen.RecordResults(0.15f - factor, 0.15f + factor, 0);
        NewLevel();
    }
    public void NewLevel()
    {
        Debug.Assert(_jobLevel < _gm.jobLevels.Length);
        _cycle++;
        _scoreFactor = 1f + _gm.jobLevels[_jobLevel].stakes / 100f;
        EnterState(CycleState.CROSSROADS, true);
    }
    public override int ToInt(CycleState state)
    {
        return (int)state;
    }
    public void ResetAndNewCycle()
    {
        TwineStory[] s = { interviewHandshake, interviewSkills, interviewBackground };
        foreach (TwineStory story in s)
        {
            story.timesPlayed = 0;
        }
        foreach (TwineStory story in interviewMindGames)
        {
            story.timesPlayed = 0;
        }
        foreach (TwineStory story in interviewConclusion)
        {
            story.timesPlayed = 0;
        }
        foreach (CycleState state in CycleState.GetValues(typeof(CycleState)))
        {
            _playedStates[state] = 0;
        }
        _output = "";
        NewLevel();
    }
    private BackScreen GetRandomScreen(CycleState state)
    {
        int i = Random.Range(0, _backScreens[state].Count);
        backScreen.SetBackImage(_backScreens[state][i]);
        return backScreen;
    }
    private void PlayThought(string thoug)
    {
        if (_currentPlayerTurn)
        {
            backScreen.Think(thoug);
        }
        else
        {
            backScreen.SceneThink(thoug);
        }
    }
    private void PlayVoice(string voic)
    {
        if (_currentPlayerTurn)
        {
            backScreen.Say(voic);
        }
        else
        {
            backScreen.SceneSay(voic);
        }
    }
    public IEnumerator PlayOutput(UnityAction after)
    {
        int index = _output.IndexOf('{');
        if (index != -1)
        {
            if (index > 1)
            {
                PlayVoice(_output.Substring(0, index));
            }
            yield return new WaitForSeconds(_bubbleWaitTime);
            int index2 = _output.IndexOf('}');
            PlayThought(_output.Substring(index + 1, index2 - index - 1));
            yield return new WaitForSeconds(_bubbleWaitTime);
            if ((index2 + 3) < _output.Length)
            {
                PlayVoice(_output.Substring(index2 + 1));
                yield return new WaitForSeconds(_bubbleWaitTime);
            }
        }
        else
        {
            PlayVoice(_output);
            yield return new WaitForSeconds(_bubbleWaitTime);
        }
        _output = "";
        if (after != null) 
            after.Invoke();
    }
    public bool BeginRandomStory(TwineStory[] options, int playedThreshold = 1)
    {
        List<TwineStory> available = new List<TwineStory>();
        foreach (TwineStory story in options)
        {
            if (story.timesPlayed < playedThreshold)
            {
                available.Add(story);
            }
        }
        if (available.Count > 0)
        {
            int choice = Random.Range(0, available.Count);
            BeginStory(available[choice]);
            return true;
        }
        return false;
    }

    public void OnStoryOutput(TwineOutput output)
    {
        if (output.Text != null && output.Text.Length > 1 && output.Name == null)
        {
            _output += output.Text;
        }
    }

    public void BeginStory(TwineStory story)
    {

        _activeStory = story;
        _playerTurn = false;
        _currentPlayerTurn= false;
        _activeStory.OnStateChanged += OnInterviewChange;
        _activeStory.OnOutput += OnStoryOutput;
        _activeStory.timesPlayed++;
        _activeStory.Begin();
    }
    public void OnActionSelected(int index)
    {

    }
    public void ShowOptionScreen(string[] options, string[] followups, UnityAction<int> callback = null)
    {
        if (callback == null)
            callback = optionScreen.callback;
        optionScreen.Clear();
        if (followups != null)
        {
            _nextPassages.Clear();
            _nextPassages.AddRange(followups);
        }
        foreach (string option in options)
        {
            optionScreen.Add(option);
        }
        optionScreen.callback = callback;
        optionScreen.Show();
    }
    public void ShowOptions()
    {
        List<TwineLink> links = _activeStory.Links;
        List<string> texts = new List<string>();
        _nextPassages.Clear();
        string text = "                                                                                          ";
        foreach (TwineLink link in links)
        {
            TwinePassage nextPass = _activeStory.GetPassage(link.PassageName);
            _nextPassages.Add(nextPass.Name);
            text = "";
            foreach (TwineOutput outpu in nextPass.Execute())
            {
                if (outpu.Name == null && outpu.Text != null && outpu.Text.Length > 1)
                {
                    text += outpu.Text;
                }
            }
            int index = text.IndexOf('{');
            if (index != -1)
            {
                int index2 = text.IndexOf('}');
                text = text.Substring(0, index) + text.Substring(index2 + 1);
            }
            if (text.Length >= _maxButtonText)
            {
                text = text.Substring(0, text.LastIndexOf(' ')) + " ...";
            }
            texts.Add(text);
        }
        ShowOptionScreen(texts.ToArray(), null, optionScreen.callback);
    }
    public void Resume()
    {
        _activeStory.Advance(_activeStory.Links[0]);
    }
    [StateEnterMethod("", 2)]//CROSSROADS
    public void EnterCrossroads()
    {
        _playedStates[CycleState.CROSSROADS] += 1;
        GetRandomScreen(CycleState.CROSSROADS).Show();
        if (_cycle == 0 && _playedStates[CycleState.CROSSROADS] == 1)
        {
            backScreen.PlayResults(EnterCrossroads);
            return;
        }
        crossRoadsScreen.Clear();
        if (_playedStates[CycleState.INTERVIEW] < 1)
        {
            crossRoadsScreen.Add("Go Job Hunting");
            _nextActions.Add(ActionType.JOB_HUNT);
        }
        if (_playedStates[CycleState.CHURCH] > 0 && Random.Range(0f, 1f) > 0.5f)
        {
            crossRoadsScreen.Add("Get On with Your Life");
            _nextActions.Add(ActionType.GO_ON);
        }
        else
        {
            crossRoadsScreen.Add("Visit the Library");
            _nextActions.Add(ActionType.LIBRARY);
            crossRoadsScreen.Add("Pray at the Church");
            _nextActions.Add(ActionType.CHURCH);
            crossRoadsScreen.Add("Try the Casino");
            _nextActions.Add(ActionType.CASINO);
            crossRoadsScreen.Add("Go to the Bar");
            _nextActions.Add(ActionType.BAR);
            crossRoadsScreen.Add("Look for Honey Honey");
            _nextActions.Add(ActionType.PAID_SEX);
            crossRoadsScreen.Add("Get on with your Life");
            _nextActions.Add(ActionType.GO_ON);
        }
        crossRoadsScreen.callback = OnCrossSelected;
        crossRoadsScreen.Show();
    }
    public void OnCrossSelected(int option)
    {
        ActionType selected = _nextActions[option];
        float wealthPlus = 0f;
        float mojoPlus = 0f;
        GameAction.GetResult(selected, _scoreFactor, ref wealthPlus, ref mojoPlus);
        backScreen.RecordResults(wealthPlus, mojoPlus, _jobLevel);

        switch (selected)
        {
            case ActionType.JOB_HUNT:
                GetRandomScreen(CycleState.JOB_HUNT).Show();
                backScreen.PlayResults(null);
                Invoke("JobHunt", 2f);
                break;
            case ActionType.BAR:
                _playedStates[CycleState.BAR] += 1;
                GetRandomScreen(CycleState.BAR).Show();
                EnterState(CycleState.BAR);
                backScreen.PlayResults(EnterCrossroads);
                break;
            case ActionType.CHURCH:
                _playedStates[CycleState.CHURCH] += 1;
                GetRandomScreen(CycleState.CHURCH).Show();
                EnterState(CycleState.CHURCH);
                backScreen.PlayResults(EnterCrossroads);
                break;
            case ActionType.LIBRARY:
                _playedStates[CycleState.LIBRARY] += 1;
                GetRandomScreen(CycleState.LIBRARY).Show();
                EnterState(CycleState.LIBRARY);
                backScreen.PlayResults(EnterCrossroads);
                break;
            case ActionType.CASINO:
                _playedStates[CycleState.CASINO] += 1;
                GetRandomScreen(CycleState.CASINO).Show();
                EnterState(CycleState.CASINO);
                backScreen.PlayResults(EnterCrossroads);
                break;
            case ActionType.PAID_SEX:
                _playedStates[CycleState.PAID_SEX] += 1;
                GetRandomScreen(CycleState.PAID_SEX).Show();
                EnterState(CycleState.PAID_SEX);
                backScreen.PlayResults(EnterCrossroads);
                break;
            case ActionType.GO_ON:
                GetRandomScreen(CycleState.WAIT).Show();
                EnterState(CycleState.WAIT);
                backScreen.PlayResults(ResetAndNewCycle);
                break;
        }
    }

    [StateEnterMethod("", 3)]
    public void EnterInterview()
    {
        _playedStates[CycleState.INTERVIEW] += 1;
        optionScreen.Clear();
        optionScreen.Add("Stay " + _gm.jobLevels[_jobLevel].name);
        if ((_jobLevel + 1) < _gm.jobLevels.Length)
            optionScreen.Add("Try " + _gm.jobLevels[_jobLevel + 1].name);
        if ((_jobLevel + 2) < _gm.jobLevels.Length)
            optionScreen.Add("What About " + _gm.jobLevels[_jobLevel + 2].name + "?");
        optionScreen.callback = OnJobChosen;
        optionScreen.Show();
    }

    public void JobHunt()
    {
        EnterState(CycleState.INTERVIEW);
    }
    public void OnJobChosen(int job)
    {
        if(job == 0 && _jobLevel == 0)
        {
            float wealthPlus = 0f;
            float mojoPlus = 0f;
            GameAction.GetResult(ActionType.GO_ON, _scoreFactor, ref wealthPlus, ref mojoPlus);
            backScreen.RecordResults(wealthPlus, mojoPlus, _jobLevel);
            backScreen.PlayResults(EnterCrossroads);
            return;
        }
        _jobDelta = job;
        _interviewScoreFactor = 1f + _gm.jobLevels[_jobLevel + job].stakes / 100f;
        GetRandomScreen(CycleState.INTERVIEW).Show();
        BeginStory(interviewHandshake);
    }
    public void UpdateInterviewScore(string passageName)
    {
        TwinePassage passage = _activeStory.GetPassage(passageName);
        float deltaMojo = 0f;
        float deltaWealth = 0f;
        foreach (string tag in passage.Tags)
        {
            ActionType action = ActionType.IDLE;
            switch (tag)
            {
                case "cheery":
                    action = ActionType.CHEERY;
                    break;
                case "stoic":
                    action = ActionType.STOIC;
                    break;
                case "unfriendly":
                    action = ActionType.UNFRIENDLY;
                    break;
                case "humble":
                    action = ActionType.HUMBLE;
                    break;
                case "jackass":
                    action = ActionType.JACKASS;
                    break;
                case "spaced":
                    action = ActionType.SPACED;
                    break;
                case "diligent":
                    action = ActionType.DILIGENT;
                    break;
                case "mojoplus":
                    action = ActionType.MOJOPLUS;
                    break;
                case "anxious":
                    action = ActionType.CHEERY;
                    break;
            }
            if (action != ActionType.IDLE)
                GameAction.GetResult(action, _interviewScoreFactor, ref deltaWealth, ref deltaMojo);
        }
        _interviewScore += deltaWealth;
        backScreen.RecordResults(0, deltaMojo, _jobLevel);
        backScreen.PlayResults(null);
    }
    public void OnInterviewChosen(int option)
    {
        optionScreen.Hide();
        string passageName = _activeStory.Links[option].PassageName;
        _activeStory.Enter(passageName);
    }
    public void OnExitInterviewStory()
    {
        if (_activeStory == interviewHandshake)
        {
            TwineStory[] skillsBack = { interviewBackground, interviewSkills };
            BeginRandomStory(skillsBack);
        }
        else if (_activeStory == interviewBackground || _activeStory == interviewSkills)
        {

            if (Random.Range(0, 2) == 0)
            {
                if (!BeginRandomStory(interviewMindGames))
                {
                    BeginStory(_interviewScore > 0 ? interviewConclusion[0] : interviewConclusion[1]);
                }
            }
            else
            {
                    BeginStory(_interviewScore > 0 ? interviewConclusion[0] : interviewConclusion[1]);
            }
        }
        else if (_activeStory == interviewConclusion[0] || _activeStory == interviewConclusion[1])
        {
            if (_activeStory == interviewConclusion[0])
                _jobLevel += _jobDelta;
            backScreen.RecordResults(_interviewScore, 0, _jobLevel);
            backScreen.PlayResults(EnterCrossroads);
        }
        else //some mind game
        {
            TwineStory[] skillsBack = { interviewBackground, interviewSkills};
            if (!BeginRandomStory(skillsBack))
            {
                BeginStory(_interviewScore > 0 ? interviewConclusion[0] : interviewConclusion[1]);
            }
        }
    }
    public void OnInterviewChange(TwineStoryState state)
    {
        Debug.Log("" + state + _activeStory.name + _activeStory.CurrentPassageName);
        if (state == TwineStoryState.Complete)
        {
            _currentPlayerTurn = _playerTurn;
            UpdateInterviewScore(_activeStory.CurrentPassageName);
            StartCoroutine(PlayOutput(OnExitInterviewStory));
        }
        else if (state == TwineStoryState.Idle)
        {
            _currentPlayerTurn = _playerTurn;
            bool showoptions = false;
            if ( _activeStory.CurrentPassageName == "Start")
            {
                showoptions = false;
            }
            else if( _activeStory.Links.Count > 1)
            {
                showoptions = true;
            }
            _playerTurn = !_currentPlayerTurn;
            TwinePassage passage = _activeStory.GetPassage(_activeStory.CurrentPassageName);
            foreach (string tag in passage.Tags)
            {
                if (tag == "nobuttons")
                {
                    showoptions = false;
                    _playerTurn = false;
                }
            }
            UpdateInterviewScore(_activeStory.CurrentPassageName);
            if(showoptions)
            {
                _playerTurn = true;
                optionScreen.callback = OnInterviewChosen;
                StartCoroutine(PlayOutput(ShowOptions));
            }
            else
            {
                StartCoroutine(PlayOutput(Resume));
            }
        }
    }

}
