package electrosphere.net.client;

import electrosphere.entity.Entity;
import electrosphere.entity.EntityUtils;
import electrosphere.entity.types.creature.CreatureUtils;
import electrosphere.game.client.world.ClientWorldData;
import electrosphere.entity.types.attach.AttachUtils;
import electrosphere.entity.types.item.ItemUtils;
import electrosphere.logger.LoggerInterface;
import electrosphere.main.Globals;
import electrosphere.main.Main;
import electrosphere.net.parser.net.message.EntityMessage;
import electrosphere.net.parser.net.message.NetworkMessage;
import electrosphere.net.parser.net.message.PlayerMessage;
import electrosphere.net.parser.net.message.StatusMessage;
import electrosphere.net.parser.net.message.WorldMessage;
import org.joml.Vector2f;
import org.joml.Vector3f;

public class ClientProtocol {
    
    boolean isLoading = true;
    
    boolean hasReceivedWorld = false;
    
    public void handleMessage(NetworkMessage message){
        switch(message.getType()){
            case STATUS_MESSAGE:
                handleStatusMessage((StatusMessage)message);
                break;
            case ENTITY_MESSAGE:
                handleEntityMessage((EntityMessage)message);
                break;
            case PLAYER_MESSAGE:
                handlePlayerMessage((PlayerMessage)message);
                break;
            case WORLD_MESSAGE:
                handleWorldMessage((WorldMessage)message);
                break;
        }
    }
    
    void handleStatusMessage(StatusMessage message){
        switch(message.getMessageSubtype()){
            case READY:
                isLoading = false;
                break;
        }
    }
    
    void handleEntityMessage(EntityMessage message){
        LoggerInterface.loggerNetworking.DEBUG("Parse entity message of type " + message.getMessageSubtype());
        switch(message.getMessageSubtype()){
            case CREATE:
                LoggerInterface.loggerNetworking.DEBUG("Spawn ID " + message.getentityID() + " of type " + message.getentityCategory() + " subtype " + message.getentitySubtype());
                Entity newlySpawnedEntity;
                switch(message.getentityCategory()){
                    case 0:
                        newlySpawnedEntity = CreatureUtils.spawnBasicCreature(message.getentitySubtype());
                        EntityUtils.getScale(newlySpawnedEntity).set(0.005f);
                        EntityUtils.getPosition(newlySpawnedEntity).set(message.getpositionX(),message.getpositionY(),message.getpositionZ());
                        EntityUtils.setEntityID(newlySpawnedEntity, message.getentityID());
                        break;
                    case 1:
                        newlySpawnedEntity = ItemUtils.spawnBasicItem(message.getentitySubtype());
                        EntityUtils.getScale(newlySpawnedEntity).set(0.005f);
                        EntityUtils.getPosition(newlySpawnedEntity).set(message.getpositionX(),message.getpositionY(),message.getpositionZ());
                        EntityUtils.setEntityID(newlySpawnedEntity, message.getentityID());
                        break;
                }
                break;
            case DESTROY:
                break;
            case MOVE:
                //literally just adding this to scope so I can use `` Entity target; `` again
                if(message.getentityID() != -1){
                    Entity target = Globals.entityManager.getEntityFromId(message.getentityID());
                    LoggerInterface.loggerNetworking.DEBUG("ID: " + message.getentityID());
                    if(target != null){
                        EntityUtils.getPosition(target).set(message.getpositionX(),message.getpositionY(),message.getpositionZ());
                    }
                }
//                CreatureUtils.attachEntityMessageToMovementTree(Globals.entityManager.getEntityFromId(message.getId()),message);
                break;
            case SETBEHAVIORTREE:
                break;
            case SETPROPERTY:
                if(message.getpropertyType()== 0){
                    Entity target = Globals.entityManager.getEntityFromId(message.getentityID());
                    if(target != null){
                        CreatureUtils.setControllerPlayerId(target, message.getpropertyValue());
                        if(message.getpropertyValue() == Main.playerId){
                            Globals.clientCharacterID = message.getentityID();
                            Globals.playerCharacter = target;
                        }
                    }
                }
                break;
            case ATTACHENTITYTOENTITY:
                Entity child = Globals.entityManager.getEntityFromId(message.getentityID());
                Entity parent = Globals.entityManager.getEntityFromId(message.gettargetID());
                LoggerInterface.loggerNetworking.DEBUG("Attach " + message.getentityID() + " to " + message.gettargetID() + " on bone " + message.getbone());
                if(child != null && parent != null){
                    AttachUtils.attachEntityToEntityAtBone(parent, child, message.getbone());
                }
                break;
            case MOVEUPDATE:
                CreatureUtils.attachEntityMessageToMovementTree(Globals.entityManager.getEntityFromId(message.getentityID()),message);
                break;
            case ATTACKUPDATE:
                CreatureUtils.attachEntityMessageToAttackTree(Globals.entityManager.getEntityFromId(message.getentityID()),message);
                break;
        }
    }
    
    void handlePlayerMessage(PlayerMessage message){
        switch(message.getMessageSubtype()){
            case SET_ID:
                Main.playerId = message.getplayerID();
                LoggerInterface.loggerNetworking.DEBUG("Player ID is " + Main.playerId);
                break;
            case SETINITIALDISCRETEPOSITION:
                Globals.clientPlayerData.setWorldPosition(message.getinitialDiscretePositionX(), message.getinitialDiscretePositionY());
                break;
        }
    }
    
    void handleWorldMessage(WorldMessage message){
        switch(message.getMessageSubtype()){
            case METADATA:
                Globals.clientWorldData = new ClientWorldData(
                        //Vector3f worldMinPoint, Vector3f worldMaxPoint, int dynamicInterpolationRatio, float randomDampener, int worldDiscreteSize
                        new Vector3f(message.getworldMinX(),0,message.getworldMinY()),
                        new Vector3f(message.getworldMaxX(),0,message.getworldMaxY()),
                        message.getdynamicInterpolationRatio(),
                        message.getrandomDampener(),
                        message.getworldSizeDiscrete()
                );
                hasReceivedWorld = true;
                break;
            case MACROVALUE:
                Globals.clientTerrainManager.attachWorldMessage(message);
                break;
            case SPAWNPOSITION:
                Globals.spawnPoint.set(new Vector3f(message.getrealLocationX(),0.25f,message.getrealLocationY()));
                break;
            case CHUNKLOADSTART:
                Globals.clientTerrainManager.attachWorldMessage(message);
                break;
            case HEIGHTMAPMODIFICATION:
                Globals.clientTerrainManager.attachWorldMessage(message);
                break;
            default:
                LoggerInterface.loggerNetworking.WARNING("Client networking: Unhandled message of type: " + message.getMessageSubtype());
                break;
        }
    }
    
    public boolean isLoading(){
        return isLoading;
    }
    
    public boolean hasReceivedWorld(){
        return hasReceivedWorld;
    }
}
