package electrosphere.server.ai.creature;

import electrosphere.engine.Globals;
import electrosphere.entity.Entity;
import electrosphere.entity.EntityTags;
import electrosphere.entity.EntityUtils;
import electrosphere.entity.state.attack.ServerAttackTree;
import electrosphere.entity.state.equip.ClientEquipState;
import electrosphere.entity.state.movement.groundmove.ClientGroundMovementTree;
import electrosphere.entity.state.movement.groundmove.ClientGroundMovementTree.MovementRelativeFacing;
import electrosphere.entity.types.creature.CreatureUtils;
import electrosphere.entity.types.item.ItemUtils;
import electrosphere.server.ai.AITree;
import electrosphere.server.datacell.Realm;
import electrosphere.server.datacell.utils.DataCellSearchUtils;
import electrosphere.util.math.MathUtils;

import org.joml.Quaterniond;
import org.joml.Vector3d;

/**
 * An ai whose goal is to try to kill enemies; however, if it has no access to a weapon it will try to run away
 */
public class OpportunisticAttacker implements AITree {
    
    /**
     * The parent of this ai tree
     */
    Entity parent;
    
    /**
     * The target for this ai
     */
    Entity target;
    
    float aggroRange = 10.0f;
    float attackRange = 1.0f;
    
    float weaponSeekRange = 10.0f;
    float pickupRange = 2.0f;
    
    int attackCooldownMax = 250;
    int attackCooldown = 0;
    
    enum OpportunisticAttackerGoal {
        ATTACK,
        SEEK_WEAPON,
        FLEE,
        IDLE,
    }
    
    OpportunisticAttackerGoal goal;

    @Override
    public String getTreeName(){
        return "OpportunisticAttacker";
    }
    
    OpportunisticAttacker(Entity character){
        this.parent = character;
    }
    
    public static void attachToCreature(Entity creature){
        OpportunisticAttacker ai = new OpportunisticAttacker(creature);
        ai.goal = OpportunisticAttackerGoal.IDLE;
    }
    
    
    
    @Override
    public void simulate(){
        switch(goal){
            case ATTACK:
                if(target != null){
                    if(inAttackRange()){
                        if(hasWeapon()){
                            faceTarget();
                            attack();
                        } else {
                            if(weaponInRange()){
                                setGoal(OpportunisticAttackerGoal.SEEK_WEAPON);
                            } else {
                                setGoal(OpportunisticAttackerGoal.IDLE);
                            }
                        }
                    } else {
                        if(inAggroRange()){
                            if(hasWeapon()){
                                faceTarget();
                                moveToTarget();
                            } else {
                                if(weaponInRange()){
                                    setGoal(OpportunisticAttackerGoal.SEEK_WEAPON);
                                } else {
                                    setGoal(OpportunisticAttackerGoal.FLEE);
                                }
                            }
                        } else {
                            target = null;
                            setGoal(OpportunisticAttackerGoal.IDLE);
                        }
                    }
                } else {
                    setGoal(OpportunisticAttackerGoal.IDLE);
                }
                break;
            case SEEK_WEAPON:
                if(target != null){
                    if(weaponInRange()){
                        if(weaponInPickupRange()){
                            pickupWeapon();
                            searchForTarget();
                            if(target != null){
                                setGoal(OpportunisticAttackerGoal.ATTACK);
                            }
                        } else {
                            moveToTarget();
                        }
                    } else {
                        setGoal(OpportunisticAttackerGoal.FLEE);
                    }
                } else {
                    if(inAggroRange()){
                        if(hasWeapon()){
                            moveToTarget();
                        } else {
                            if(!weaponInRange()){
                                setGoal(OpportunisticAttackerGoal.FLEE);
                            }
                        }
                    } else {
                        target = null;
                        setGoal(OpportunisticAttackerGoal.IDLE);
                    }
                }
                break;
            case FLEE:
                if(target != null){
                    if(weaponInRange()){
                        setGoal(OpportunisticAttackerGoal.SEEK_WEAPON);
                    } else {
                        if(inAggroRange()){
                            //find direction opposite target and move there
                        } else {
                            target = null;
                            setGoal(OpportunisticAttackerGoal.IDLE);
                        }
                    }
                } else {
                    setGoal(OpportunisticAttackerGoal.IDLE);
                }
                break;
            case IDLE:
                searchForTarget();
                break;
        }
        
    }
    
    void attack(){
        if(attackCooldown == 0){
            attackCooldown = attackCooldownMax;
            ServerAttackTree attackTree = CreatureUtils.serverGetAttackTree(parent);
            attackTree.start();
        } else {
            attackCooldown--;
        }
    }
    
    void moveToTarget(){
        Vector3d targetPosition = EntityUtils.getPosition(target);
        Vector3d characterPosition = EntityUtils.getPosition(parent);
        Vector3d moveVector = new Vector3d(targetPosition).sub(characterPosition).normalize();
        CreatureUtils.setFacingVector(parent, new Vector3d((float)moveVector.x,(float)moveVector.y,(float)moveVector.z));
        ClientGroundMovementTree characterMoveTree = (ClientGroundMovementTree)CreatureUtils.serverGetEntityMovementTree(parent);
        if(characterMoveTree.getState()==ClientGroundMovementTree.MovementTreeState.IDLE || characterMoveTree.getState()==ClientGroundMovementTree.MovementTreeState.SLOWDOWN){
            characterMoveTree.start(MovementRelativeFacing.FORWARD);
        }
    }
    
    boolean inAttackRange(){
        boolean rVal = false;
        Vector3d position = EntityUtils.getPosition(parent);
        Vector3d targetPosition = EntityUtils.getPosition(target);
        if(new Vector3d(position).distance(targetPosition) < attackRange){
            rVal = true;
        }
        return rVal;
    }
    
    boolean inAggroRange(){
        boolean rVal = false;
        Vector3d position = EntityUtils.getPosition(parent);
        Vector3d targetPosition = EntityUtils.getPosition(target);
        if(new Vector3d(position).distance(targetPosition) < aggroRange){
            rVal = true;
        }
        return rVal;
    }
    
    
    void searchForTarget(){
        Vector3d position = EntityUtils.getPosition(parent);
        Realm realm = Globals.realmManager.getEntityRealm(parent);
        for(Entity current : DataCellSearchUtils.getEntitiesWithTagAroundLocation(realm,position,EntityTags.LIFE_STATE)){
            if(current != parent){
                Vector3d potentialTargetPosition = EntityUtils.getPosition(current);
                if(position.distance(potentialTargetPosition) < aggroRange){
                    target = current;
                    setGoal(OpportunisticAttackerGoal.ATTACK);
                    break;
                }
            }
        }
    }
    
    boolean hasWeapon(){
        boolean rVal = false;
        if(ClientEquipState.hasEquipState(parent)){
            //TODO:fix hasWeapon
            ClientEquipState equipState = ClientEquipState.getEquipState(parent);
            // if(equipState.hasEquipPrimary()){
            //     rVal = true;
            // }
        }
        return rVal;
    }
    
    boolean weaponInRange(){
        boolean rVal = false;
        Vector3d position = EntityUtils.getPosition(parent);
        Realm realm = Globals.realmManager.getEntityRealm(parent);
        for(Entity current : DataCellSearchUtils.getEntitiesWithTagAroundLocation(realm,position,EntityTags.ITEM)){
            if(current != parent && ItemUtils.isItem(current) && ItemUtils.isWeapon(current)){
                Vector3d potentialTargetPosition = EntityUtils.getPosition(current);
                if(position.distance(potentialTargetPosition) < weaponSeekRange){
                    target = current;
                    return true;
                }
            }
        }
        return rVal;
    }
    
    boolean weaponInPickupRange(){
        Vector3d position = EntityUtils.getPosition(parent);
        if(target != null){
            Vector3d potentialTargetPosition = EntityUtils.getPosition(target);
            if(position.distance(potentialTargetPosition) < pickupRange){
                    return true;
                }
        }
        return false;
    }
    
    void pickupWeapon(){
        if(ClientEquipState.hasEquipState(parent)){
            ClientEquipState equipState = ClientEquipState.getEquipState(parent);
            // if(!equipState.hasEquipPrimary()){
            //     equipState.attemptEquip(target);
            // }
        }
    }
    
    void faceTarget(){
        Vector3d position = EntityUtils.getPosition(parent);
        Vector3d targetPosition = EntityUtils.getPosition(target);
        Vector3d movementVector = new Vector3d(targetPosition).sub(position).normalize();
        Quaterniond movementQuaternion = new Quaterniond().rotationTo(MathUtils.getOriginVector(), new Vector3d(movementVector.x,0,movementVector.z)).normalize();
        CreatureUtils.setFacingVector(parent, movementVector);
        EntityUtils.getRotation(parent).set(movementQuaternion);
    }
    
    void setGoal(OpportunisticAttackerGoal goal){
        this.goal = goal;
        switch(goal){
            case IDLE:
//                System.out.println("Set goal idle");
                break;
            case FLEE:
//                System.out.println("Set goal flee");
                break;
            case SEEK_WEAPON:
//                System.out.println("Set goal seek weapon");
                break;
            case ATTACK:
//                System.out.println("Set goal attack");
                break;
        }
    }

    public static AITree construct(Entity parent) {
        // TODO Auto-generated method stub
        throw new UnsupportedOperationException("Unimplemented method 'construct'");
    }

    @Override
    public void updateStateAndPriority() {
        // TODO Auto-generated method stub
        throw new UnsupportedOperationException("Unimplemented method 'updateStateAndPriority'");
    }

    @Override
    public String getCurrentStateName() {
        // TODO Auto-generated method stub
        throw new UnsupportedOperationException("Unimplemented method 'getCurrentStateName'");
    }

    @Override
    public int getCurrentStatePriority() {
        // TODO Auto-generated method stub
        throw new UnsupportedOperationException("Unimplemented method 'getCurrentStatePriority'");
    }
    
}
