package com.n3plugins.guardiansrift;

final class GuardiansRiftController
{
    private final GuardiansRiftRuntime runtime;
    private GuardiansRiftSnapshot snapshot = GuardiansRiftSnapshot.idle();
    private boolean running;
    private boolean startingRun;

    GuardiansRiftController(GuardiansRiftRuntime runtime)
    {
        this.runtime = runtime;
    }

    void start()
    {
        running = true;
        snapshot = snapshot.toBuilder()
                .running(true)
                .state(GuardiansRiftState.WAITING_FOR_BOOTSTRAP)
                .status("Starting")
                .stopReason(null)
                .build();
    }

    void stop(String reason)
    {
        running = false;
        startingRun = false;
        snapshot = snapshot.toBuilder()
                .running(false)
                .state(GuardiansRiftState.STOPPED)
                .status("Stopped")
                .stopReason(reason)
                .build();
    }

    void toggle()
    {
        if (running)
        {
            stop("manual");
        }
        else
        {
            start();
        }
    }

    GuardiansRiftSnapshot snapshot()
    {
        return snapshot;
    }

    GuardiansRiftSnapshot onGameTick(boolean bootstrapReady)
    {
        if (!running)
        {
            return snapshot;
        }
        if (!bootstrapReady)
        {
            return update(GuardiansRiftState.WAITING_FOR_BOOTSTRAP, "Waiting for bootstrap");
        }

        GuardiansRiftSnapshot current = runtime.snapshot();
        if (!current.isLoggedIn())
        {
            return update(GuardiansRiftState.WAITING_FOR_LOGIN, "Waiting for login");
        }
        if (!current.hasPickaxe())
        {
            stop("No pickaxe found");
            return snapshot;
        }
        if (current.isUsePouches() && !current.isPouchRepairReady())
        {
            stop("Essence pouch repair requirements missing");
            return snapshot;
        }

        GuardiansRiftState state = determineState(current);
        perform(state);
        return update(state, statusFor(state));
    }

    private GuardiansRiftState determineState(GuardiansRiftSnapshot current)
    {
        if (!current.isGameStarted() && !current.isInAltar() && (current.isBreakDue() || current.isBreakActive()))
        {
            return GuardiansRiftState.BREAK;
        }
        if (current.isGameBusy())
        {
            return GuardiansRiftState.GAME_BUSY;
        }
        if (current.isOutsideBarrier())
        {
            return GuardiansRiftState.ENTER_GAME;
        }
        if (current.isDegradedPouchPresent())
        {
            return GuardiansRiftState.REPAIR_POUCH;
        }
        if (!current.isGameStarted())
        {
            return pregameState(current);
        }
        if (!current.hasFirstPortalSpawned() && needsMoreStartingFragments(current))
        {
            return preFirstPortalState(current);
        }
        if (current.isGameEnding())
        {
            return gameEndingState(current);
        }
        if (current.isInLargeMine())
        {
            return GuardiansRiftState.LEAVE_EAST_MINE;
        }
        if (current.isInHugeMine())
        {
            if (current.isInventoryFull())
            {
                return GuardiansRiftState.ENTER_PORTAL;
            }
            return current.isMining() ? GuardiansRiftState.MINING : GuardiansRiftState.MINE_HUGE_GUARDIANS;
        }
        if (current.isInAltar())
        {
            startingRun = false;
            return current.getGuardianEssence() > 0 || current.getPouchEssence() > 0 || current.isFullPouchAvailable()
                    ? GuardiansRiftState.CRAFT_RUNES
                    : GuardiansRiftState.LEAVE_ALTAR;
        }
        if (current.isPortalSpawned() && (current.isEmptyPouchAvailable() || current.getInventoryFreeSlots() > 15))
        {
            return GuardiansRiftState.ENTER_PORTAL;
        }
        if (current.isInventoryFull() || startingRun)
        {
            startingRun = true;
            if (current.isPowerEssencePresent())
            {
                return GuardiansRiftState.POWER_GUARDIAN;
            }
            if (current.isPowerCellPresent())
            {
                return GuardiansRiftState.USE_CELL;
            }
            return GuardiansRiftState.ENTER_ALTAR;
        }
        if (current.isCraftingEssence())
        {
            return GuardiansRiftState.CRAFTING_ESSENCE;
        }
        if (current.getCellCount() == 0)
        {
            return GuardiansRiftState.GET_CELLS;
        }
        if (current.isInventoryRunesPresent())
        {
            return current.isDropRunes() || current.isDroppableRunesPresent()
                    ? GuardiansRiftState.DROP_RUNES
                    : GuardiansRiftState.DEPOSIT_RUNES;
        }
        if (current.getFragmentCount() < current.getNeededFragments())
        {
            return current.isMining() ? GuardiansRiftState.MINING : GuardiansRiftState.MINE_REGULAR_GUARDIANS;
        }
        return GuardiansRiftState.CRAFT_ESSENCE;
    }

    private GuardiansRiftState pregameState(GuardiansRiftSnapshot current)
    {
        if (current.isInHugeMine())
        {
            return GuardiansRiftState.ENTER_PORTAL;
        }
        if (current.isInLargeMine())
        {
            return GuardiansRiftState.WAITING_FOR_GAME;
        }
        if (current.isInAltar())
        {
            return current.getGuardianEssence() > 0 || current.getPouchEssence() > 0
                    ? GuardiansRiftState.CRAFT_RUNES
                    : GuardiansRiftState.LEAVE_ALTAR;
        }
        if (current.isInventoryRunesPresent())
        {
            return current.isDropRunes() || current.isDroppableRunesPresent()
                    ? GuardiansRiftState.DROP_RUNES
                    : GuardiansRiftState.DEPOSIT_RUNES;
        }
        if (current.getCellCount() < 10)
        {
            return GuardiansRiftState.GET_CELLS;
        }
        return GuardiansRiftState.MOVE_TO_EAST_MINE;
    }

    private GuardiansRiftState preFirstPortalState(GuardiansRiftSnapshot current)
    {
        if (current.isInAltar())
        {
            return GuardiansRiftState.LEAVE_ALTAR;
        }
        if (current.isInHugeMine())
        {
            if (current.isInventoryFull())
            {
                return GuardiansRiftState.ENTER_PORTAL;
            }
            return current.isMining() ? GuardiansRiftState.MINING : GuardiansRiftState.MINE_HUGE_GUARDIANS;
        }
        if (current.isInLargeMine())
        {
            return current.isMining() ? GuardiansRiftState.MINING : GuardiansRiftState.MINE_LARGE_GUARDIANS;
        }
        if (current.getCellCount() < 10)
        {
            return GuardiansRiftState.GET_CELLS;
        }
        return GuardiansRiftState.MOVE_TO_EAST_MINE;
    }

    private GuardiansRiftState gameEndingState(GuardiansRiftSnapshot current)
    {
        if (current.isInAltar())
        {
            return current.getGuardianEssence() > 0 || current.getPouchEssence() > 0
                    ? GuardiansRiftState.CRAFT_RUNES
                    : GuardiansRiftState.LEAVE_ALTAR;
        }
        if (current.isInHugeMine())
        {
            return GuardiansRiftState.ENTER_PORTAL;
        }
        if (current.isPowerEssencePresent())
        {
            return GuardiansRiftState.POWER_GUARDIAN;
        }
        if (current.isPowerCellPresent())
        {
            return GuardiansRiftState.USE_CELL;
        }
        if (current.getGuardianEssence() + current.getPouchEssence() > 20)
        {
            return GuardiansRiftState.ENTER_ALTAR;
        }
        if (current.isInventoryRunesPresent())
        {
            return current.isDropRunes() || current.isDroppableRunesPresent()
                    ? GuardiansRiftState.DROP_RUNES
                    : GuardiansRiftState.DEPOSIT_RUNES;
        }
        return GuardiansRiftState.CRAFT_ESSENCE;
    }

    private boolean needsMoreStartingFragments(GuardiansRiftSnapshot current)
    {
        return current.getStartingFragments() > 0 && current.getFragmentCount() <= current.getStartingFragments();
    }

    private void perform(GuardiansRiftState state)
    {
        switch (state)
        {
            case BREAK:
                runtime.startBreak();
                break;
            case GAME_BUSY:
                runtime.gameBusy();
                break;
            case ENTER_GAME:
                runtime.enterGame();
                break;
            case REPAIR_POUCH:
                runtime.repairPouch();
                break;
            case WAITING_FOR_GAME:
                runtime.waitForGame();
                break;
            case GET_CELLS:
                runtime.getCells();
                break;
            case MOVE_TO_EAST_MINE:
                runtime.moveToEastMine();
                break;
            case LEAVE_EAST_MINE:
                runtime.leaveEastMine();
                break;
            case ENTER_PORTAL:
                runtime.enterPortal();
                break;
            case MINE_HUGE_GUARDIANS:
                runtime.mineHugeGuardians();
                break;
            case MINE_LARGE_GUARDIANS:
                runtime.mineLargeGuardians();
                break;
            case MINE_REGULAR_GUARDIANS:
                runtime.mineRegularGuardians();
                break;
            case CRAFT_ESSENCE:
                runtime.craftEssence();
                break;
            case FILL_POUCHES:
                runtime.fillPouches();
                break;
            case POWER_GUARDIAN:
                runtime.powerGuardian();
                break;
            case USE_CELL:
                runtime.useCell();
                break;
            case ENTER_ALTAR:
                runtime.enterAltar();
                break;
            case CRAFT_RUNES:
                runtime.craftRunes();
                break;
            case LEAVE_ALTAR:
                runtime.leaveAltar();
                break;
            case DEPOSIT_RUNES:
                runtime.depositRunes();
                break;
            case DROP_RUNES:
                runtime.dropRunes();
                break;
            default:
                break;
        }
    }

    private GuardiansRiftSnapshot update(GuardiansRiftState state, String status)
    {
        snapshot = snapshot.toBuilder()
                .running(running)
                .state(state)
                .status(status)
                .stopReason(null)
                .build();
        return snapshot;
    }

    private static String statusFor(GuardiansRiftState state)
    {
        return state.name();
    }
}
