package net.md_5.bungee.forge;

import java.util.Map;
import net.md_5.bungee.ServerConnector;
import net.md_5.bungee.UserConnection;
import net.md_5.bungee.protocol.packet.PluginMessage;

/**
 * Handshake sequence manager for the Bungee - Forge Client (Upstream) link.
 * Modelled after the Forge implementation. See
 * https://github.com/MinecraftForge/FML/blob/master/src/main/java/cpw/mods/fml/common/network/handshake/FMLHandshakeClientState.java
 */
enum ForgeClientHandshakeState implements IForgeClientPacketHandler<ForgeClientHandshakeState>
{

    /**
     * Initiated at the start of a client handshake. This is a special case
     * where we don't want to use a {@link PluginMessage}, we're just sending
     * stuff out here.
     *
     * Transitions into the HELLO state upon completion.
     *
     * Requires: {@link UserConnection}.
     */
    START
    {
        @Override
        public ForgeClientHandshakeState handle(PluginMessage message, UserConnection con)
        {
            ForgeLogger.logClient( ForgeLogger.LogDirection.RECEIVED, this.name(), message );
            con.unsafe().sendPacket( message );
            con.getForgeClientHandler().setState( HELLO );
            return HELLO;
        }

        @Override
        public ForgeClientHandshakeState send(PluginMessage message, UserConnection con)
        {
            return HELLO;
        }
    },
    /**
     * Waiting to receive a client HELLO and the mod list. Upon receiving the
     * mod list, return the mod list of the server.
     *
     * We will be stuck in this state if we don't have a forge client. This is
     * OK.
     *
     * Transitions to the WAITINGCACK state upon completion.
     *
     * Requires:
     * {@link PluginMessage}, {@link UserConnection}, {@link ServerConnector}
     */
    HELLO
    {
        @Override
        public ForgeClientHandshakeState handle(PluginMessage message, UserConnection con)
        {
            ForgeLogger.logClient( ForgeLogger.LogDirection.RECEIVED, this.name(), message );
            // Server Hello.
            if ( message.getData()[0] == 0 )
            {
                con.unsafe().sendPacket( message );
            }

            return this;
        }

        @Override
        public ForgeClientHandshakeState send(PluginMessage message, UserConnection con)
        {
            // Client Hello.
            if ( message.getData()[0] == 1 )
            {
                return this;
            }

            // Mod list.
            if ( message.getData()[0] == 2 )
            {
                if ( con.getForgeClientHandler().getClientModList() == null )
                {
                    // This is the first Forge connection - so get the mods now.
                    // Once we've done it, no point doing it again.
                    Map<String, String> clientModList = ForgeUtils.readModList( message );
                    con.getForgeClientHandler().setClientModList( clientModList );
                }

                return WAITINGSERVERDATA;
            }

            return this;
        }

    },
    WAITINGSERVERDATA
    {

        @Override
        public ForgeClientHandshakeState handle(PluginMessage message, UserConnection con)
        {
            ForgeLogger.logClient( ForgeLogger.LogDirection.RECEIVED, this.name(), message );
            // Mod list.
            if ( message.getData()[0] == 2 )
            {
                con.unsafe().sendPacket( message );
            }

            return this;
        }

        @Override
        public ForgeClientHandshakeState send(PluginMessage message, UserConnection con)
        {
            // ACK
            return WAITINGSERVERCOMPLETE;
        }
    },
    WAITINGSERVERCOMPLETE
    {

        @Override
        public ForgeClientHandshakeState handle(PluginMessage message, UserConnection con)
        {
            ForgeLogger.logClient( ForgeLogger.LogDirection.RECEIVED, this.name(), message );
            // Mod ID's.
            if ( message.getData()[0] == 3 )
            {
                con.unsafe().sendPacket( message );
                return this;
            }

            con.unsafe().sendPacket( message ); // pass everything else
            return this;
        }

        @Override
        public ForgeClientHandshakeState send(PluginMessage message, UserConnection con)
        {
            // Send ACK.
            return PENDINGCOMPLETE;
        }
    },
    PENDINGCOMPLETE
    {

        @Override
        public ForgeClientHandshakeState handle(PluginMessage message, UserConnection con)
        {
            // Ack.
            if ( message.getData()[0] == -1 )
            {
                ForgeLogger.logClient( ForgeLogger.LogDirection.RECEIVED, this.name(), message );
                con.unsafe().sendPacket( message );
            }

            return this;
        }

        @Override
        public ForgeClientHandshakeState send(PluginMessage message, UserConnection con)
        {
            // Send an ACK
            return COMPLETE;
        }
    },
    COMPLETE
    {

        @Override
        public ForgeClientHandshakeState handle(PluginMessage message, UserConnection con)
        {
            // Ack.
            if ( message.getData()[0] == -1 )
            {
                ForgeLogger.logClient( ForgeLogger.LogDirection.RECEIVED, this.name(), message );
                con.unsafe().sendPacket( message );
            }

            return this;
        }

        @Override
        public ForgeClientHandshakeState send(PluginMessage message, UserConnection con)
        {
            return DONE;
        }
    },
    /**
     * Handshake has been completed. Ignores any future handshake packets.
     */
    DONE
    {

        @Override
        public ForgeClientHandshakeState handle(PluginMessage message, UserConnection con)
        {
            ForgeLogger.logClient( ForgeLogger.LogDirection.RECEIVED, this.name(), message );
            return this;
        }

        @Override
        public ForgeClientHandshakeState send(PluginMessage message, UserConnection con)
        {
            return this;
        }
    }
}
