package electrosphere.net.parser.net.message;

import io.github.studiorailgun.CircularByteBuffer;

/**
 * A network message
 */
public abstract class NetworkMessage {
    
    /**
     * The different categories of network messages
     */
    public enum MessageType {
        ENTITY_MESSAGE,
        LORE_MESSAGE,
        PLAYER_MESSAGE,
        TERRAIN_MESSAGE,
        SERVER_MESSAGE,
        AUTH_MESSAGE,
        CHARACTER_MESSAGE,
        INVENTORY_MESSAGE,
        SYNCHRONIZATION_MESSAGE,
        COMBAT_MESSAGE,
    }
    
    /**
     * The type of this message
     */
    MessageType type;

    /**
     * Tracks whether the message has been serialized to bytes or not
     */
    boolean serialized;

    /**
     * The raw bytes contained in the message
     */
    byte[] rawBytes;

    /**
     * Gets the type of the message
     * @return The type of the message
     */
    public MessageType getType() {
        return type;
    }

    /**
     * Gets the raw bytes of the message
     * @return The raw bytes
     */
    public byte[] getRawBytes() {
        return rawBytes;
    }
    
    /**
     * Parses the byte stream for the next message
     * @param byteBuffer The byte buffer
     * @param pool The message pool
     * @return The message if one is at the front of the byte stream, null otherwise
     */
    public static NetworkMessage parseBytestreamForMessage(CircularByteBuffer byteBuffer, MessagePool pool){
        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,pool);
                        }
                        break;
                    case TypeBytes.ENTITY_MESSAGE_TYPE_MOVEUPDATE:
                        if(EntityMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = EntityMessage.parsemoveUpdateMessage(byteBuffer,pool);
                        }
                        break;
                    case TypeBytes.ENTITY_MESSAGE_TYPE_ATTACKUPDATE:
                        if(EntityMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = EntityMessage.parseattackUpdateMessage(byteBuffer,pool);
                        }
                        break;
                    case TypeBytes.ENTITY_MESSAGE_TYPE_STARTATTACK:
                        if(EntityMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = EntityMessage.parsestartAttackMessage(byteBuffer,pool);
                        }
                        break;
                    case TypeBytes.ENTITY_MESSAGE_TYPE_KILL:
                        if(EntityMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = EntityMessage.parseKillMessage(byteBuffer,pool);
                        }
                        break;
                    case TypeBytes.ENTITY_MESSAGE_TYPE_DESTROY:
                        if(EntityMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = EntityMessage.parseDestroyMessage(byteBuffer,pool);
                        }
                        break;
                    case TypeBytes.ENTITY_MESSAGE_TYPE_SETPROPERTY:
                        if(EntityMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = EntityMessage.parsesetPropertyMessage(byteBuffer,pool);
                        }
                        break;
                    case TypeBytes.ENTITY_MESSAGE_TYPE_ATTACHENTITYTOENTITY:
                        if(EntityMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = EntityMessage.parseattachEntityToEntityMessage(byteBuffer,pool);
                        }
                        break;
                    case TypeBytes.ENTITY_MESSAGE_TYPE_UPDATEENTITYVIEWDIR:
                        if(EntityMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = EntityMessage.parseupdateEntityViewDirMessage(byteBuffer,pool);
                        }
                        break;
                    case TypeBytes.ENTITY_MESSAGE_TYPE_SYNCPHYSICS:
                        if(EntityMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = EntityMessage.parsesyncPhysicsMessage(byteBuffer,pool);
                        }
                        break;
                    case TypeBytes.ENTITY_MESSAGE_TYPE_INTERACT:
                        if(EntityMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = EntityMessage.parseinteractMessage(byteBuffer,pool);
                        }
                        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,pool);
                        }
                        break;
                    case TypeBytes.LORE_MESSAGE_TYPE_RESPONSERACES:
                        if(LoreMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = LoreMessage.parseResponseRacesMessage(byteBuffer,pool);
                        }
                        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,pool);
                        }
                        break;
                    case TypeBytes.PLAYER_MESSAGE_TYPE_SETINITIALDISCRETEPOSITION:
                        if(PlayerMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = PlayerMessage.parseSetInitialDiscretePositionMessage(byteBuffer,pool);
                        }
                        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,pool);
                        }
                        break;
                    case TypeBytes.TERRAIN_MESSAGE_TYPE_RESPONSEMETADATA:
                        if(TerrainMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = TerrainMessage.parseResponseMetadataMessage(byteBuffer,pool);
                        }
                        break;
                    case TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTEDITVOXEL:
                        if(TerrainMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = TerrainMessage.parseRequestEditVoxelMessage(byteBuffer,pool);
                        }
                        break;
                    case TypeBytes.TERRAIN_MESSAGE_TYPE_UPDATEVOXEL:
                        if(TerrainMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = TerrainMessage.parseUpdateVoxelMessage(byteBuffer,pool);
                        }
                        break;
                    case TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTUSETERRAINPALETTE:
                        if(TerrainMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = TerrainMessage.parseRequestUseTerrainPaletteMessage(byteBuffer,pool);
                        }
                        break;
                    case TypeBytes.TERRAIN_MESSAGE_TYPE_SPAWNPOSITION:
                        if(TerrainMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = TerrainMessage.parseSpawnPositionMessage(byteBuffer,pool);
                        }
                        break;
                    case TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTCHUNKDATA:
                        if(TerrainMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = TerrainMessage.parseRequestChunkDataMessage(byteBuffer,pool);
                        }
                        break;
                    case TypeBytes.TERRAIN_MESSAGE_TYPE_SENDCHUNKDATA:
                        if(TerrainMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = TerrainMessage.parsesendChunkDataMessage(byteBuffer,pool);
                        }
                        break;
                    case TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTREDUCEDCHUNKDATA:
                        if(TerrainMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = TerrainMessage.parseRequestReducedChunkDataMessage(byteBuffer,pool);
                        }
                        break;
                    case TypeBytes.TERRAIN_MESSAGE_TYPE_SENDREDUCEDCHUNKDATA:
                        if(TerrainMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = TerrainMessage.parseSendReducedChunkDataMessage(byteBuffer,pool);
                        }
                        break;
                    case TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTREDUCEDBLOCKDATA:
                        if(TerrainMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = TerrainMessage.parseRequestReducedBlockDataMessage(byteBuffer,pool);
                        }
                        break;
                    case TypeBytes.TERRAIN_MESSAGE_TYPE_SENDREDUCEDBLOCKDATA:
                        if(TerrainMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = TerrainMessage.parseSendReducedBlockDataMessage(byteBuffer,pool);
                        }
                        break;
                    case TypeBytes.TERRAIN_MESSAGE_TYPE_UPDATEBLOCK:
                        if(TerrainMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = TerrainMessage.parseUpdateBlockMessage(byteBuffer,pool);
                        }
                        break;
                    case TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTFLUIDDATA:
                        if(TerrainMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = TerrainMessage.parseRequestFluidDataMessage(byteBuffer,pool);
                        }
                        break;
                    case TypeBytes.TERRAIN_MESSAGE_TYPE_SENDFLUIDDATA:
                        if(TerrainMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = TerrainMessage.parsesendFluidDataMessage(byteBuffer,pool);
                        }
                        break;
                    case TypeBytes.TERRAIN_MESSAGE_TYPE_UPDATEFLUIDDATA:
                        if(TerrainMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = TerrainMessage.parseupdateFluidDataMessage(byteBuffer,pool);
                        }
                        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,pool);
                        }
                        break;
                    case TypeBytes.SERVER_MESSAGE_TYPE_PONG:
                        if(ServerMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = ServerMessage.parsePongMessage(byteBuffer,pool);
                        }
                        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,pool);
                        }
                        break;
                    case TypeBytes.AUTH_MESSAGE_TYPE_AUTHDETAILS:
                        if(AuthMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = AuthMessage.parseAuthDetailsMessage(byteBuffer,pool);
                        }
                        break;
                    case TypeBytes.AUTH_MESSAGE_TYPE_AUTHSUCCESS:
                        if(AuthMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = AuthMessage.parseAuthSuccessMessage(byteBuffer,pool);
                        }
                        break;
                    case TypeBytes.AUTH_MESSAGE_TYPE_AUTHFAILURE:
                        if(AuthMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = AuthMessage.parseAuthFailureMessage(byteBuffer,pool);
                        }
                        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,pool);
                        }
                        break;
                    case TypeBytes.CHARACTER_MESSAGE_TYPE_RESPONSECHARACTERLIST:
                        if(CharacterMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = CharacterMessage.parseResponseCharacterListMessage(byteBuffer,pool);
                        }
                        break;
                    case TypeBytes.CHARACTER_MESSAGE_TYPE_REQUESTCREATECHARACTER:
                        if(CharacterMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = CharacterMessage.parseRequestCreateCharacterMessage(byteBuffer,pool);
                        }
                        break;
                    case TypeBytes.CHARACTER_MESSAGE_TYPE_RESPONSECREATECHARACTERSUCCESS:
                        if(CharacterMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = CharacterMessage.parseResponseCreateCharacterSuccessMessage(byteBuffer,pool);
                        }
                        break;
                    case TypeBytes.CHARACTER_MESSAGE_TYPE_RESPONSECREATECHARACTERFAILURE:
                        if(CharacterMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = CharacterMessage.parseResponseCreateCharacterFailureMessage(byteBuffer,pool);
                        }
                        break;
                    case TypeBytes.CHARACTER_MESSAGE_TYPE_REQUESTSPAWNCHARACTER:
                        if(CharacterMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = CharacterMessage.parseRequestSpawnCharacterMessage(byteBuffer,pool);
                        }
                        break;
                    case TypeBytes.CHARACTER_MESSAGE_TYPE_RESPONSESPAWNCHARACTER:
                        if(CharacterMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = CharacterMessage.parseResponseSpawnCharacterMessage(byteBuffer,pool);
                        }
                        break;
                    case TypeBytes.CHARACTER_MESSAGE_TYPE_EDITORSWAP:
                        if(CharacterMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = CharacterMessage.parseEditorSwapMessage(byteBuffer,pool);
                        }
                        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,pool);
                        }
                        break;
                    case TypeBytes.INVENTORY_MESSAGE_TYPE_REMOVEITEMFROMINVENTORY:
                        if(InventoryMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = InventoryMessage.parseremoveItemFromInventoryMessage(byteBuffer,pool);
                        }
                        break;
                    case TypeBytes.INVENTORY_MESSAGE_TYPE_CLIENTREQUESTEQUIPITEM:
                        if(InventoryMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = InventoryMessage.parseclientRequestEquipItemMessage(byteBuffer,pool);
                        }
                        break;
                    case TypeBytes.INVENTORY_MESSAGE_TYPE_SERVERCOMMANDMOVEITEMCONTAINER:
                        if(InventoryMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = InventoryMessage.parseserverCommandMoveItemContainerMessage(byteBuffer,pool);
                        }
                        break;
                    case TypeBytes.INVENTORY_MESSAGE_TYPE_SERVERCOMMANDEQUIPITEM:
                        if(InventoryMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = InventoryMessage.parseserverCommandEquipItemMessage(byteBuffer,pool);
                        }
                        break;
                    case TypeBytes.INVENTORY_MESSAGE_TYPE_SERVERCOMMANDUNEQUIPITEM:
                        if(InventoryMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = InventoryMessage.parseserverCommandUnequipItemMessage(byteBuffer,pool);
                        }
                        break;
                    case TypeBytes.INVENTORY_MESSAGE_TYPE_CLIENTREQUESTUNEQUIPITEM:
                        if(InventoryMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = InventoryMessage.parseclientRequestUnequipItemMessage(byteBuffer,pool);
                        }
                        break;
                    case TypeBytes.INVENTORY_MESSAGE_TYPE_CLIENTREQUESTADDTOOLBAR:
                        if(InventoryMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = InventoryMessage.parseclientRequestAddToolbarMessage(byteBuffer,pool);
                        }
                        break;
                    case TypeBytes.INVENTORY_MESSAGE_TYPE_CLIENTREQUESTADDNATURAL:
                        if(InventoryMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = InventoryMessage.parseclientRequestAddNaturalMessage(byteBuffer,pool);
                        }
                        break;
                    case TypeBytes.INVENTORY_MESSAGE_TYPE_CLIENTUPDATETOOLBAR:
                        if(InventoryMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = InventoryMessage.parseclientUpdateToolbarMessage(byteBuffer,pool);
                        }
                        break;
                    case TypeBytes.INVENTORY_MESSAGE_TYPE_CLIENTREQUESTPERFORMITEMACTION:
                        if(InventoryMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = InventoryMessage.parseclientRequestPerformItemActionMessage(byteBuffer,pool);
                        }
                        break;
                    case TypeBytes.INVENTORY_MESSAGE_TYPE_CLIENTREQUESTCRAFT:
                        if(InventoryMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = InventoryMessage.parseclientRequestCraftMessage(byteBuffer,pool);
                        }
                        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,pool);
                        }
                        break;
                    case TypeBytes.SYNCHRONIZATION_MESSAGE_TYPE_UPDATECLIENTSTRINGSTATE:
                        if(SynchronizationMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = SynchronizationMessage.parseUpdateClientStringStateMessage(byteBuffer,pool);
                        }
                        break;
                    case TypeBytes.SYNCHRONIZATION_MESSAGE_TYPE_UPDATECLIENTINTSTATE:
                        if(SynchronizationMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = SynchronizationMessage.parseUpdateClientIntStateMessage(byteBuffer,pool);
                        }
                        break;
                    case TypeBytes.SYNCHRONIZATION_MESSAGE_TYPE_UPDATECLIENTLONGSTATE:
                        if(SynchronizationMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = SynchronizationMessage.parseUpdateClientLongStateMessage(byteBuffer,pool);
                        }
                        break;
                    case TypeBytes.SYNCHRONIZATION_MESSAGE_TYPE_UPDATECLIENTFLOATSTATE:
                        if(SynchronizationMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = SynchronizationMessage.parseUpdateClientFloatStateMessage(byteBuffer,pool);
                        }
                        break;
                    case TypeBytes.SYNCHRONIZATION_MESSAGE_TYPE_UPDATECLIENTDOUBLESTATE:
                        if(SynchronizationMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = SynchronizationMessage.parseUpdateClientDoubleStateMessage(byteBuffer,pool);
                        }
                        break;
                    case TypeBytes.SYNCHRONIZATION_MESSAGE_TYPE_CLIENTREQUESTBTREEACTION:
                        if(SynchronizationMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = SynchronizationMessage.parseClientRequestBTreeActionMessage(byteBuffer,pool);
                        }
                        break;
                    case TypeBytes.SYNCHRONIZATION_MESSAGE_TYPE_SERVERNOTIFYBTREETRANSITION:
                        if(SynchronizationMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = SynchronizationMessage.parseServerNotifyBTreeTransitionMessage(byteBuffer,pool);
                        }
                        break;
                    case TypeBytes.SYNCHRONIZATION_MESSAGE_TYPE_ATTACHTREE:
                        if(SynchronizationMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = SynchronizationMessage.parseAttachTreeMessage(byteBuffer,pool);
                        }
                        break;
                    case TypeBytes.SYNCHRONIZATION_MESSAGE_TYPE_DETATCHTREE:
                        if(SynchronizationMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = SynchronizationMessage.parseDetatchTreeMessage(byteBuffer,pool);
                        }
                        break;
                    case TypeBytes.SYNCHRONIZATION_MESSAGE_TYPE_LOADSCENE:
                        if(SynchronizationMessage.canParseMessage(byteBuffer,secondByte)){
                            rVal = SynchronizationMessage.parseLoadSceneMessage(byteBuffer,pool);
                        }
                        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,pool);
                        }
                        break;
                }
                break;
            }
        }
        return rVal;
    }
    
    /**
     * Checks if this message is serialized or not
     * @return true if it is serialized, false otherwise
     */
    public boolean isSerialized(){
        return serialized;
    }
    
    /**
     * Serializes the message
     */
    abstract void serialize();
    
}

