package electrosphere.entity.types.creature;

import electrosphere.collision.dispatch.CollisionObject;
import electrosphere.dynamics.RigidBody;
import electrosphere.entity.Entity;
import electrosphere.entity.EntityDataStrings;
import electrosphere.entity.EntityUtils;
import electrosphere.entity.state.movement.GroundMovementTree;
import electrosphere.entity.types.hitbox.HitboxData;
import electrosphere.entity.types.hitbox.HitboxUtils;
import electrosphere.game.data.creature.type.CreatureType;
import electrosphere.game.data.creature.type.MovementSystem;
import electrosphere.entity.state.AttackTree;
import electrosphere.entity.state.GravityTree;
import electrosphere.entity.state.IdleTree;
import electrosphere.entity.state.collidable.CollidableTree;
import electrosphere.entity.types.collision.CollisionObjUtils;
import electrosphere.entity.state.life.LifeState;
import electrosphere.game.collision.PhysicsUtils;
import electrosphere.game.collision.collidable.Collidable;
import electrosphere.game.data.creature.type.AttackMove;
import electrosphere.game.data.creature.type.CollidableTemplate;
import electrosphere.logger.LoggerInterface;
import electrosphere.main.Globals;
import electrosphere.main.Main;
import electrosphere.net.NetUtils;
import electrosphere.net.parser.net.message.EntityMessage;
import electrosphere.net.parser.net.message.NetworkMessage;
import electrosphere.net.server.Player;
import electrosphere.renderer.Actor;
import electrosphere.renderer.ActorUtils;
import electrosphere.renderer.Model;
import electrosphere.util.ModelLoader;
import org.joml.Quaternionf;
import org.joml.Vector3d;
import org.joml.Vector3f;

/**
 *
 * @author amaterasu
 */
public class CreatureUtils {
//    public static Entity spawnBasicCreature(int creatureId, float acceleration, float maxVelocity){
//        Entity rVal = new Entity();
//        rVal.putData(EntityDataStrings.DATA_STRING_CREATURE_IS_CREATURE, true);
//        rVal.putData(EntityDataStrings.DATA_STRING_MODEL_PATH, Globals.entityTypeMap.get(creatureId).getModelPath());
//        Globals.assetManager.addModelPathToQueue(Globals.entityTypeMap.get(creatureId).getModelPath());
//        rVal.putData(EntityDataStrings.DATA_STRING_CREATURE_TYPE, creatureId);
//        rVal.putData(EntityDataStrings.DATA_STRING_POSITION, new Vector3f(0,0,0));
//        rVal.putData(EntityDataStrings.DATA_STRING_ROTATION, new Quaternionf().rotateAxis((float)0, new Vector3f(1,0,0)));
//        rVal.putData(EntityDataStrings.DATA_STRING_SCALE, new Vector3f(1,1,1));
//        rVal.putData(EntityDataStrings.DATA_STRING_MOVEMENT_BT, new MovementTree(rVal));
//        rVal.putData(EntityDataStrings.DATA_STRING_MOVEMENT_VECTOR, new Vector3f(0,0,0));
//        rVal.putData(EntityDataStrings.DATA_STRING_MAX_NATURAL_VELOCITY, maxVelocity);
//        rVal.putData(EntityDataStrings.DATA_STRING_ACCELERATION, acceleration);
//        rVal.putData(EntityDataStrings.DATA_STRING_VELOCITY, 0f);
//        Globals.entityManager.registerEntity(rVal);
//        Globals.entityManager.registerDrawableEntity(rVal);
//        Globals.entityManager.registerMoveableEntity(rVal);
//        return rVal;
//    }
    
    public static Entity spawnBasicCreature(String type){
        CreatureType rawType = Globals.gameConfigCurrent.getCreatureMap().getCreature(type);
        Entity rVal = EntityUtils.spawnDrawableEntity(rawType.getModelPath());
        for(HitboxData hitboxdata : rawType.getHitboxes()){
            if(hitboxdata.getType().equals("hit")){
                Globals.hitboxManager.registerHitbox(HitboxUtils.spawnRegularHitbox(rVal, hitboxdata.getBone(), hitboxdata.getRadius()));
            } else if(hitboxdata.getType().equals("hurt")){
                Globals.hitboxManager.registerHitbox(HitboxUtils.spawnRegularHurtbox(rVal, hitboxdata.getBone(), hitboxdata.getRadius()));
            }
        }
        if(rawType.getCollidable() != null){
            CollidableTemplate physicsTemplate = rawType.getCollidable();
            switch(physicsTemplate.getType()){
                case "CYLINDER":
                    CollisionObject rigidBody = PhysicsUtils.getCylinderObject(new Vector3f(physicsTemplate.getDimension1(),physicsTemplate.getDimension2(),physicsTemplate.getDimension3()));
                    Collidable collidable = new Collidable(rVal, Collidable.TYPE_CREATURE);
                    rVal.putData(EntityDataStrings.PHYSICS_COLLISION_BODY, rigidBody);
                    rVal.putData(EntityDataStrings.PHYSICS_COLLISION_BODY_OFFSET, new Vector3f(physicsTemplate.getOffsetX(),physicsTemplate.getOffsetY(),physicsTemplate.getOffsetZ()));
                    rVal.putData(EntityDataStrings.PHYSICS_MODEL_TEMPLATE, physicsTemplate);
                    rVal.putData(EntityDataStrings.PHYSICS_COLLIDABLE,collidable);
                    rVal.putData(EntityDataStrings.COLLIDABLE_TREE, new CollidableTree(rVal,collidable,rigidBody));
                    Globals.collisionEngine.registerPhysicsEntity(rVal);
                    Globals.collisionEngine.registerDynamicPhysicsEntity(rVal);
                    Globals.collisionEngine.registerCollisionObject(rigidBody, collidable);
                    Globals.entityManager.registerCollidableEntity(rVal);
//                    if(rVal.getDataKeys().contains(EntityDataStrings.DATA_STRING_MOVEMENT_BT)){
//                        CreatureUtils.getEntityMovementTree(rVal).setCollisionObject(rigidBody, collidable);
//                    }
                    break;
            }
        }
        for(MovementSystem movementSystem : rawType.getMovementSystems()){
            switch(movementSystem.getType()){
                case "GROUND":
                    rVal.putData(EntityDataStrings.DATA_STRING_MOVEMENT_BT, new GroundMovementTree(rVal,CollisionObjUtils.getCollidable(rVal)));
                    rVal.putData(EntityDataStrings.DATA_STRING_MOVEMENT_VECTOR, new Vector3f(0,0,0));
                    rVal.putData(EntityDataStrings.DATA_STRING_MAX_NATURAL_VELOCITY, movementSystem.getMaxVelocity());
                    rVal.putData(EntityDataStrings.DATA_STRING_ACCELERATION, movementSystem.getAcceleration());
                    rVal.putData(EntityDataStrings.DATA_STRING_VELOCITY, 0f);
                    Globals.entityManager.registerMoveableEntity(rVal);
                    break;
            }
        }
        for(String token : rawType.getTokens()){
            switch(token){
                case "BLENDER_TRANSFORM":
                    ActorUtils.applyBlenderTransformer(rVal);
                    break;
                case "ATTACKER":
                    AttackTree attackTree = new AttackTree(rVal);
                    rVal.putData(EntityDataStrings.ATTACK_TREE, attackTree);
                    rVal.putData(EntityDataStrings.ATTACK_MOVE_TYPE_ACTIVE, null);
                    Globals.entityManager.registerAttackerEntity(rVal);
                    break;
                case "GRAVITY":
                    Collidable collidable = (Collidable)rVal.getData(EntityDataStrings.PHYSICS_COLLIDABLE);
                    CollisionObject collisionObject = (CollisionObject)rVal.getData(EntityDataStrings.PHYSICS_COLLISION_BODY);
                    GravityTree gravityTree = new GravityTree(rVal,collidable,collisionObject);
//                    gravityTree.setCollisionObject(collisionObject, collidable);
                    rVal.putData(EntityDataStrings.GRAVITY_ENTITY, true);
                    rVal.putData(EntityDataStrings.GRAVITY_TREE, gravityTree);
                    Globals.entityManager.registerGravityEntity(rVal);
                    break;
                case "TARGETABLE":
                    Globals.entityManager.registerTargetableEntity(rVal);
                    break;
            }
        }
        //add all attack moves
        if(rawType.getAttackMoves() != null && rawType.getAttackMoves().size() > 0){
            for(AttackMove attackMove : rawType.getAttackMoves()){
                switch(attackMove.getType()){
                    case EntityDataStrings.ATTACK_MOVE_TYPE_MELEE_SWING_ONE_HAND:
                        rVal.putData(EntityDataStrings.ATTACK_MOVE_TYPE_MELEE_SWING_ONE_HAND, attackMove);
                        break;
                }
            }
        }
        //add health system
        rVal.putData(EntityDataStrings.LIFE_STATE, new LifeState(rVal, rawType.getHealthSystem()));
        Globals.entityManager.registerLifeStateEntity(rVal);
        //idle tree & generic stuff all creatures have
        rVal.putData(EntityDataStrings.IDLE_TREE, new IdleTree(rVal));
        Globals.entityManager.registerCreatureEntity(rVal);
        rVal.putData(EntityDataStrings.DATA_STRING_CREATURE_IS_CREATURE, true);
        rVal.putData(EntityDataStrings.DATA_STRING_CREATURE_TYPE, type);
        rVal.putData(EntityDataStrings.DATA_STRING_MOVEMENT_VECTOR, new Vector3f(0,0,1));
        rVal.putData(EntityDataStrings.DRAW_CAST_SHADOW, true);
        return rVal;
    }
    
    public static void sendEntityToPlayer(Player player, Entity creature){
        int id = creature.getId();
        String type = CreatureUtils.getType(creature);
        Vector3d position = EntityUtils.getPosition(creature);
        NetworkMessage message = EntityMessage.constructCreateMessage(id, EntityDataStrings.ENTITY_CATEGORY_CREATURE, type, (float)position.x, (float)position.y, (float)position.z);
        player.addMessage(message);
        if(CreatureUtils.hasControllerPlayerId(creature)){
            LoggerInterface.loggerNetworking.INFO("Sending controller packets");
            player.addMessage(NetUtils.createSetCreatureControllerIdEntityMessage(creature));
        }
    }
    
    public static void setMovementVector(Entity e, Vector3f vector){
        e.putData(EntityDataStrings.DATA_STRING_MOVEMENT_VECTOR, vector);
    }
    
    public static Vector3f getMovementVector(Entity e){
        return (Vector3f)e.getData(EntityDataStrings.DATA_STRING_MOVEMENT_VECTOR);
    }
    
    public static float getAcceleration(Entity e){
        return (float)e.getData(EntityDataStrings.DATA_STRING_ACCELERATION);
    }
    
    public static void setAcceleration(Entity e, float scalar){
        e.putData(EntityDataStrings.DATA_STRING_ACCELERATION, scalar);
    }
    
    public static float getVelocity(Entity e){
        return (float)e.getData(EntityDataStrings.DATA_STRING_VELOCITY);
    }
    
    public static void setVelocity(Entity e, float scalar){
        e.putData(EntityDataStrings.DATA_STRING_VELOCITY, scalar);
    }
    
    public static float getMaxNaturalVelocity(Entity e){
        return (float)e.getData(EntityDataStrings.DATA_STRING_MAX_NATURAL_VELOCITY);
    }
    
    public static void setMaxNaturalVelocity(Entity e, float scalar){
        e.putData(EntityDataStrings.DATA_STRING_MAX_NATURAL_VELOCITY, scalar);
    }
    
    public static GroundMovementTree getEntityMovementTree(Entity e){
        return (GroundMovementTree)e.getData(EntityDataStrings.DATA_STRING_MOVEMENT_BT);
    }
    
    public static void attachEntityMessageToMovementTree(Entity e, EntityMessage em){
        getEntityMovementTree(e).addNetworkMessage(em);
    }
    
    public static AttackTree getEntityAttackTree(Entity e){
        return (AttackTree)e.getData(EntityDataStrings.ATTACK_TREE);
    }
    
    public static void attachEntityMessageToAttackTree(Entity e, EntityMessage em){
        getEntityAttackTree(e).addNetworkMessage(em);
    }
    
    public static String getType(Entity e){
        return (String)e.getData(EntityDataStrings.DATA_STRING_CREATURE_TYPE);
    }
    
    public static int getControllerPlayerId(Entity e){
        return (int)e.getData(EntityDataStrings.DATA_STRING_CREATURE_CONTROLLER_PLAYER_ID);
    }
    
    public static void setControllerPlayerId(Entity e, int id){
        e.putData(EntityDataStrings.DATA_STRING_CREATURE_CONTROLLER_PLAYER_ID, id);
    }
    
    public static boolean hasControllerPlayerId(Entity e){
        return e.getDataKeys().contains(EntityDataStrings.DATA_STRING_CREATURE_CONTROLLER_PLAYER_ID);
    }
    
    public static boolean isCreature(Entity e){
        return e.getDataKeys().contains(EntityDataStrings.DATA_STRING_CREATURE_IS_CREATURE);
    }
    
    public static AttackTree getAttackTree(Entity e){
        return (AttackTree)e.getData(EntityDataStrings.ATTACK_TREE);
    }
    
    public static IdleTree getIdleTree(Entity e){
        return (IdleTree)e.getData(EntityDataStrings.IDLE_TREE);
    }
    
    
    
    
}
