package me.redteapot.redmacros;

import com.mojang.realmsclient.dto.RealmsServer;
import com.mumfrey.liteloader.core.LiteLoader;
import com.mumfrey.liteloader.core.LiteLoaderEventBroker;
import me.redteapot.redmacros.component.IComponent;
import me.redteapot.redmacros.component.IComponentTickable;
import me.redteapot.redmacros.component.KeyboardInputComponent;
import me.redteapot.redmacros.component.PlayerStateComponent;
import me.redteapot.redmacros.lua.LuaThread;
import me.redteapot.redmacros.lua.events.LuaEvent;
import me.redteapot.redmacros.lua.libs.ChatLib;
import me.redteapot.redmacros.util.TextUtil;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ServerData;
import net.minecraft.network.INetHandler;
import net.minecraft.network.play.server.SPacketJoinGame;
import net.minecraft.util.text.ITextComponent;
import org.luaj.vm2.LuaTable;
import org.luaj.vm2.LuaValue;

import java.util.HashMap;

/**
 * RedMacros mod core singleton class
 */
public class Core extends ComponentSystem {
    /**
     * Core singleton instance
     */
    private static Core instance;

    /**
     * Get Core singleton instance
     * @return Instance
     */
    public static Core getInstance() {
        if(instance == null)
            instance = new Core();

        return instance;
    }

    /**
     * Runnable holding Luaj interpreter
     */
    private LuaThread luaThread;

    /**
     * Create new Core instance
     */
    private Core() {
        luaThread = null;
    }

    /**
     * Init component list
     */
    private void initComponents() {
        addComponent(new KeyboardInputComponent(LiteModRedMacros.macroKeyBinding));
        addComponent(new PlayerStateComponent(Minecraft.getMinecraft().player));
    }

    /**
     * Enqueue event into event system
     *
     * @param type Event type
     * @param parameters Event parameters table
     */
    public void enqueueEvent(String type, LuaTable parameters) {
        if(luaThread != null) {
            if(parameters == null)
                parameters = LuaValue.tableOf();

            luaThread.enqueueEvent(new LuaEvent(type, parameters));
        } else {
            System.err.println("Tried to enqueue event when LuaRunnable is null!");
        }
    }

    public boolean setInboundChatFilter(LuaValue filter) {
        if(filter.isboolean()) {
            if(!filter.checkboolean()) {
                luaThread.setInboundChatFilter(null);
                return true;
            } else {
                return false;
            }
        }

        luaThread.setInboundChatFilter(filter);

        return true;
    }

    public boolean setOutboundChatFilter(LuaValue filter) {
        if(filter.isboolean()) {
            if(!filter.checkboolean()) {
                luaThread.setOutboundChatFilter(null);
                return true;
            } else {
                return false;
            }
        }

        luaThread.setOutboundChatFilter(filter);

        return true;
    }

    /**
     * OnJoinGame event handler
     *
     * <p>Starts running world/server macro files if there are any.</p>
     *
     * @param netHandler Network handler
     * @param joinGamePacket Join game packet
     * @param serverData Server data
     * @param realmsServer Realms server
     */
    public void onJoinGame(INetHandler netHandler, SPacketJoinGame joinGamePacket, ServerData serverData, RealmsServer realmsServer) {
        String macroFileName;
        if(LiteLoader.getGameEngine().isSinglePlayer()) {
            macroFileName = MacrosFilesUtil.getMacroFilePathSP(Minecraft.getMinecraft().getIntegratedServer().getWorldName());
        } else {
            macroFileName = MacrosFilesUtil.getMacroFilePathMP(serverData.serverIP);
        }

        initComponents();

        if(macroFileName != null) {
            luaThread = new LuaThread(macroFileName);
            luaThread.start();
        }
    }

    /**
     * OnDisconnectGame event handler
     *
     * <p>Kills Luaj thread if it is running and cleans resources.</p>
     */
    public void onDisconnectGame() {
        if(luaThread != null && !luaThread.isInterrupted()) {
            luaThread.interrupt();
            luaThread.dispose();
        }

        luaThread = null;

        clearComponents();
    }

    public boolean onSendChatMessage(String message) {
        boolean isPassed = true;

        if(luaThread != null)
            isPassed = luaThread.runOutboundChatFilter(message);

        if(isPassed) {
            LuaTable params = LuaValue.tableOf();
            params.set("message", message);
            enqueueEvent(ChatLib.EVT_SEND, params);
        }

        return isPassed;
    }

    public boolean onChat(ITextComponent chat, LiteLoaderEventBroker.ReturnValue<ITextComponent> newMessage) {
        boolean isPassed = true;

        ITextComponent message = chat;

        if(luaThread != null) {
            isPassed = luaThread.runInboundChatFilter(chat, newMessage);

            if(isPassed && newMessage.isSet())
                message = newMessage.get();
        }

        if(isPassed) {
            LuaTable params = LuaValue.tableOf();
            params.set("messageComponent", TextUtil.textComponentToLuaTable(message));
            params.set("unformattedText", message.getUnformattedText());
            enqueueEvent(ChatLib.EVT_RECEIVE, params);
        }

        return isPassed;
    }

    public void onTick(boolean inGame) {
        for(IComponent component : components.values()) {
            if(component instanceof IComponentTickable)
                ((IComponentTickable) component).onTick(inGame);
        }
    }
}
