package tc.oc.commons.bukkit.teleport;

import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import javax.annotation.Nullable;
import javax.inject.Inject;

import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.SettableFuture;
import net.md_5.bungee.api.ChatColor;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.metadata.FixedMetadataValue;
import org.bukkit.metadata.MetadataValue;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.messaging.Messenger;
import tc.oc.api.docs.Server;
import tc.oc.api.docs.virtual.ServerDoc;
import tc.oc.commons.core.plugin.PluginFacet;

public class PlayerServerChanger implements PluginFacet, Listener {

    private static final String PLUGIN_CHANNEL = "BungeeCord";
    private static final String METADATA_KEY = "QuitFuture";

    private final Plugin plugin;
    private final Messenger messenger;
    private final Server localServer;

    @Inject PlayerServerChanger(Plugin plugin, Messenger messenger, Server localServer) {
        this.plugin = plugin;
        this.messenger = messenger;
        this.localServer = localServer;
    }

    @Override
    public void enable() {
        messenger.registerOutgoingPluginChannel(plugin, PLUGIN_CHANNEL);
    }

    @Override
    public void disable() {
        messenger.unregisterOutgoingPluginChannel(plugin, PLUGIN_CHANNEL);
    }

    private ListenableFuture<?> quitFuture(Player player) {
        if(player.isOnline()) {
            final SettableFuture<?> future = SettableFuture.create();
            player.setMetadata(METADATA_KEY, new FixedMetadataValue(plugin, future));
            return future;
        } else {
            return Futures.immediateFuture(null);
        }
    }

    @EventHandler(priority = EventPriority.LOWEST)
    public void onQuit(PlayerQuitEvent event) {
        final MetadataValue future = event.getPlayer().getMetadata(METADATA_KEY, plugin);
        if(future != null) {
            ((SettableFuture) future.value()).set(null);
        }
    }

    public ListenableFuture<?> sendPlayerToLobby(Player player, boolean quiet) {
        return sendPlayerToServer(player, (String) null, quiet);
    }

    public ListenableFuture<?> sendPlayerToServer(Player player, @Nullable ServerDoc.BungeeName server, boolean quiet) {
        return sendPlayerToServer(player, server == null ? null : server.bungee_name(), quiet);
    }

    public ListenableFuture<?> sendPlayerToServer(Player player, @Nullable String bungeeName, boolean quiet) {
        if(localServer.bungee_name().equals(bungeeName) || (localServer.role() == ServerDoc.Role.LOBBY && bungeeName == null)) {
            return Futures.immediateFuture(null);
        }

        final ByteArrayOutputStream message = new ByteArrayOutputStream();
        final DataOutputStream out = new DataOutputStream(message);

        try {
            out.writeUTF(quiet ? "ConnectQuiet" : "Connect");
            out.writeUTF(bungeeName == null ? "default" : bungeeName);
        } catch(IOException e) {
            return Futures.immediateFailedFuture(e);
        }

        player.sendPluginMessage(plugin, PLUGIN_CHANNEL, message.toByteArray());
        return quitFuture(player);
    }

    public ListenableFuture<?> kickPlayer(Player player, String message) {
        // Magic color sequence signals Bungee to disconnect the player
        player.kickPlayer(ChatColor.BLACK.toString() + ChatColor.RED + ChatColor.RESET + message);
        return quitFuture(player);
    }
}