package electrosphere.net.parser.net.message;

import electrosphere.net.parser.util.ByteStreamUtils;
import electrosphere.net.parser.net.raw.CircularByteBuffer;

import java.util.List;

public abstract class NetworkMessage {
    
    public enum MessageType {
ENTITY_MESSAGE,
LORE_MESSAGE,
PLAYER_MESSAGE,
TERRAIN_MESSAGE,
SERVER_MESSAGE,
AUTH_MESSAGE,
CHARACTER_MESSAGE,
INVENTORY_MESSAGE,
SYNCHRONIZATION_MESSAGE,
COMBAT_MESSAGE,
    }
    
    MessageType type;
    boolean serialized; // has this message been converted to bytes?
    byte[] rawBytes;

    public MessageType getType() {
        return type;
    }

    public byte[] getRawBytes() {
        return rawBytes;
    }
    
    public static NetworkMessage parseBytestreamForMessage(CircularByteBuffer byteBuffer){
        NetworkMessage rVal = null;
        byte firstByte;
        byte secondByte;
        if(byteBuffer.getRemaining() > 1){
            firstByte = byteBuffer.peek();
            switch(firstByte){
                case TypeBytes.MESSAGE_TYPE_ENTITY:
                    secondByte = byteBuffer.peek(1);
                    switch(secondByte){
                    case TypeBytes.ENTITY_MESSAGE_TYPE_CREATE:
                        if(EntityMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = EntityMessage.parseCreateMessage(byteBuffer);
                        }
                        break;
                    case TypeBytes.ENTITY_MESSAGE_TYPE_SPAWNCREATURE:
                        if(EntityMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = EntityMessage.parseSpawnCreatureMessage(byteBuffer);
                        }
                        break;
                    case TypeBytes.ENTITY_MESSAGE_TYPE_SPAWNITEM:
                        if(EntityMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = EntityMessage.parseSpawnItemMessage(byteBuffer);
                        }
                        break;
                    case TypeBytes.ENTITY_MESSAGE_TYPE_SPAWNOBJECT:
                        if(EntityMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = EntityMessage.parseSpawnObjectMessage(byteBuffer);
                        }
                        break;
                    case TypeBytes.ENTITY_MESSAGE_TYPE_MOVEUPDATE:
                        if(EntityMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = EntityMessage.parsemoveUpdateMessage(byteBuffer);
                        }
                        break;
                    case TypeBytes.ENTITY_MESSAGE_TYPE_ATTACKUPDATE:
                        if(EntityMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = EntityMessage.parseattackUpdateMessage(byteBuffer);
                        }
                        break;
                    case TypeBytes.ENTITY_MESSAGE_TYPE_STARTATTACK:
                        if(EntityMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = EntityMessage.parsestartAttackMessage(byteBuffer);
                        }
                        break;
                    case TypeBytes.ENTITY_MESSAGE_TYPE_KILL:
                        if(EntityMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = EntityMessage.parseKillMessage(byteBuffer);
                        }
                        break;
                    case TypeBytes.ENTITY_MESSAGE_TYPE_DESTROY:
                        if(EntityMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = EntityMessage.parseDestroyMessage(byteBuffer);
                        }
                        break;
                    case TypeBytes.ENTITY_MESSAGE_TYPE_SETPROPERTY:
                        if(EntityMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = EntityMessage.parsesetPropertyMessage(byteBuffer);
                        }
                        break;
                    case TypeBytes.ENTITY_MESSAGE_TYPE_ATTACHENTITYTOENTITY:
                        if(EntityMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = EntityMessage.parseattachEntityToEntityMessage(byteBuffer);
                        }
                        break;
                    case TypeBytes.ENTITY_MESSAGE_TYPE_SPAWNFOLIAGESEED:
                        if(EntityMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = EntityMessage.parseSpawnFoliageSeedMessage(byteBuffer);
                        }
                        break;
                    case TypeBytes.ENTITY_MESSAGE_TYPE_UPDATEENTITYVIEWDIR:
                        if(EntityMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = EntityMessage.parseupdateEntityViewDirMessage(byteBuffer);
                        }
                        break;
                    case TypeBytes.ENTITY_MESSAGE_TYPE_SYNCPHYSICS:
                        if(EntityMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = EntityMessage.parsesyncPhysicsMessage(byteBuffer);
                        }
                        break;
                }
                break;
                case TypeBytes.MESSAGE_TYPE_LORE:
                    secondByte = byteBuffer.peek(1);
                    switch(secondByte){
                    case TypeBytes.LORE_MESSAGE_TYPE_REQUESTRACES:
                        if(LoreMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = LoreMessage.parseRequestRacesMessage(byteBuffer);
                        }
                        break;
                    case TypeBytes.LORE_MESSAGE_TYPE_RESPONSERACES:
                        if(LoreMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = LoreMessage.parseResponseRacesMessage(byteBuffer);
                        }
                        break;
                }
                break;
                case TypeBytes.MESSAGE_TYPE_PLAYER:
                    secondByte = byteBuffer.peek(1);
                    switch(secondByte){
                    case TypeBytes.PLAYER_MESSAGE_TYPE_SET_ID:
                        if(PlayerMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = PlayerMessage.parseSet_IDMessage(byteBuffer);
                        }
                        break;
                    case TypeBytes.PLAYER_MESSAGE_TYPE_SETINITIALDISCRETEPOSITION:
                        if(PlayerMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = PlayerMessage.parseSetInitialDiscretePositionMessage(byteBuffer);
                        }
                        break;
                }
                break;
                case TypeBytes.MESSAGE_TYPE_TERRAIN:
                    secondByte = byteBuffer.peek(1);
                    switch(secondByte){
                    case TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTMETADATA:
                        if(TerrainMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = TerrainMessage.parseRequestMetadataMessage(byteBuffer);
                        }
                        break;
                    case TypeBytes.TERRAIN_MESSAGE_TYPE_RESPONSEMETADATA:
                        if(TerrainMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = TerrainMessage.parseResponseMetadataMessage(byteBuffer);
                        }
                        break;
                    case TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTEDITVOXEL:
                        if(TerrainMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = TerrainMessage.parseRequestEditVoxelMessage(byteBuffer);
                        }
                        break;
                    case TypeBytes.TERRAIN_MESSAGE_TYPE_UPDATEVOXEL:
                        if(TerrainMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = TerrainMessage.parseUpdateVoxelMessage(byteBuffer);
                        }
                        break;
                    case TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTUSETERRAINPALETTE:
                        if(TerrainMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = TerrainMessage.parseRequestUseTerrainPaletteMessage(byteBuffer);
                        }
                        break;
                    case TypeBytes.TERRAIN_MESSAGE_TYPE_SPAWNPOSITION:
                        if(TerrainMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = TerrainMessage.parseSpawnPositionMessage(byteBuffer);
                        }
                        break;
                    case TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTCHUNKDATA:
                        if(TerrainMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = TerrainMessage.parseRequestChunkDataMessage(byteBuffer);
                        }
                        break;
                    case TypeBytes.TERRAIN_MESSAGE_TYPE_SENDCHUNKDATA:
                        if(TerrainMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = TerrainMessage.parsesendChunkDataMessage(byteBuffer);
                        }
                        break;
                    case TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTREDUCEDCHUNKDATA:
                        if(TerrainMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = TerrainMessage.parseRequestReducedChunkDataMessage(byteBuffer);
                        }
                        break;
                    case TypeBytes.TERRAIN_MESSAGE_TYPE_SENDREDUCEDCHUNKDATA:
                        if(TerrainMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = TerrainMessage.parseSendReducedChunkDataMessage(byteBuffer);
                        }
                        break;
                    case TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTFLUIDDATA:
                        if(TerrainMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = TerrainMessage.parseRequestFluidDataMessage(byteBuffer);
                        }
                        break;
                    case TypeBytes.TERRAIN_MESSAGE_TYPE_SENDFLUIDDATA:
                        if(TerrainMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = TerrainMessage.parsesendFluidDataMessage(byteBuffer);
                        }
                        break;
                    case TypeBytes.TERRAIN_MESSAGE_TYPE_UPDATEFLUIDDATA:
                        if(TerrainMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = TerrainMessage.parseupdateFluidDataMessage(byteBuffer);
                        }
                        break;
                }
                break;
                case TypeBytes.MESSAGE_TYPE_SERVER:
                    secondByte = byteBuffer.peek(1);
                    switch(secondByte){
                    case TypeBytes.SERVER_MESSAGE_TYPE_PING:
                        if(ServerMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = ServerMessage.parsePingMessage(byteBuffer);
                        }
                        break;
                    case TypeBytes.SERVER_MESSAGE_TYPE_PONG:
                        if(ServerMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = ServerMessage.parsePongMessage(byteBuffer);
                        }
                        break;
                }
                break;
                case TypeBytes.MESSAGE_TYPE_AUTH:
                    secondByte = byteBuffer.peek(1);
                    switch(secondByte){
                    case TypeBytes.AUTH_MESSAGE_TYPE_AUTHREQUEST:
                        if(AuthMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = AuthMessage.parseAuthRequestMessage(byteBuffer);
                        }
                        break;
                    case TypeBytes.AUTH_MESSAGE_TYPE_AUTHDETAILS:
                        if(AuthMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = AuthMessage.parseAuthDetailsMessage(byteBuffer);
                        }
                        break;
                    case TypeBytes.AUTH_MESSAGE_TYPE_AUTHSUCCESS:
                        if(AuthMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = AuthMessage.parseAuthSuccessMessage(byteBuffer);
                        }
                        break;
                    case TypeBytes.AUTH_MESSAGE_TYPE_AUTHFAILURE:
                        if(AuthMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = AuthMessage.parseAuthFailureMessage(byteBuffer);
                        }
                        break;
                }
                break;
                case TypeBytes.MESSAGE_TYPE_CHARACTER:
                    secondByte = byteBuffer.peek(1);
                    switch(secondByte){
                    case TypeBytes.CHARACTER_MESSAGE_TYPE_REQUESTCHARACTERLIST:
                        if(CharacterMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = CharacterMessage.parseRequestCharacterListMessage(byteBuffer);
                        }
                        break;
                    case TypeBytes.CHARACTER_MESSAGE_TYPE_RESPONSECHARACTERLIST:
                        if(CharacterMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = CharacterMessage.parseResponseCharacterListMessage(byteBuffer);
                        }
                        break;
                    case TypeBytes.CHARACTER_MESSAGE_TYPE_REQUESTCREATECHARACTER:
                        if(CharacterMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = CharacterMessage.parseRequestCreateCharacterMessage(byteBuffer);
                        }
                        break;
                    case TypeBytes.CHARACTER_MESSAGE_TYPE_RESPONSECREATECHARACTERSUCCESS:
                        if(CharacterMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = CharacterMessage.parseResponseCreateCharacterSuccessMessage(byteBuffer);
                        }
                        break;
                    case TypeBytes.CHARACTER_MESSAGE_TYPE_RESPONSECREATECHARACTERFAILURE:
                        if(CharacterMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = CharacterMessage.parseResponseCreateCharacterFailureMessage(byteBuffer);
                        }
                        break;
                    case TypeBytes.CHARACTER_MESSAGE_TYPE_REQUESTSPAWNCHARACTER:
                        if(CharacterMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = CharacterMessage.parseRequestSpawnCharacterMessage(byteBuffer);
                        }
                        break;
                    case TypeBytes.CHARACTER_MESSAGE_TYPE_RESPONSESPAWNCHARACTER:
                        if(CharacterMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = CharacterMessage.parseResponseSpawnCharacterMessage(byteBuffer);
                        }
                        break;
                }
                break;
                case TypeBytes.MESSAGE_TYPE_INVENTORY:
                    secondByte = byteBuffer.peek(1);
                    switch(secondByte){
                    case TypeBytes.INVENTORY_MESSAGE_TYPE_ADDITEMTOINVENTORY:
                        if(InventoryMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = InventoryMessage.parseaddItemToInventoryMessage(byteBuffer);
                        }
                        break;
                    case TypeBytes.INVENTORY_MESSAGE_TYPE_REMOVEITEMFROMINVENTORY:
                        if(InventoryMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = InventoryMessage.parseremoveItemFromInventoryMessage(byteBuffer);
                        }
                        break;
                    case TypeBytes.INVENTORY_MESSAGE_TYPE_CLIENTREQUESTEQUIPITEM:
                        if(InventoryMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = InventoryMessage.parseclientRequestEquipItemMessage(byteBuffer);
                        }
                        break;
                    case TypeBytes.INVENTORY_MESSAGE_TYPE_SERVERCOMMANDMOVEITEMCONTAINER:
                        if(InventoryMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = InventoryMessage.parseserverCommandMoveItemContainerMessage(byteBuffer);
                        }
                        break;
                    case TypeBytes.INVENTORY_MESSAGE_TYPE_SERVERCOMMANDEQUIPITEM:
                        if(InventoryMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = InventoryMessage.parseserverCommandEquipItemMessage(byteBuffer);
                        }
                        break;
                    case TypeBytes.INVENTORY_MESSAGE_TYPE_SERVERCOMMANDUNEQUIPITEM:
                        if(InventoryMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = InventoryMessage.parseserverCommandUnequipItemMessage(byteBuffer);
                        }
                        break;
                    case TypeBytes.INVENTORY_MESSAGE_TYPE_CLIENTREQUESTUNEQUIPITEM:
                        if(InventoryMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = InventoryMessage.parseclientRequestUnequipItemMessage(byteBuffer);
                        }
                        break;
                    case TypeBytes.INVENTORY_MESSAGE_TYPE_CLIENTREQUESTPERFORMITEMACTION:
                        if(InventoryMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = InventoryMessage.parseclientRequestPerformItemActionMessage(byteBuffer);
                        }
                        break;
                }
                break;
                case TypeBytes.MESSAGE_TYPE_SYNCHRONIZATION:
                    secondByte = byteBuffer.peek(1);
                    switch(secondByte){
                    case TypeBytes.SYNCHRONIZATION_MESSAGE_TYPE_UPDATECLIENTSTATE:
                        if(SynchronizationMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = SynchronizationMessage.parseUpdateClientStateMessage(byteBuffer);
                        }
                        break;
                    case TypeBytes.SYNCHRONIZATION_MESSAGE_TYPE_UPDATECLIENTSTRINGSTATE:
                        if(SynchronizationMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = SynchronizationMessage.parseUpdateClientStringStateMessage(byteBuffer);
                        }
                        break;
                    case TypeBytes.SYNCHRONIZATION_MESSAGE_TYPE_UPDATECLIENTINTSTATE:
                        if(SynchronizationMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = SynchronizationMessage.parseUpdateClientIntStateMessage(byteBuffer);
                        }
                        break;
                    case TypeBytes.SYNCHRONIZATION_MESSAGE_TYPE_UPDATECLIENTLONGSTATE:
                        if(SynchronizationMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = SynchronizationMessage.parseUpdateClientLongStateMessage(byteBuffer);
                        }
                        break;
                    case TypeBytes.SYNCHRONIZATION_MESSAGE_TYPE_UPDATECLIENTFLOATSTATE:
                        if(SynchronizationMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = SynchronizationMessage.parseUpdateClientFloatStateMessage(byteBuffer);
                        }
                        break;
                    case TypeBytes.SYNCHRONIZATION_MESSAGE_TYPE_UPDATECLIENTDOUBLESTATE:
                        if(SynchronizationMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = SynchronizationMessage.parseUpdateClientDoubleStateMessage(byteBuffer);
                        }
                        break;
                    case TypeBytes.SYNCHRONIZATION_MESSAGE_TYPE_CLIENTREQUESTBTREEACTION:
                        if(SynchronizationMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = SynchronizationMessage.parseClientRequestBTreeActionMessage(byteBuffer);
                        }
                        break;
                    case TypeBytes.SYNCHRONIZATION_MESSAGE_TYPE_SERVERNOTIFYBTREETRANSITION:
                        if(SynchronizationMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = SynchronizationMessage.parseServerNotifyBTreeTransitionMessage(byteBuffer);
                        }
                        break;
                    case TypeBytes.SYNCHRONIZATION_MESSAGE_TYPE_ATTACHTREE:
                        if(SynchronizationMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = SynchronizationMessage.parseAttachTreeMessage(byteBuffer);
                        }
                        break;
                    case TypeBytes.SYNCHRONIZATION_MESSAGE_TYPE_DETATCHTREE:
                        if(SynchronizationMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = SynchronizationMessage.parseDetatchTreeMessage(byteBuffer);
                        }
                        break;
                    case TypeBytes.SYNCHRONIZATION_MESSAGE_TYPE_LOADSCENE:
                        if(SynchronizationMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = SynchronizationMessage.parseLoadSceneMessage(byteBuffer);
                        }
                        break;
                }
                break;
                case TypeBytes.MESSAGE_TYPE_COMBAT:
                    secondByte = byteBuffer.peek(1);
                    switch(secondByte){
                    case TypeBytes.COMBAT_MESSAGE_TYPE_SERVERREPORTHITBOXCOLLISION:
                        if(CombatMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = CombatMessage.parseserverReportHitboxCollisionMessage(byteBuffer);
                        }
                        break;
                }
                break;
            }
        }
        return rVal;
    }
    
    public boolean isSerialized(){
        return serialized;
    }
    
    abstract void serialize();
    
}

