package electrosphere.server.ai.creature;

import java.util.Random;

import org.joml.Vector3d;

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.block.ServerBlockTree;
import electrosphere.entity.state.movement.groundmove.ClientGroundMovementTree;
import electrosphere.entity.state.movement.groundmove.ServerGroundMovementTree;
import electrosphere.entity.state.movement.groundmove.ClientGroundMovementTree.MovementRelativeFacing;
import electrosphere.entity.types.creature.CreatureUtils;
import electrosphere.game.data.creature.type.ai.AttackerTreeData;
import electrosphere.logger.LoggerInterface;
import electrosphere.server.ai.AITree;
import electrosphere.server.datacell.Realm;
import electrosphere.server.datacell.utils.DataCellSearchUtils;

/**
 * Keeps track of targets and instructs the ai to attack
 */
public class Attacker implements AITree {

    /**
     * The name of the tree type
     */
    public static final String TREE_NAME = "Attacker";

    /**
     * Different states available to the Attacker tree
     */
    public enum AttackerState {

        /**
         * Not doing anything
         */
        IDLE,

        /**
         * Trying to close the distance between itself and its target
         */
        MOVING_INTO_ATTACKING_RANGE,

        /**
         * Moving around the target. If the target is a player, this is intended to give the player openings to attack during
         */
        DANCING,

        /**
         * Actually going in for an attack
         */
        ATTACKING,
    }

    /**
     * The configuring tree data for this tree
     */
    AttackerTreeData treeData;


    /**
     * The current state of this instance of this tree
     */
    AttackerState state = AttackerState.IDLE;

    /**
     * The parent to this tree
     */
    Entity parent;

    /**
     * The current target of this tree
     */
    Entity target = null;


    /**
     * The number of frames the tree has been in its current state
     */
    int currentStateFrameCount = 0;

    /**
     * The random used for rolls on behavior transitions
     */
    Random random = new Random();




    @Override
    public String getTreeName(){
        return TREE_NAME;
    }

    /**
     * Constructor
     * @param parent The parent to this tree
     */
    private Attacker(Entity parent){
        this.parent = parent;
    }

    /**
     * Constructs an attacker tree
     * @param parent The parent entity
     * @param treeData The tree data
     * @return The Attacker tree
     */
    public static AITree construct(Entity parent, AttackerTreeData treeData) {
        Attacker rVal = new Attacker(parent);
        rVal.treeData = treeData;
        return rVal;
    }

    @Override
    public void simulate() {
        switch(state){
            case IDLE: {
                ServerBlockTree serverBlockTree = ServerBlockTree.getServerBlockTree(parent);
                if(serverBlockTree.isBlocking()){
                    serverBlockTree.stop();
                }
            } break;
            case MOVING_INTO_ATTACKING_RANGE: {
                //make sure we're blocking
                ServerBlockTree serverBlockTree = ServerBlockTree.getServerBlockTree(parent);
                if(!serverBlockTree.isBlocking()){
                    serverBlockTree.start();
                }
                //close distance
                moveToTarget();
            } break;
            case DANCING: {
                ServerBlockTree serverBlockTree = ServerBlockTree.getServerBlockTree(parent);
                if(!serverBlockTree.isBlocking()){
                    serverBlockTree.start();
                }
                //TODO: move parallel
            } break;
            case ATTACKING: {
                ServerBlockTree serverBlockTree = ServerBlockTree.getServerBlockTree(parent);
                ServerAttackTree serverAttackTree = ServerAttackTree.getServerAttackTree(parent);
                if(serverBlockTree.isBlocking()){
                    serverBlockTree.stop();
                } else if(serverAttackTree.isAttacking()){

                } else {
                    serverAttackTree.start();
                }
            } break;
        }
    }

    @Override
    public void updateStateAndPriority() {
        switch(state){
            case IDLE: {
                if(target != null){
                    setState(AttackerState.MOVING_INTO_ATTACKING_RANGE);
                } else {
                    searchForTarget();
                }
            } break;
            case MOVING_INTO_ATTACKING_RANGE: {
                if(inAttackRange()){
                    //should change to dancing or attacking
                    if(this.random.nextInt(2) > 0){
                        setState(AttackerState.ATTACKING);
                    } else {
                        setState(AttackerState.DANCING);
                    }
                } else if(
                    inAggroRange() &&
                    !inAttackRange() &&
                    currentStateFrameCount > this.treeData.getStateChangeTimeout()
                ){
                    //should change to dancing or moving into attack range
                    if(this.random.nextInt(2) > 0){
                        setState(AttackerState.MOVING_INTO_ATTACKING_RANGE);
                    } else {
                        setState(AttackerState.DANCING);
                    }
                } else if(!inAggroRange()) {
                    this.target = null;
                    setState(AttackerState.IDLE);
                }
            } break;
            case DANCING: {
                if(inAttackRange()){
                    //should change to dancing or attacking
                    if(this.random.nextInt(2) > 0){
                        setState(AttackerState.ATTACKING);
                    } else {
                        setState(AttackerState.DANCING);
                    }
                } else if(
                    inAggroRange() &&
                    !inAttackRange() &&
                    currentStateFrameCount > this.treeData.getStateChangeTimeout()
                ){
                    //should change to dancing or moving into attack range
                    if(this.random.nextInt(2) > 0){
                        setState(AttackerState.MOVING_INTO_ATTACKING_RANGE);
                    } else {
                        setState(AttackerState.DANCING);
                    }
                } else if(!inAggroRange()) {
                    this.target = null;
                    setState(AttackerState.IDLE);
                }
            } break;
            case ATTACKING: {
                if(inAttackRange()){
                    //should change to dancing or attacking
                    if(this.random.nextInt(2) > 0){
                        setState(AttackerState.ATTACKING);
                    } else {
                        setState(AttackerState.DANCING);
                    }
                } else if(
                    inAggroRange() &&
                    !inAttackRange() &&
                    currentStateFrameCount > this.treeData.getStateChangeTimeout()
                ){
                    //should change to dancing or moving into attack range
                    if(this.random.nextInt(2) > 0){
                        setState(AttackerState.MOVING_INTO_ATTACKING_RANGE);
                    } else {
                        setState(AttackerState.DANCING);
                    }
                } else if(!inAggroRange()) {
                    this.target = null;
                    setState(AttackerState.IDLE);
                }
            } break;
        }
        currentStateFrameCount++;
    }

    @Override
    public String getCurrentStateName() {
        switch(state){
            case IDLE:
                return "IDLE";
            case MOVING_INTO_ATTACKING_RANGE:
                return "MOVING INTO ATTACKING RANGE";
            case DANCING:
                return "DANCING";
            case ATTACKING:
                return "ATTACKING";
        }
        LoggerInterface.loggerEngine.ERROR(new IllegalStateException("Unaccounted for state name in ai tree"));
        return "";
    }

    @Override
    public int getCurrentStatePriority() {
        switch(state){
            case IDLE:
                return 0;
            case MOVING_INTO_ATTACKING_RANGE:
                return 3;
            case DANCING:
                return 2;
            case ATTACKING:
                return 8;
        }
        LoggerInterface.loggerEngine.ERROR(new IllegalStateException("Unaccounted for state name in ai tree"));
        return 0;
    }

    /**
     * Sets the state of the tree
     * @param state The state
     */
    private void setState(AttackerState state){
        this.state = state;
        this.currentStateFrameCount = 0;
    }

    /**
     * Searches for a valid target
     */
    private 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) < treeData.getAggroRange()){
                    target = current;
                    break;
                }
            }
        }
    }

    /**
     * Checks if the target is within the aggression range
     */
    private boolean inAggroRange(){
        boolean rVal = false;
        Vector3d position = EntityUtils.getPosition(parent);
        Vector3d targetPosition = EntityUtils.getPosition(target);
        if(new Vector3d(position).distance(targetPosition) < treeData.getAggroRange()){
            rVal = true;
        }
        return rVal;
    }

    /**
     * Checks if the target is within range of an attack animation
     * @return
     */
    private boolean inAttackRange(){
        boolean rVal = false;
        Vector3d position = EntityUtils.getPosition(parent);
        Vector3d targetPosition = EntityUtils.getPosition(target);
        if(new Vector3d(position).distance(targetPosition) < treeData.getAttackRange()){
            rVal = true;
        }
        return rVal;
    }

    /**
     * Attempts to move towards the target
     */
    private 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));
        ServerGroundMovementTree characterMoveTree = (ServerGroundMovementTree)CreatureUtils.serverGetEntityMovementTree(parent);
        if(characterMoveTree.getState()==ClientGroundMovementTree.MovementTreeState.IDLE || characterMoveTree.getState()==ClientGroundMovementTree.MovementTreeState.SLOWDOWN){
            characterMoveTree.start(MovementRelativeFacing.FORWARD);
        }
    }


}
