using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using FFXIVClassic.Common;
using FFXIVClassic_Map_Server.Actors;
using FFXIVClassic_Map_Server.actors.chara.npc;
using FFXIVClassic_Map_Server.actors;
using FFXIVClassic_Map_Server.actors.chara;
using FFXIVClassic_Map_Server.actors.chara.ai;
using FFXIVClassic_Map_Server.actors.chara.ai.controllers;
using FFXIVClassic_Map_Server.packets.send.actor;
using FFXIVClassic_Map_Server.actors.chara.ai.state;
namespace FFXIVClassic_Map_Server.Actors
{
    [Flags]
    enum AggroType
    {
        None = 0x00,
        Sight = 0x01,
        Scent = 0x02,
        LowHp = 0x04,
        IgnoreLevelDifference = 0x08
    }
    class BattleNpc : Npc
    {
        public HateContainer hateContainer;
        public AggroType aggroType;
        public bool neutral;
        private uint despawnTime;
        private uint spawnDistance;
        private float spawnX, spawnY, spawnZ;
        public BattleNpc(int actorNumber, ActorClass actorClass, string uniqueId, Area spawnedArea, float posX, float posY, float posZ, float rot,
            ushort actorState, uint animationId, string customDisplayName)
            : base(actorNumber, actorClass, uniqueId, spawnedArea, posX, posY, posZ, rot, actorState, animationId, customDisplayName)  
        {
            this.aiContainer = new AIContainer(this, new BattleNpcController(this), new PathFind(this), new TargetFind(this));
            this.currentSubState = SetActorStatePacket.SUB_STATE_MONSTER;
            //this.currentMainState = SetActorStatePacket.MAIN_STATE_ACTIVE;
            //charaWork.property[2] = 1;
            //npcWork.hateType = 1;
            this.hateContainer = new HateContainer(this);
            this.allegiance = CharacterTargetingAllegiance.BattleNpcs;
            spawnX = posX;
            spawnY = posY;
            spawnZ = posZ;
            // todo: read this from db
            aggroType = AggroType.Sight;
            this.moveState = 2;
            ResetMoveSpeeds();
            this.meleeRange = 1.5f;
            despawnTime = 10;
        }
        public override void Update(DateTime tick)
        {
            this.aiContainer.Update(tick);
            this.statusEffects.Update(tick);
        }
        public override bool CanAttack()
        {
            
            return true;
        }
        /// // todo: create an action object? 
        public bool OnAttack(AttackState state)
        {
            return false;
        }
        public override void Spawn(DateTime tick)
        {
            base.Spawn(tick);
            this.isMovingToSpawn = false;
            this.ResetMoveSpeeds();
            this.ChangeState(SetActorStatePacket.MAIN_STATE_PASSIVE);
        }
        public override void Die(DateTime tick)
        {
            if (IsAlive())
            {
                aiContainer.InternalDie(tick, despawnTime);
                this.ResetMoveSpeeds();
                this.positionX = oldPositionX;
                this.positionY = oldPositionY;
                this.positionZ = oldPositionZ;
                this.isAtSpawn = true;
            }
            else
            {
                var err = $"[{actorId}][{customDisplayName}] {positionX} {positionY} {positionZ} {GetZoneID()} tried to die ded";
                Program.Log.Error(err);
                //throw new Exception(err);
            }
        }
        public void OnRoam(DateTime tick)
        {
            // todo: move this to battlenpccontroller..
            bool foundActor = false;
            // leash back to spawn
            if (!IsCloseToSpawn())
            {
                isMovingToSpawn = true;
                aiContainer.Reset();
            }
            else
            {
                this.isMovingToSpawn = false;
            }
            // dont bother checking for any in-range players if going back to spawn
            if (!this.isMovingToSpawn && this.aggroType != AggroType.None)
            {
                foreach (var player in zone.GetActorsAroundActor(this, 50))
                {
                    uint levelDifference = (uint)Math.Abs(this.charaWork.parameterSave.state_mainSkillLevel - player.charaWork.parameterSave.state_mainSkillLevel);
                    if (levelDifference < 10 && ((BattleNpcController)aiContainer.GetController()).CanAggroTarget(player))
                        hateContainer.AddBaseHate(player);
                }
            }
            if (target == null)
                aiContainer.pathFind.PathInRange(spawnX, spawnY, spawnZ, 1.0f, 35.0f);
        }
        public uint GetDespawnTime()
        {
            return despawnTime;
        }
        public void SetDespawnTime(uint seconds)
        {
            despawnTime = seconds;
        }
        
        public bool IsCloseToSpawn()
        {
            return this.isAtSpawn = Utils.DistanceSquared(positionX, positionY, positionZ, spawnX, spawnY, spawnZ) <= 2500.0f;
        }
    }
}