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

public class GameState : MonoBehaviour {

    public uint strikesTillGameOver = 5;
    public UnityEngine.UI.Text scoreText;
    public UnityEngine.UI.Text strikeText;
    //public GameObject greetingSuccessHolder;
    //public GameObject newFeatureHolder;
    //public GameObject menuTextPrefab;
    //public float timeToRemoveSuccess = 3.0f;
    
    public List<string> greetings; // + sup, dude
    public List<string> failureMessages;

    public GameObject characterSpawnLocation;

    public List<UnityEngine.UI.Button> greetingMenuButtons;
    public List<UnityEngine.UI.Text> greetingMenuText;

    public List<int> greetingsTillNewList;

    [HideInInspector]
    public int noGreetings = 1;
    [HideInInspector]
    public int greetingsAttempted = 0;
    [HideInInspector]
    public uint score = 0; // successful greetings
    [HideInInspector]
    public uint strikes = 0;
    [HideInInspector]
    public Guest currentCharacter;
    //[HideInInspector]
    public List<FeatureInfo> featurePresetInfo;
    
    private int greetingsTillNew_idx = 0;
    [HideInInspector]
    public int greetingsTillNew;

    public float greetTime = 5f;
    public float leaveTime = 10f;

    public List<Sprite> Sprite00Body;
    public List<Sprite> Sprite01Suit;
    public List<Sprite> Sprite02Eyes;
    public List<Sprite> Sprite03Hair;

    public GameObject Portrait;
    public UnityEngine.UI.Image Body;
    public UnityEngine.UI.Image Suit;
    public UnityEngine.UI.Image Eyes;
    public UnityEngine.UI.Image Hair;

    public float Chance3Mod = 0.3f;
    public float Chance2Mod = 0.5f;

    public List<UnityEngine.UI.Image> Mods;

    public List<Sprite> SpriteMods1;
    public List<Sprite> SpriteMods2;
    public List<Sprite> SpriteMods3;
    public List<Sprite> SpriteMods4;
    public List<Sprite> SpriteMods5;
    public List<Sprite> SpriteMods6;
    public List<Sprite> SpriteMods7;
    public List<Sprite> SpriteMods8;

    private CharacterSpawner characterSpawner;
    private SpawnTest spawnTester;

    [HideInInspector]
    public List<int> greetedMods = new List<int>();

    public float performancePeriod = 20f;
    public int noAttemptedInPeriod = 0;
    public int noSuccessInPeriod = 0;
    public AnimationCurve successRateRequiredByAttempts;

    public List<UnityEngine.UI.Image> ModUIImages;
    public List<Sprite> ModUISprites;

    public float PerformanceScore()
    {
        var percent = noAttemptedInPeriod != 0 ? noSuccessInPeriod / (float)noAttemptedInPeriod : 1f;
        var minimum = successRateRequiredByAttempts.Evaluate((float)noAttemptedInPeriod);
        return Math.Max(0.0f, (percent - minimum) / (1.0f - minimum));
    }

    public void AddFailInPerformance()
    {
        ++noAttemptedInPeriod;
        Invoke("RemoveFailInPerformance", performancePeriod);
    }

    public void RemoveFailInPerformance()
    {
        --noAttemptedInPeriod;
    }

    public void AddSuccessInPerformance()
    {
        ++noSuccessInPeriod;
        ++noAttemptedInPeriod;
        Invoke("RemoveSuccessInPerformance", performancePeriod);
    }

    public void RemoveSuccessInPerformance()
    {
        --noSuccessInPeriod;
        --noAttemptedInPeriod;
    }

    public List<T> ListShuffle<T>(List<T> alpha)
    {
        var result = new List<T>(alpha);
        for (int i = 0; i < result.Count; i++)
        {
            T temp = result[i];
            int randomIndex = UnityEngine.Random.Range(i, alpha.Count);
            result[i] = result[randomIndex];
            result[randomIndex] = temp;
        }

        return result;
    } 

    public List<List<Sprite>> SpriteModsByCategories()
    {
        return new List<List<Sprite>>() { SpriteMods1, SpriteMods2, SpriteMods3, SpriteMods4,
                                            SpriteMods5, SpriteMods6, SpriteMods7, SpriteMods8 };
    }

    public int TotalNoMods()
    {
        return featurePresetInfo.Count;
    }

    void updateScores()
    {
        scoreText.text = string.Format("Score: {0}", score);
        strikeText.text = string.Format("Strikes: {0}", strikes);
    }

    private void Awake()
    {
        spawnTester = FindObjectOfType<SpawnTest>();
        characterSpawner = FindObjectOfType<CharacterSpawner>();
    }

    // Use this for initialization
    void Start ()
    {
        var randomized_sprites = ListShuffle(ModUISprites);
        for (int i = 0; i < randomized_sprites.Count; ++i)
            ModUIImages[i].sprite = randomized_sprites[i];

        updateScores();

        var availableGreetings = new List<string>(greetings);
        featurePresetInfo = new List<FeatureInfo>();


        for(int mod_list_idx = 0; mod_list_idx < SpriteModsByCategories().Count; ++mod_list_idx)
        {
            var mod_list = SpriteModsByCategories()[mod_list_idx];

            string greet;
            if (availableGreetings.Any())
            {
                var idx = UnityEngine.Random.Range(0, availableGreetings.Count);
                greet = availableGreetings[idx];
                availableGreetings.RemoveAt(idx);
            }
            else
            {
                greet = greetings[UnityEngine.Random.Range(0, greetings.Count)];
            }

            //foreach (var feature in mod_list)
            //{
            //    var fi = new FeatureInfo();
            //    fi.greeting = greet;
            //    fi.feature = feature;
            //    fi.mod_category = mod_list_idx;
            //    featurePresetInfo.Add(fi);
            //}

            var fi = new FeatureInfo();
            fi.greeting = greet;
            fi.mod_category = mod_list_idx;
            featurePresetInfo.Add(fi);
        }

        featurePresetInfo = ListShuffle(featurePresetInfo);

        //CreateGreetingMenu(CurrentCharacterInfos().Select((info) => info.greeting));

        greetingsTillNew = greetingsTillNewList[greetingsTillNew_idx];
        //DoCharacterWithNewGreeting();
        
        Portrait.SetActive(false);
    }

    List<FeatureInfo> CurrentFeatureInfos()
    {
        return featurePresetInfo.GetRange(0, noGreetings);
    }

    public void GuestLeftWithoutGreet()
    {
        ++strikes;
        AddFailInPerformance();
    }

    public void EndGreeting()
    {
        Portrait.SetActive(false);
        CleanupNewIndicator();
        currentCharacter = null;

        for (int i = 0; i < greetingMenuButtons.Count; ++i)
            greetingMenuButtons[i].interactable = false;
    }

    public void GreetingSelected(Guest guest, FeatureInfo greeting)
    {
        greetedMods.Add(greeting.mod_category);
        greetedMods = greetedMods.Distinct().ToList();

        Debug.Log(greeting);

        guest.greeted = true;

        var success = currentCharacter.modsInfo.Any((x) => x == greeting);
        if (success)
        {
            Debug.Log("correct");
            ++score;
            AddSuccessInPerformance();
            updateScores();

            //tell guest to go to seat
            SeatAllocator.instance.targetGuest.GreetedCorrectly();
        }
        else
        {
            Debug.Log("bad");
            ++strikes;
            AddFailInPerformance();
            updateScores();
            //tell guest to go to seat
            SeatAllocator.instance.targetGuest.GreetedIncorrectly();
        }
        IndicateSuccess(success, greeting.greeting);
        //Destroy(currentCharacter);
        ++greetingsAttempted;
        --greetingsTillNew;

        var any_to_greet = spawnTester.spawnParent.GetComponentsInChildren<Guest>().Any((x) => !x.greeted);
        if(!any_to_greet)
        {
            characterSpawner.CancelInvoke();
            characterSpawner.SpawnAndReset();
        }

        EndGreeting();

        //BeginNewGreeting();
    }

    public void IndicateSuccess(bool success, string greetingChosen)
    {
        //CancelInvoke("CleanupSuccessIndicator");
        //CleanupSuccessIndicator();

        //var successItem = Instantiate(menuTextPrefab, Vector3.zero, Quaternion.identity);
        //successItem.transform.GetChild(0).GetComponent<UnityEngine.UI.Text>().text =
        //    success ? greetingChosen : failureMessages[UnityEngine.Random.Range(0, failureMessages.Count)];
        //successItem.transform.SetParent(greetingSuccessHolder.transform);

        //Invoke("CleanupSuccessIndicator", timeToRemoveSuccess);
    }

    private void CleanupSuccessIndicator()
    {
        //for (int i = 0; i < greetingSuccessHolder.transform.childCount; ++i)
        //    Destroy(greetingSuccessHolder.transform.GetChild(i).gameObject);
    }

    public void BeginNewGreeting(Guest guest)
    {
        if (!guest.seen)
        {
            guest.seen = true;
            var newGreeting = greetingsTillNew == 0;
            if (newGreeting && noGreetings != TotalNoMods())
            {
                ++greetingsTillNew_idx;
                ++noGreetings;
                var requires_reset = greetingsTillNew_idx >= greetingsTillNewList.Count;
                if (requires_reset)
                    greetingsTillNew_idx = greetingsTillNewList.Count - 1;
                greetingsTillNew = greetingsTillNewList[greetingsTillNew_idx];
                DoCharacterWithNewGreeting(guest);
            }
            else
            {
                var info = featurePresetInfo[UnityEngine.Random.Range(0, noGreetings)];
                guest.Body = Sprite00Body[UnityEngine.Random.Range(0, Sprite00Body.Count)];
                guest.Suit = Sprite01Suit[UnityEngine.Random.Range(0, Sprite01Suit.Count)];
                guest.Eyes = Sprite02Eyes[UnityEngine.Random.Range(0, Sprite02Eyes.Count)];
                guest.Hair = Sprite03Hair[UnityEngine.Random.Range(0, Sprite03Hair.Count)];
                guest.modsInfo = RandomMods(false);
                guest.modsSprites = guest.modsInfo.Select((mod) => {
                    var sprite_list = SpriteModsByCategories()[mod.mod_category];
                    return sprite_list[UnityEngine.Random.Range(0, sprite_list.Count)];
                }).ToList();
                DoCharacter(guest);
            }
        }
        else
            DoCharacter(guest);
    }

    public void CleanupGreetingMenu()
    {
        for (int i = 0; i < greetingMenuButtons.Count; ++i)
            greetingMenuButtons[i].interactable = false;
    }

    public void CreateGreetingMenu(Guest guest, List<FeatureInfo> some_greets, FeatureInfo new_greet)
    {
        some_greets = some_greets.Distinct().ToList();
        CleanupGreetingMenu();

        for (int i = 0; i < some_greets.Count; ++i)
        {
            var this_greet = some_greets[i];
            var button = greetingMenuButtons[i];
            greetingMenuText[i].text = this_greet.greeting;
            button.onClick.RemoveAllListeners();
            button.onClick.AddListener(() => { GreetingSelected(guest, this_greet); });
            button.interactable = (new_greet == null) || (greetingMenuText[i].text == new_greet.greeting);
        }

        for (int i = some_greets.Count; i < greetingMenuButtons.Count; ++i)
        {
            var button = greetingMenuButtons[i];
            greetingMenuText[i].text = "";
            button.onClick.RemoveAllListeners();
            button.interactable = false;
        }
    }

    public List<FeatureInfo> RandomMods(bool has_new)
    {
        int no_mods;
        var r = UnityEngine.Random.Range(0f, 1f);
        if (r < 1 - Chance3Mod - Chance2Mod)
            no_mods = 1;
        else if (r < 1 - Chance3Mod)
            no_mods = 2;
        else
            no_mods = 3;

        no_mods = Mathf.Min(noGreetings, no_mods);
        
        var mods = new List<FeatureInfo>();

        for (int mod_i = has_new ? 1 : 0; mod_i < no_mods; ++mod_i)
        {
            FeatureInfo mod;
            do
            {
                mod = featurePresetInfo[UnityEngine.Random.Range(0, noGreetings)];
            }
            while (mods.Any((x) => x.mod_category == mod.mod_category));
            mods.Add(mod);
        }

        return mods;
    }

    public void DoCharacterWithNewGreeting(Guest guest)
    {
        guest.Body = Sprite00Body[UnityEngine.Random.Range(0, Sprite00Body.Count)];
        guest.Suit = Sprite01Suit[UnityEngine.Random.Range(0, Sprite01Suit.Count)];
        guest.Eyes = Sprite02Eyes[UnityEngine.Random.Range(0, Sprite02Eyes.Count)];
        guest.Hair = Sprite03Hair[UnityEngine.Random.Range(0, Sprite03Hair.Count)];
        guest.modsInfo = RandomMods(true);
        var lastMod = CurrentFeatureInfos().Last();
        guest.modsInfo.Add(lastMod);
        guest.modsSprites = guest.modsInfo.Select((mod) => {
            var sprite_list = SpriteModsByCategories()[mod.mod_category];
            return sprite_list[UnityEngine.Random.Range(0, sprite_list.Count)];
        }).ToList();

        CreateGreetingMenu(guest, CurrentFeatureInfos(), lastMod);
        CreatePortrait(guest);
        setNewIndictor();
    }

    public void DoCharacter(Guest guest)
    {
        CreatePortrait(guest);

        var ungreeted_info = CurrentFeatureInfos().Find((info) => !greetedMods.Any((x) => x == info.mod_category));
        CreateGreetingMenu(guest, CurrentFeatureInfos(), ungreeted_info);
    }

    private void setNewIndictor()
    {
        //var successItem = Instantiate(menuTextPrefab, Vector3.zero, Quaternion.identity);
        //successItem.transform.GetChild(0).GetComponent<UnityEngine.UI.Text>().text = "New Feature";
        //successItem.transform.SetParent(newFeatureHolder.transform);
    }

    private void CleanupNewIndicator()
    {
        //for (int i = 0; i < newFeatureHolder.transform.childCount; ++i)
        //    Destroy(newFeatureHolder.transform.GetChild(i).gameObject);
    }

    public void CreatePortrait(Guest guest)
    {
        Portrait.SetActive(true);
        Body.sprite = guest.Body;
        Suit.sprite = guest.Suit;
        Eyes.sprite = guest.Eyes;
        Hair.sprite = guest.Hair;

        foreach (var mod in Mods)
            mod.gameObject.SetActive(true);

        if (guest.modsInfo.Count < Mods.Count)
            Mods[guest.modsInfo.Count].gameObject.SetActive(false);

        for(int i = 0; i < guest.modsInfo.Count; ++i)
            Mods[i].sprite = guest.modsSprites[i];

        //var go = Instantiate(guest.info.feature, characterSpawnLocation.transform.position,
        //    Quaternion.LookRotation(Vector3.up, Vector3.forward));
        //var character = go.AddComponent<Character>();
        //character.info = info;
        currentCharacter = guest;
    }

    // Update is called once per frame
    void Update () {
        Debug.Log( string.Format("{0}, ({1}/{2}) : {3}", PerformanceScore(), noSuccessInPeriod, noAttemptedInPeriod, (PerformanceScore() > 0.0f ? "okay" : "fail")));

        if (strikes >= strikesTillGameOver)
            FindObjectOfType<UIController>().GameOver();
    }
}
