package electrosphere.server.ai;

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

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

import electrosphere.entity.Entity;
import electrosphere.entity.EntityDataStrings;
import electrosphere.entity.EntityUtils;
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.creature.Attacker;
import electrosphere.server.ai.creature.Blocker;

/**
 * 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 list of trees for this ai in particular
     */
    List<AITree> trees = new LinkedList<AITree>();

    /**
     * The currently active tree
     */
    AITree currentTree = null;

    /**
     * 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 Attacker.TREE_NAME:
                    rVal.trees.add(Attacker.construct(parent, (AttackerTreeData) aiData));
                break;
                case Blocker.TREE_NAME: {
                    rVal.trees.add(Blocker.construct(parent, (BlockerTreeData) 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(){
        //
        //Check basic values of entity to make sure the ai trees aren't changing state when updating state and priority
        Vector3d currentPos = new Vector3d(EntityUtils.getPosition(parent));
        Quaterniond currentRot = new Quaterniond(EntityUtils.getRotation(parent));


        //
        //Update state and find the highest priority tree
        for(AITree tree : trees){
            tree.updateStateAndPriority();
            if(currentTree == null){
                currentTree = tree;
            } else if(currentTree.getCurrentStatePriority() < tree.getCurrentStatePriority()){
                currentTree = tree;
            }
        }

        //
        //Actual check to see if state has changed (throw error if it does)
        Vector3d postUpdatePos = EntityUtils.getPosition(parent);
        Quaterniond postUpdateRot = EntityUtils.getRotation(parent);
        if(postUpdatePos.distance(currentPos) > 0){
            String message = "An AI tree changed entity state while it was updating its priority!\n" + 
            "This is an illegal operation!\n" +
            "All entity updates should be in the simulate method!" +
            "(The position changed)";
            LoggerInterface.loggerEngine.ERROR(new IllegalStateException(message));
        }
        if(postUpdateRot.dot(currentRot) < ERROR_CHECK_ROT_THRESHOLD){
            String message = "An AI tree changed entity state while it was updating its priority!\n" + 
            "This is an illegal operation!\n" +
            "All entity updates should be in the simulate method!" +
            "(The rotation changed) " + postUpdateRot.dot(currentRot);
            LoggerInterface.loggerEngine.ERROR(new IllegalStateException(message));
        }


        //
        //Simulate the highest priority tree
        if(currentTree != null && !CreatureUtils.hasControllerPlayerId(this.parent)){
            currentTree.simulate();
        }
    }

    /**
     * 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 current highest priority tree
     * @return The highest priority tree
     */
    public AITree getCurrentTree(){
        return this.currentTree;
    }
    
}
