using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.UI;
using Assets.Scripts.Classes;
using Assets.Scripts.Races;

namespace Assets.Scripts
{
    public class Player : MonoBehaviour
    {
        public float speed = 5f;
        System.Random rand = new System.Random();
        DateTime now;
        UIHandler uihandler;
        AudioHandler audioHandler;
        int bobbingDirection = -1;

        string playername;
        int health;
        int maxHealth;
        int maxSecondary;
        int secondary;
        int strength;
        int dexterity;
        int intelligence;
        int level;
        int experience;
        int maxExperience;
        BasicRace race;
        BasicClass role;
        BasicSkill[] skills = new BasicSkill[3];
        int points = 0;
        bool isDodging = false;

        int killcount = -1;
        int difficulty = 0;

        // Start is called before the first frame update
        void Start()
        {
            uihandler = GameObject.Find("UIHandler").GetComponent<UIHandler>();
            audioHandler = GameObject.Find("AudioHandler").GetComponent<AudioHandler>();
        }

        public void generatePlayer(BasicRace race, BasicClass role, string name, int difficulty)
        {
            playername = name;
            health = 100;
            maxHealth = 100;
            maxSecondary = 20;
            secondary = 20;
            strength = 5;
            dexterity = 5;
            intelligence = 5;
            experience = 0;
            maxExperience = 10;
            level = 0;
            killcount = 0;
            this.race = race;
            this.race.applyBonus(this);
            this.role = role;
            this.role.applyBonus(this);
            this.difficulty = difficulty;
            generateSkills();
        }

        private void generateSkills()
        {
            switch (role.classname)
            {
                case "Warrior":
                    skills[0] = new BasicSkill(15,10,2,"Slash","Skills/Warrior/Slash",null);
                    skills[0].setDescription("A basic slash. But better than a basic attack");
                    skills[1] = new BasicSkill(0, 5, 1, "Block", "Skills/Warrior/Block", null);
                    skills[1].setDescription("Block the next attack");
                    skills[2] = new BasicSkill(30, 30, 4, "Execution", "Skills/Warrior/Execution", null);
                    skills[2].setDescription("A powerful skill. But hard to cast early");
                    break;
                case "Thief":
                    skills[0] = new BasicSkill(15, 10, 2, "Stab", "Skills/Thief/Stab", null);
                    skills[0].setDescription("Early skill to apply some damage.");
                    skills[1] = new BasicSkill(0, 5, 1, "SmokeScreen", "Skills/Thief/SmokeScreen", null);
                    skills[1].setDescription("Hide from the next attack");
                    skills[2] = new BasicSkill(30, 30, 4, "Heartstop", "Skills/Thief/Heartstop", null);
                    skills[2].setDescription("Stop the heart of your enemy. High damage.");
                    break;
                case "Mage":
                    skills[0] = new BasicSkill(15, 10, 2, "Icicle", "Skills/Mage/Icicle", null);
                    skills[0].setDescription("Small icicles to cut down your enemies HP");
                    skills[1] = new BasicSkill(0, 5, 1, "Teleport", "Skills/Mage/Teleport", null);
                    skills[1].setDescription("Evade the next attack");
                    skills[2] = new BasicSkill(30, 30, 4, "Fireball", "Skills/Mage/Fireball", null);
                    skills[2].setDescription("What a big fireball. Does some good damage");
                    break;
            }
        }

        public void changeStats(int strength, int health, int dexterity, int intelligence, int secondary)
        {
            this.maxHealth = this.maxHealth + health;
            this.health = maxHealth;
            this.maxSecondary = this.maxSecondary + secondary;
            this.secondary = maxSecondary;
            this.strength = this.strength + strength;
            this.dexterity = this.dexterity + dexterity;
            this.intelligence = this.intelligence + intelligence;
        }

        // Update is called once per frame
        void Update()
        {
            if (killcount != -1)
            {
                getRotation();
                regeneratePlayer();
                uihandler.adjustInformation(this);
            }
        }

        private void regeneratePlayer()
        {
            if (uihandler.state != UIState.FIGHT)
            {
                if (now == null)
                {
                    now = DateTime.Now;
                }
                else
                {
                    if (now.AddSeconds(10).CompareTo(DateTime.Now) <= 0)
                    {
                        now = DateTime.Now;
                        regainSecondary(5);
                        healPlayer();
                    }
                }
            }
            else
            {
                now = DateTime.Now;
            }
        }

        public void regainSecondary(int amount)
        {
            secondary = secondary + amount;
            if (secondary >= maxSecondary)
            {
                secondary = maxSecondary;
            }
        }

        private void healPlayer()
        {
            health = health + 5;
            if (health >= maxHealth)
            {
                health = maxHealth;
            }
        }

        public void move()
        {
            float x = Input.GetAxis("Horizontal");
            float z = Input.GetAxis("Vertical");

            if (Input.GetKeyDown(KeyCode.Space))
            {
                if ((int)(gameObject.GetComponent<Rigidbody>().velocity.y * 10) == 0)
                {
                    gameObject.GetComponent<Rigidbody>().velocity = new Vector3(0, 5, 0);
                    audioHandler.playJump();
                }
            }

            Vector3 movement = new Vector3(0, 0, z);
            Vector3 rotation = new Vector3(0, x * 20, 0);

            gameObject.transform.Rotate(rotation * speed * Time.deltaTime);
            gameObject.transform.Translate(movement * speed * Time.deltaTime);

            if (z != 0)
            {
                GameObject camera = GameObject.Find("Main Camera");

                if (camera.transform.localPosition.y >= 0.5)
                {
                    bobbingDirection = -1;
                }
                else if (camera.transform.localPosition.y <= 0.3)
                {
                    bobbingDirection = 1;
                }

                camera.transform.Translate(new Vector3(0, bobbingDirection, 0) * Time.deltaTime / 2);
            }
        }

        public void getRotation()
        {
            GameObject.Find("North").GetComponent<Text>().color = Color.white;
            GameObject.Find("East").GetComponent<Text>().color = Color.white;
            GameObject.Find("South").GetComponent<Text>().color = Color.white;
            GameObject.Find("West").GetComponent<Text>().color = Color.white;
            float rotation = gameObject.transform.rotation.eulerAngles.y;
            if (rotation < 0)
            {
                rotation += 360;
            }
            if (rotation <= 44 || rotation >= 315)
            {
                GameObject.Find("North").GetComponent<Text>().color = Color.red;
            }
            else if (rotation >= 45 && rotation <= 134)
            {
                GameObject.Find("East").GetComponent<Text>().color = Color.red;
            }
            else if (rotation >= 135 && rotation <= 224)
            {
                GameObject.Find("South").GetComponent<Text>().color = Color.red;
            }
            else if (rotation >= 225 && rotation <= 314)
            {
                GameObject.Find("West").GetComponent<Text>().color = Color.red;
            }
        }

        public int[] getStats()
        {
            int[] result = { health, maxHealth, secondary, maxSecondary, strength, dexterity, intelligence, level, experience, maxExperience, points};
            return result;
        }

        public void displayAction(int index, GameObject image, GameObject desc)
        {
            skills[index].display(image, desc, secondary);
        }

        public int calculateDamage()
        {
            int result;
            int bonus = 0;
            if (isCrit())
            {
                bonus = strength * 2;
            }
            result = strength + bonus + 5;
            switch (difficulty)
            {
                case 0:
                    result = result * 2;
                    break;
                case 1:
                    //Default difficulty
                    break;
                case 2:
                    result = result / 2;
                    break;
            }
            return result;
        }

        public void updateKillcount(GameObject gameObject)
        {
            gameObject.GetComponent<Text>().text = "Slimes: " + killcount + "/" + (20 * (difficulty + 1));
        }

        public int castSkill(int index)
        {
            int damage = 0;
            if (skills[index].canPlayerCast(secondary))
            {
                if (index == 1)
                {
                    isDodging = true;
                }
                secondary = secondary - skills[index].getSecondaryConsumption();
                if (role.classname == "Wizard")
                {
                    damage = skills[index].calculateDamage(intelligence, isCrit());
                }
                else
                {
                    damage = skills[index].calculateDamage(strength, isCrit());
                }

                switch (difficulty)
                {
                    case 0:
                        damage = damage * 2;
                        break;
                    case 1:
                        //Default difficulty
                        break;
                    case 2:
                        damage = damage / 2;
                        break;
                }
                skills[index].playSound(audioHandler);
            }
            return damage;
        }

        public void reduceCooldown(int index)
        {
            for (int i = 0; i < skills.Length; i++)
            {
                if (i != index)
                {
                    skills[i].reduceCooldown();
                }
            }
        }

        public bool isCrit()
        {
            return rand.Next(1, 101) <= dexterity;
        }

        public int getKillCount()
        {
            return killcount;
        }

        public bool takeDamage(int amount)
        {
            if (amount > 0)
            {
                if (isDodging)
                {
                    isDodging = false;
                }
                else
                {
                    if (rand.Next(1, 101) > dexterity + (intelligence / 2))
                    {
                        health = health - amount;
                        audioHandler.playDamage();
                    }
                }
            }
            return health <= 0;
        }

        public void enemyKilled()
        {
            uihandler.showMessage("SUCCESS;You killed your enemy!");
            killcount++;
            if (killcount == 30 * (difficulty + 1) && GameObject.Find("Worldgenerator").GetComponent<WorldGenerator>().gameWon())
            {
                uihandler.showMessage("SUCCESS;You won the game!");
            }
        }

        public void gainExperience(int amount)
        {
            experience = experience + amount;
            if (experience >= maxExperience)
            {
                experience = experience - maxExperience;
                maxExperience = maxExperience * 2;
                level++;
                uihandler.showMessage("SUCCESS;You gained a level!");
                points = points + 3;
                audioHandler.playLevelUp();
            }
        }

        public void updateName(Text nameUI)
        {
            nameUI.text = playername + " (" + race.racename + "/" + role.classname + ", Lvl. " + level + ")";
        }

        public void updateNameHUD(Text nameUI)
        {
            nameUI.text = playername + "\n\r" + race.racename + "/" + role.classname + "\r\n Lvl. " + level;
        }

        public void upgradeStrength()
        {
            if (points > 0)
            {
                strength++;
                points--;
            }
            else
            {
                uihandler.showMessage("ERROR;You don't have enough points!");
            }
        }

        public void upgradeDexterity()
        {
            if (points > 0)
            {
                dexterity++;
                points--;
            }
            else
            {
                uihandler.showMessage("ERROR;You don't have enough points!");
            }
        }

        public void upgradeIntelligence()
        {
            if (points > 0)
            {
                intelligence++;
                points--;
            }
            else
            {
                uihandler.showMessage("ERROR;You don't have enough points!");
            }
        }

        public void upgradeHealth()
        {
            if (points > 0)
            {
                maxHealth = maxHealth + 5;
                health = maxHealth;
                points--;
            }
            else
            {
                uihandler.showMessage("ERROR;You don't have enough points!");
            }
        }

        public void upgradeSecondary()
        {
            if (points > 0)
            {
                maxSecondary = maxSecondary + 5;
                secondary = maxSecondary;
                points--;
            }
            else
            {
                uihandler.showMessage("ERROR;You don't have enough points!");
            }
        }

        public void displaySkills(GameObject pnlSkills)
        {
            for (int i = 1; i <= 3; i++)
            {
                GameObject skill = pnlSkills.transform.Find("skill" + i).gameObject;
                skills[i-1].displaySkill(skill.transform.Find("imgAction").gameObject, skill.transform.Find("descAction").gameObject);
            }
        }
    }
}
