package com.n3plugins.guardiansrift;

import com.google.inject.Provides;
import com.n3plugins.PacketUtils.InputLockService;
import com.n3plugins.PacketUtils.PacketUtilsPlugin;
import com.n3plugins.breakhandler.BreakHandler;
import net.runelite.api.ChatMessageType;
import net.runelite.api.Client;
import net.runelite.api.events.ChatMessage;
import net.runelite.api.events.GameTick;
import net.runelite.client.config.ConfigManager;
import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.input.KeyManager;
import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor;
import net.runelite.client.ui.overlay.OverlayManager;
import net.runelite.client.util.HotkeyListener;

import javax.inject.Inject;

@PluginDescriptor(
        name = "<html><font color='#FFBF00'>[n3]</font> Guardians of the Rift</html>",
        description = "n3-native Guardians of the Rift automation.",
        enabledByDefault = false,
        tags = {"n3", "guardians", "rift", "runecraft", "automation"}
)
public class GuardiansRiftPlugin extends Plugin
{
    @Inject
    private Client client;

    @Inject
    private GuardiansRiftConfig config;

    @Inject
    private OverlayManager overlayManager;

    @Inject
    private GuardiansRiftOverlay overlay;

    @Inject
    private KeyManager keyManager;

    @Inject
    private InputLockService inputLockService;

    @Inject
    private BreakHandler breakHandler;

    private GuardiansRiftController controller;
    private RuneLiteGuardiansRiftRuntime runtime;
    private volatile GuardiansRiftSnapshot snapshot = GuardiansRiftSnapshot.idle();
    private boolean breakTrackingActive;

    private final HotkeyListener toggleListener = new HotkeyListener(() -> config.hotkey())
    {
        @Override
        public void hotkeyPressed()
        {
            if (controller != null)
            {
                controller.toggle();
                snapshot = controller.snapshot();
                setBreakTrackingActive(snapshot.isRunning());
            }
        }
    };

    @Provides
    GuardiansRiftConfig provideConfig(ConfigManager configManager)
    {
        return configManager.getConfig(GuardiansRiftConfig.class);
    }

    @Override
    protected void startUp()
    {
        runtime = new RuneLiteGuardiansRiftRuntime(client, config, this::shouldBreak, this::isBreakActive);
        controller = new GuardiansRiftController(runtime);
        snapshot = controller.snapshot();
        if (breakHandler != null)
        {
            breakHandler.registerPlugin(this);
        }
        overlayManager.add(overlay);
        keyManager.registerKeyListener(toggleListener);
    }

    @Override
    protected void shutDown()
    {
        setBreakTrackingActive(false);
        if (breakHandler != null)
        {
            breakHandler.unregisterPlugin(this);
        }
        inputLockService.release(this);
        PacketUtilsPlugin.releaseAccountBootstrap(this);
        keyManager.unregisterKeyListener(toggleListener);
        overlayManager.remove(overlay);
        controller = null;
        runtime = null;
        snapshot = GuardiansRiftSnapshot.idle();
    }

    @Subscribe
    public void onGameTick(GameTick event)
    {
        if (controller == null)
        {
            setBreakTrackingActive(false);
            inputLockService.release(this);
            PacketUtilsPlugin.releaseAccountBootstrap(this);
            return;
        }

        GuardiansRiftSnapshot current = controller.snapshot();
        setBreakTrackingActive(current.isRunning());
        boolean bootstrapReady = true;
        if (current.isRunning())
        {
            bootstrapReady = PacketUtilsPlugin.requireAccountBootstrap(this);
        }
        else
        {
            PacketUtilsPlugin.releaseAccountBootstrap(this);
        }
        if (current.isRunning() && bootstrapReady && handleBreakPause())
        {
            inputLockService.release(this);
            snapshot = current;
            return;
        }

        snapshot = controller.onGameTick(bootstrapReady);
        setBreakTrackingActive(snapshot.isRunning());
        if (shouldLockInput(snapshot))
        {
            inputLockService.request(this, "Guardians of the Rift");
        }
        else
        {
            inputLockService.release(this);
        }
    }

    @Subscribe
    public void onChatMessage(ChatMessage event)
    {
        if (runtime == null || event.getType() != ChatMessageType.GAMEMESSAGE && event.getType() != ChatMessageType.SPAM)
        {
            return;
        }
        runtime.onGameMessage(event.getMessage());
    }

    GuardiansRiftSnapshot snapshot()
    {
        return snapshot;
    }

    private boolean shouldLockInput(GuardiansRiftSnapshot snap)
    {
        return snap != null
                && snap.isRunning()
                && snap.getState() != GuardiansRiftState.STOPPED
                && snap.getState() != GuardiansRiftState.WAITING_FOR_BOOTSTRAP
                && snap.getState() != GuardiansRiftState.WAITING_FOR_LOGIN
                && snap.getState() != GuardiansRiftState.BREAK;
    }

    private boolean handleBreakPause()
    {
        if (breakHandler == null)
        {
            return false;
        }
        boolean shouldBreak = breakHandler.shouldBreak(this);
        boolean breakActive = breakHandler.isBreakActive(this);
        if (shouldBreak && !breakActive)
        {
            breakHandler.startBreak(this);
            breakActive = true;
        }
        return shouldBreak || breakActive;
    }

    private boolean shouldBreak()
    {
        return breakHandler != null && breakHandler.shouldBreak(this);
    }

    private boolean isBreakActive()
    {
        return breakHandler != null && breakHandler.isBreakActive(this);
    }

    private void setBreakTrackingActive(boolean active)
    {
        if (breakHandler == null || breakTrackingActive == active)
        {
            return;
        }
        if (active)
        {
            breakHandler.startPlugin(this);
        }
        else
        {
            breakHandler.stopPlugin(this);
        }
        breakTrackingActive = active;
    }
}
