package electrosphere.server.ai;

import java.util.LinkedList;
import java.util.List;

import electrosphere.entity.Entity;
import electrosphere.entity.EntityDataStrings;
import electrosphere.entity.state.movement.groundmove.ServerGroundMovementTree;
import electrosphere.entity.types.creature.CreatureUtils;
import electrosphere.game.data.creature.type.ai.AITreeData;
import electrosphere.game.data.creature.type.ai.AttackerTreeData;
import electrosphere.game.data.creature.type.ai.BlockerTreeData;
import electrosphere.logger.LoggerInterface;
import electrosphere.server.ai.blackboard.Blackboard;
import electrosphere.server.ai.nodes.AITreeNode;
import electrosphere.server.ai.trees.creature.AttackerAITree;
import electrosphere.server.ai.trees.test.BlockerAITree;

/**
 * A collection of AITrees that are attached to a single entity.
 * Maintains which 
 */
public class AI {

    /**
     * The error checking threshold for ai mutation
     */
    static final float ERROR_CHECK_ROT_THRESHOLD = 0.999f;

    /**
     * The entity this ai is associated with
     */
    Entity parent;

    /**
     * The root node of the behavior tree
     */
    AITreeNode rootNode;

    /**
     * The blackboard for the tree
     */
    Blackboard blackboard = new Blackboard();

    /**
     * Internal usage, used to track the nodes that have been evaluated and ensure we're not looping
     */
    List<AITreeNode> evaluatedNodes = new LinkedList<AITreeNode>();

    /**
     * The status of the ai
     */
    String status = "Idle";

    /**
     * Constructs an AI from a list of trees that should be present on the ai
     * @param treeData The list of data on trees to be provided
     * @return The AI
     */
    protected static AI constructAI(Entity parent, List<AITreeData> treeData){
        AI rVal = new AI(parent);

        //attach all trees
        for(AITreeData aiData : treeData){
            switch(aiData.getName()){
                case BlockerAITree.TREE_NAME: {
                    rVal.rootNode = BlockerAITree.create((BlockerTreeData) aiData);
                } break;
                case AttackerAITree.TREE_NAME: {
                    rVal.rootNode = AttackerAITree.create((AttackerTreeData) aiData);
                } break;
                default: {
                    LoggerInterface.loggerEngine.ERROR(new IllegalArgumentException("Trying to construct ai tree with undefined data type! " + aiData.getName()));
                } break;
            }
        }

        return rVal;
    }

    /**
     * Private constructor
     */
    private AI(Entity parent){
        this.parent = parent;
    }

    /**
     * Simulates a single frame of this ai. Only runs simulation logic for the highest priority tree
     */
    protected void simulate(){
        if(this.shouldExecute()){
            this.rootNode.evaluate(this.parent, this.blackboard);
        }
    }

    /**
     * Sets the ai key on the entity
     * @param entity The entity
     * @param ai The ai
     */
    public static void setAI(Entity entity, AI ai){
        entity.putData(EntityDataStrings.AI, ai);
    }

    /**
     * Gets the ai on the key of the entity
     * @param entity The entity
     * @return The AI if it exists, null otherwise
     */
    public static AI getAI(Entity entity){
        if(entity.containsKey(EntityDataStrings.AI)){
            return (AI)entity.getData(EntityDataStrings.AI);
        } else {
            return null;
        }
    }

    /**
     * Gets the parent entity of this AI
     * @return The parent entity
     */
    public Entity getParent(){
        return this.parent;
    }

    /**
     * Gets the root node of the behavior tree
     * @return The root node
     */
    public AITreeNode getRootNode(){
        return this.rootNode;
    }

    /**
     * Checks if the ai should simulate or not
     * @return true if should simulate, false otherwise
     */
    private boolean shouldExecute(){
        return !CreatureUtils.hasControllerPlayerId(this.parent);
    }

    /**
     * Sets the status of the ai
     * @param status The status
     */
    public void setStatus(String status){
        this.status = status;
    }

    /**
     * Gets the status of the ai
     * @return The status
     */
    public String getStatus(){
        return this.status;
    }

    /**
     * Resets key components
     */
    protected void resetComponents(){
        if(ServerGroundMovementTree.getServerGroundMovementTree(this.parent) != null){
            ServerGroundMovementTree.getServerGroundMovementTree(this.parent).slowdown();
        }
    }
    
}
