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

namespace RPGCreationKit.Player
{
    /// <summary>
    /// Manages the Player In Inventory screen, instantiating items and controlling the Animator
    /// </summary>
    public class ThirdPersonPlayer : MonoBehaviour
    {

        #region Singleton
        public static ThirdPersonPlayer instance;
        private void Awake()
        {
            if (!instance)
                instance = this;
        }
        #endregion
        public RuntimeAnimatorController pcMaleAnimator;
        public RuntimeAnimatorController pcFemaleAnimator;

        public GameObject PlayerModel;
        public Animator m_Animator;

        public BodyData character;

        private WeaponItem currentWeapon;
        public GameObject currentWeaponObject;
        public WeaponOnHand currentWeaponOnHand;
        public GameObject currentWeaponOnHip;

        private AmmoItem currentAmmo;
        public GameObject currentAmmoObject;


        public Animator currentWeaponAnimator;

        public Equipment equipment;

        public int combatType;
        public WeaponOnHand defaultWeaponOnHand;

        // Third Person Specific



        // Use this for initialization
        void Start()
        {

        }

        public void OnEquipmentChangesHands()
        {
            // If there is a weapon equipped
            if (equipment.itemsEquipped[(int)EquipmentSlots.RHand] != null && equipment.itemsEquipped[(int)EquipmentSlots.RHand].item != null)
            {
                // Destroy previous weapon equipped if there was a swap
                if (currentWeaponObject != null)
                {
                    Destroy(currentWeaponObject);
                    currentWeapon = null;
                }

                if(currentWeaponOnHip != null)
                    Destroy(currentWeaponOnHip);

                // Instantiate Weapon on hand, assign references
                currentWeapon = (WeaponItem)equipment.itemsEquipped[(int)EquipmentSlots.RHand].item;

                if (equipment.currentWeapon.weaponType == WeaponType.Bow)
                    currentWeaponObject = Instantiate(equipment.currentWeapon.WeaponOnHand, character.lHand);
                else
                    currentWeaponObject = Instantiate(equipment.currentWeapon.WeaponOnHand, character.rHand);

                currentWeaponOnHand = currentWeaponObject.GetComponent<WeaponOnHand>();
                currentWeaponOnHand.thisAttacker = PlayerCombat.instance.gameObject.GetComponent<IMeleeAttacker>();
                currentWeaponAnimator = currentWeaponObject.GetComponent<Animator>();

                if (currentWeaponOnHand != null)
                    currentWeaponOnHand.thisEntity = RckPlayer.GetPlayerEntity();

                if (equipment.currentWeapon.WeaponSheathed != null)
                {
                    if (equipment.currentWeapon.weaponType == WeaponType.Bow ||
                       equipment.currentWeapon.weaponType == WeaponType.BladeTwoHands ||
                       equipment.currentWeapon.weaponType == WeaponType.BluntTwoHands)
                        currentWeaponOnHip = Instantiate(equipment.currentWeapon.WeaponSheathed, character.upperChest);
                    else
                        currentWeaponOnHip = Instantiate(equipment.currentWeapon.WeaponSheathed, character.hips);
                }
                currentWeaponObject.SetActive(false);
                currentWeaponOnHip.SetActive(true);
            }
            else if (currentWeapon != null)
            {
                // Unequip = destroy previous equipped weapon
                Destroy(currentWeaponObject);
                Destroy(currentWeaponOnHip);
                currentWeapon = null;
            }


            if (RckPlayer.instance.isInThirdPerson)
                RckPlayer.instance.SwitchToThirdPerson();
            else
                RckPlayer.instance.SwitchToFirstPerson();

            UpdateAnimator();
        }

        public void OnEquipmentChangesAmmo()
        {
            if (equipment.itemsEquipped[(int)EquipmentSlots.Ammo] != null && equipment.itemsEquipped[(int)EquipmentSlots.Ammo].item != null)
            {
                if (currentAmmoObject != null && equipment.itemsEquipped[(int)EquipmentSlots.Ammo].item != currentAmmoObject)
                {
                    Destroy(currentAmmoObject);
                    currentAmmo = null;
                }

                // Instantiate Weapon on hand, assign references
                currentAmmo = (AmmoItem)equipment.itemsEquipped[(int)EquipmentSlots.Ammo].item;
                currentAmmoObject = Instantiate(currentAmmo.bagOnBody, character.upperChest);
            }
            else if (currentAmmo != null)
            {
                // Unequip = destroy previous equipped weapon
                Destroy(currentAmmoObject);
                currentAmmo = null;
            }

            if (RckPlayer.instance.isInThirdPerson)
                RckPlayer.instance.SwitchToThirdPerson();
            else
                RckPlayer.instance.SwitchToFirstPerson();
            
            UpdateAnimator();
        }

        public void ShowHideCharacter(bool show)
        {
            PlayerModel.SetActive(show);

            if (show)
                UpdateAnimator();
        }

        public void UpdateAnimator()
        {
            m_Animator.SetTrigger("ReturnToDefault");
            m_Animator.SetBool("InCombat", false);
            m_Animator.Update(0f);
        }


        public void SpawnNewPlayer(Race _race, GameObject _character, bool sex, RPGCreationKit.SaveSystem.FaceBlendshapesSaveData _faceData, int _hairType, int _eyesType, Color _SkinColor, Color _LipsColor, Color _HairColor)
        {
            GameObject cc = Instantiate(_character, transform);
            PlayerModel = cc;
            Destroy(PlayerModel.GetComponent<Animator>());

            character = PlayerModel.GetComponent<BodyData>();

            //Equipment.PlayerEquipment.characterModel = character;
            Equipment.PlayerEquipment.characterModel = character;
            // Apply face blendshapes
            BodyData bodyData = character.GetComponent<BodyData>();

            for (int i = 0; i < _faceData.allShapes.Count; i++)
                bodyData.head.SetBlendShapeWeight(_faceData.allShapes[i].index, _faceData.allShapes[i].weight);

            HeadBlendshapesManager headBlendshapes = bodyData.GetComponentInChildren<HeadBlendshapesManager>();
            headBlendshapes.AdjustChildBlendshapes();
            
            // change skin color
            Color co = _SkinColor;
            SkinnedMeshRenderer Torso = bodyData.upperbody;
            SkinnedMeshRenderer Legs = bodyData.lowerbody;
            SkinnedMeshRenderer Head = bodyData.head;
            SkinnedMeshRenderer Arms = bodyData.arms;
            SkinnedMeshRenderer Hands = bodyData.hands;
            SkinnedMeshRenderer Feet = bodyData.feet;
            SkinnedMeshRenderer Lips = bodyData.Lips;
            SkinnedMeshRenderer TempHair = bodyData.hair;

            Material[] MyTorsoMats = Torso.materials;
            Material[] MyLegsMats = Legs.materials;
            Material[] MyHeadMats = Head.materials;
            Material[] MyArmsMats = Arms.materials;
            Material[] MyHandsMats = Hands.materials;
            Material[] MyFeetMats = Feet.materials;
            Material[] MyLipsMats = Lips.materials;

            for (int i = 0; i < MyTorsoMats.Length; i++)
                MyTorsoMats[i].SetColor("_Diffuse", co);
            for (int i = 0; i < MyLegsMats.Length; i++)
                MyLegsMats[i].SetColor("_Diffuse", co);
            for (int i = 0; i < MyHeadMats.Length; i++)
                MyHeadMats[i].SetColor("_Diffuse", co);
            for (int i = 0; i < MyArmsMats.Length; i++)
                MyArmsMats[i].SetColor("_Diffuse", co);
            for (int i = 0; i < MyHandsMats.Length; i++)
                MyHandsMats[i].SetColor("_Diffuse", co);
            for (int i = 0; i < MyFeetMats.Length; i++)
                MyFeetMats[i].SetColor("_Diffuse", co);
            for (int i = 0; i < MyLipsMats.Length; i++)
                MyLipsMats[i].SetColor("_Diffuse", _LipsColor);

            // spawn hair 
            if (_hairType != -1)
            {
                // Spawn new hair
                Destroy(TempHair);
                Hair hair = (!sex) ? _race.maleHairTypes[_hairType] : _race.femaleHairTypes[_hairType];

                bodyData.hair = Instantiate(hair.mesh.gameObject, bodyData.transform).GetComponent<SkinnedMeshRenderer>();

                // Attach
                bodyData.hair.transform.parent = bodyData.head.transform;
                bodyData.hair.rootBone = bodyData.head.rootBone;
                bodyData.hair.bones = bodyData.head.bones;
            }
            
            // change hair color
            SkinnedMeshRenderer Hair = bodyData.hair;
            Material[] MyHairMats = Hair.materials;
            for (int i = 0; i < MyHairMats.Length; i++)
                MyHairMats[i].SetColor("_Diffuse", _HairColor);

            // edit eyes
            Eye eyesType = _race.eyeTypes[_eyesType];

            bodyData.eyes.sharedMaterial = eyesType.eyes.sharedMaterial;

            // Disable AI HeadPos on the Model
            character.GetComponentInChildren<AIHeadPos>().gameObject.SetActive(false);

            m_Animator.enabled = false;
            m_Animator.Update(Time.deltaTime);
            m_Animator.enabled = true;
            m_Animator.applyRootMotion = false;

            m_Animator.runtimeAnimatorController = (sex == false) ? pcMaleAnimator : pcFemaleAnimator;
            m_Animator.enabled = true;

            PlayerCombat.instance.tpsAnim = m_Animator;
            ThirdPersonPlayer.instance.currentWeaponOnHand = defaultWeaponOnHand;

            character.GetComponent<Ragdoll>().animator = m_Animator;

            m_Animator.Rebind();
            m_Animator.Update(0f);
        }

        public void ApplyFaceBlenshapes()
        {

        }

        public void DrawWeaponAnimationEvent()
        {
            if(currentWeaponOnHand == null || currentWeaponOnHand == null)
            {
                UpdateAnimator();
                return;
            }
            currentWeaponOnHip.SetActive(false);
            currentWeaponOnHand.gameObject.SetActive(true);
        }

        public void UndrawWeaponAnimationEvent()
        {
            if (currentWeaponOnHand == null || currentWeaponOnHand == null)
            {
                UpdateAnimator();
                return;
            }

            currentWeaponOnHand.gameObject.SetActive(false);
            currentWeaponOnHip.SetActive(true);
        }

        public void UndrawWeaponEvent()
        {
            //m_Anim.runtimeAnimatorController = defaultAnimatorController;
            //m_Animator.SetBool("InCombat", false);
        }

        /// <summary>
        /// Called by animation event, resets the animator runtime controller to default
        /// </summary>
        public void DrawWeaponEvent()
        {
            //m_Anim.runtimeAnimatorController = defaultAnimatorController;
            //m_Animator.SetBool("InCombat", true);
        }

        // Start is called before the first frame update
        public void OnAnimatorIK(int layerIndex)
        {
            m_Animator.SetLookAtPosition(RckPlayer.instance.cameraAnim.tpsLookAtTarget.position);
            m_Animator.SetLookAtWeight(1, 0.5f, 1, 1, 1);
        }

        public void AttackAnimationEvent()
        {
            currentWeaponOnHand.StartCasting(false, PlayerCombat.instance.lastAttackIndex);
        }

        public void ChargedAttackAnimationEvent()
        {
            currentWeaponOnHand.StartCasting(true, PlayerCombat.instance.lastChargedAttackIndex);
        }

        public void EndAttackAnimationEvent()
        {
            currentWeaponOnHand.StopCasting();
        }

        public void PlayAttackSound()
        {
            if(RckPlayer.instance.isInThirdPerson)
                currentWeaponOnHand.PlayOneShot(currentWeaponOnHand.weaponItem.weaponAttacks[PlayerCombat.instance.curAttackType].attackSound);
        }

        public void PlayChargedAttackSound()
        {
            if (RckPlayer.instance.isInThirdPerson)
                currentWeaponOnHand.PlayOneShot(currentWeaponOnHand.weaponItem.weaponChargedAttacks[PlayerCombat.instance.curChargedAttackType].attackSound);
        }
        
        // Anim events are driven by FP only
        public void ResetAttackStateEvent()
        {
            /*
            if(Player.RckPlayer.instance.isInThirdPerson)
            {
                PlayerCombat.instance.isAttacking = false;
                PlayerCombat.instance.canAttack = true;
            }
            */
        }

        public void BlockAnimationEvent()
        {
            /*
            if (Player.RckPlayer.instance.isInThirdPerson)
            {
                PlayerCombat.instance.isBlocking = true;
            }
            */
        }

        [HideInInspector] public GameObject projectile;
        public void SpawnAmmoOnRHand()
        {
            projectile = Instantiate(((AmmoItem)equipment.itemsEquipped[(int)EquipmentSlots.Ammo].item).projectile, character.rHand);

            if(!RckPlayer.instance.isInThirdPerson)
            {
                if (projectile.GetComponent<MeshRenderer>() != null)
                    projectile.GetComponent<MeshRenderer>().shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.ShadowsOnly;
            }
        }

        public void OnAmmoIsReady()
        {

        }

        public void DestroyAmmoProjectile()
        {
            if(projectile != null)
                Destroy(projectile);

            projectile = null;
        }

        public void PlayCastSpellSound()
        {
            /*
            if (!RPGCreationKit.Player.RckPlayer.instance.isInThirdPerson && SpellsKnowledge.Player.spellInUse.sOnCast)
                playerCombat.spellsAudioSource.PlayOneShot(SpellsKnowledge.Player.spellInUse.sOnCast);
            */
        }

        GameObject spellProjectile;
        SpellProjectile currentSpellProjectile;

        public void CastSpellInit()
        {
            Spell curSpell = SpellsKnowledge.Player.spellInUse;

            Transform handT = (Equipment.PlayerEquipment.isUsingShield || (Equipment.PlayerEquipment.currentWeapon != null && Equipment.PlayerEquipment.currentWeapon.weaponType == WeaponType.Bow)) ? character.rHand : character.lHand;

            switch (curSpell.mode)
            {
                case SpellModes.Projectile:
                    spellProjectile = Instantiate(curSpell.magicProjectile, handT);
                    spellProjectile.SetLayer(RCKLayers.ThirdPersonOnly, true);
                    currentSpellProjectile = spellProjectile.GetComponent<SpellProjectile>();
                    break;

                case SpellModes.Touch:
                    spellProjectile = Instantiate(curSpell.magicProjectile, handT);
                    spellProjectile.SetLayer(RCKLayers.ThirdPersonOnly, true);
                    currentSpellProjectile = spellProjectile.GetComponent<SpellProjectile>();
                    currentSpellProjectile.Init(Entity.GetPlayerEntity(), true);
                    break;

                case SpellModes.Self:
                    spellProjectile = Instantiate(curSpell.magicProjectile, handT);
                    spellProjectile.SetLayer(RCKLayers.ThirdPersonOnly, true);
                    currentSpellProjectile = spellProjectile.GetComponent<SpellProjectile>();
                    currentSpellProjectile.Init(Entity.GetPlayerEntity(), true);
                    break;
            }
        }

        public void CastSpellAttackEvent()
        {
            // Instantiate/Do Stuff
            Spell curSpell = SpellsKnowledge.Player.spellInUse;
            Ray ray;

            // From the shooting everything else is managed by he FP shot projectile
            Destroy(currentSpellProjectile.gameObject);
        }

        public void CastSpellEndAndDestroy()
        {

        }

        public void CastSpellResetEvent()
        {
            /*
            playerCombat.ResetAttackStateEvent();
            playerCombat.isAttacking = false;
            playerCombat.isCastingSpell = false;
            */
        }
    }
}