package engine.mobileAI.Behaviours;

import engine.Enum;
import engine.InterestManagement.InterestManager;
import engine.InterestManagement.WorldGrid;
import engine.math.Vector3fImmutable;
import engine.mobileAI.utilities.CombatUtilities;
import engine.mobileAI.utilities.MovementUtilities;
import engine.objects.*;
import engine.server.MBServerStatics;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;

public class StandardMob {

    public static void run(Mob mob){

        HashSet<AbstractWorldObject> inRange = WorldGrid.getObjectsInRangePartial(mob.loc, MBServerStatics.CHARACTER_LOAD_RANGE, MBServerStatics.MASK_PLAYER);

        if(inRange.isEmpty())
            return;

        if(!mob.isAlive()){
            CheckForRespawn(mob);
            return;
        }

        if (mob.isMoving()) {
            mob.setLoc(mob.getMovementLoc());
            mob.updateLocation();
        }

        if(mob.combatTarget == null) {
            if (!inRange.isEmpty()) {
                CheckForAggro(mob);
            }
        }else{
            CheckToDropCombatTarget(mob);
            if(mob.combatTarget == null){
                CheckForAggro(mob);
            }
        }

        if(MovementUtilities.canMove(mob))
            CheckForMovement(mob);

        if(mob.combatTarget != null && !mob.isMoving())
            CheckForAttack(mob);
    }

    public static void CheckToDropCombatTarget(Mob mob){

        if(!mob.combatTarget.isAlive()){
            mob.setCombatTarget(null);
            return;
        }

        if(mob.combatTarget.getObjectType().equals(Enum.GameObjectType.PlayerCharacter)){
            PlayerCharacter pcTarget = (PlayerCharacter) mob.combatTarget;
            if (!mob.canSee(pcTarget)) {
                mob.setCombatTarget(null);
                return;
            }
        }

        if(mob.bindLoc.distanceSquared(mob.combatTarget.loc) > 90 * 90){
            mob.setCombatTarget(null);
        }


    }

    public static void CheckForRespawn(Mob mob){
        if (mob.deathTime == 0) {
            mob.setDeathTime(System.currentTimeMillis());
            return;
        }

        if (!mob.despawned) {
            if (mob.getCharItemManager().getInventoryCount() > 0) {
                if (System.currentTimeMillis() > mob.deathTime + MBServerStatics.DESPAWN_TIMER_WITH_LOOT) {
                    mob.despawn();
                    mob.deathTime = System.currentTimeMillis();
                    return;
                }
                //No items in inventory.
            } else if (mob.isHasLoot()) {
                if (System.currentTimeMillis() > mob.deathTime + MBServerStatics.DESPAWN_TIMER_ONCE_LOOTED) {
                    mob.despawn();
                    mob.deathTime = System.currentTimeMillis();
                    return;
                }
                //Mob never had Loot.
            } else {
                if (System.currentTimeMillis() > mob.deathTime + MBServerStatics.DESPAWN_TIMER_ONCE_LOOTED) {
                    mob.despawn();
                    mob.deathTime = System.currentTimeMillis();
                    return;
                }
            }
            return;
        }

        if(Mob.discDroppers.contains(mob))
            return;
        float baseRespawnTimer = mob.spawnTime;
        float reduction = (100-mob.level) * 0.01f;
        float reducedRespawnTime = baseRespawnTimer * (1.0f - reduction);
        float respawnTimer = reducedRespawnTime * 1000f;
        if (System.currentTimeMillis() > (mob.deathTime + respawnTimer)) {
            Zone.respawnQue.add(mob);
        }
    }

    public static void CheckForAggro(Mob mob){

        if(mob == null || !mob.isAlive() || mob.playerAgroMap.isEmpty()){
            return;
        }

        if(!mob.BehaviourType.isAgressive)
            return;

        if(mob.BehaviourType.equals(Enum.MobBehaviourType.HamletGuard)){
            return;
        }

        if(mob.hate_values == null)
            mob.hate_values = new HashMap<>();

        if(mob.combatTarget != null)
            return;

        HashSet<AbstractWorldObject> inRange = WorldGrid.getObjectsInRangePartial(mob.loc, 60f, MBServerStatics.MASK_PLAYER);

        if(inRange.isEmpty()){
            mob.setCombatTarget(null);
            return;
        }

        //clear out any players who are not in hated range anymore
        ArrayList<PlayerCharacter> toRemove = new ArrayList<>();
        for(PlayerCharacter pc : mob.hate_values.keySet()){
            if(!inRange.contains(pc))
                toRemove.add(pc);
        }
        for(PlayerCharacter pc : toRemove){
            mob.hate_values.remove(pc);
        }

        //find most hated target
        PlayerCharacter mostHated = null;
        for(AbstractWorldObject awo : inRange){
            PlayerCharacter loadedPlayer = (PlayerCharacter)awo;
            if (loadedPlayer == null)
                continue;

            //Player is Dead, Mob no longer needs to attempt to aggro. Remove them from aggro map.
            if (!loadedPlayer.isAlive())
                continue;

            //Can't see target, skip aggro.
            if (!mob.canSee(loadedPlayer))
                continue;

            // No aggro for this race type
            if (mob.notEnemy != null && mob.notEnemy.size() > 0 && mob.notEnemy.contains(loadedPlayer.getRace().getRaceType().getMonsterType()))
                continue;

            //mob has enemies and this player race is not it
            if (mob.enemy != null && mob.enemy.size() > 0 && !mob.enemy.contains(loadedPlayer.getRace().getRaceType().getMonsterType()))
                continue;

            if(mostHated == null)
                mostHated = loadedPlayer;

            if(mob.hate_values.containsKey(loadedPlayer))
                if(mob.hate_values.get(loadedPlayer) > mob.hate_values.get(mostHated))
                    mostHated = loadedPlayer;
        }

        if(mostHated != null)
            mob.setCombatTarget(mostHated);
    }

    public static void CheckForMovement(Mob mob){

        if(!mob.BehaviourType.canRoam)
            return;

        if(mob.combatTarget != null){
            //chase player
            if(!CombatUtilities.inRange2D(mob,mob.combatTarget,mob.getRange())) {
                if(mob.combatTarget.getObjectType().equals(Enum.GameObjectType.PlayerCharacter)) {
                    PlayerCharacter target = (PlayerCharacter)mob.combatTarget;
                    if(target.isMoving()){
                        MovementUtilities.aiMove(mob, mob.combatTarget.loc, false);
                        return;
                    }else{
                        Vector3fImmutable smoothLoc = Vector3fImmutable.getRandomPointOnCircle(mob.combatTarget.loc,mob.getRange() -1);
                        MovementUtilities.aiMove(mob, smoothLoc, false);
                        return;
                    }
                }
                MovementUtilities.aiMove(mob, mob.combatTarget.loc, false);
            }

        }else{
            //patrol
            if (mob.isMoving()) {
                mob.stopPatrolTime = System.currentTimeMillis();
                return;
            }
            if(mob.stopPatrolTime + 5000L < System.currentTimeMillis()) {
                Vector3fImmutable patrolPoint = Vector3fImmutable.getRandomPointOnCircle(mob.bindLoc, 40f);
                MovementUtilities.aiMove(mob, patrolPoint, true);
            }
        }
    }

    public static void CheckForAttack(Mob mob){

        if(mob.isMoving())
            return;

        if(mob.getLastAttackTime() > System.currentTimeMillis())
            return;

        if (mob.BehaviourType.callsForHelp)
            MobCallForHelp(mob);

        ItemBase mainHand = mob.getWeaponItemBase(true);
        ItemBase offHand = mob.getWeaponItemBase(false);

        if(!CombatUtilities.inRangeToAttack(mob,mob.combatTarget))
            return;

        InterestManager.setObjectDirty(mob);

        if (mainHand == null && offHand == null) {
            CombatUtilities.combatCycle(mob, mob.combatTarget, true, null);
            int delay = 3000;
            mob.setLastAttackTime(System.currentTimeMillis() + delay);
        } else if (mob.getWeaponItemBase(true) != null) {
            int delay = 3000;
            CombatUtilities.combatCycle(mob, mob.combatTarget, true, mob.getWeaponItemBase(true));
            mob.setLastAttackTime(System.currentTimeMillis() + delay);
        } else if (mob.getWeaponItemBase(false) != null) {
            int attackDelay = 3000;
            CombatUtilities.combatCycle(mob, mob.combatTarget, false, mob.getWeaponItemBase(false));
            mob.setLastAttackTime(System.currentTimeMillis() + attackDelay);
        }
    }

    public static void MobCallForHelp(Mob mob){
        HashSet<AbstractWorldObject> mobs = WorldGrid.getObjectsInRangePartial(mob.loc,60f, MBServerStatics.MASK_MOB);
        for(AbstractWorldObject awo : mobs){
            Mob responder = (Mob)awo;
            if(responder.combatTarget == null)
                if(MovementUtilities.canMove(responder))
                    MovementUtilities.aiMove(responder,mob.loc,false);
        }
    }
}