package eu.jonahbauer.wizard.core.machine;

import eu.jonahbauer.wizard.common.machine.TimeoutState;
import eu.jonahbauer.wizard.common.messages.player.PlayerMessage;
import eu.jonahbauer.wizard.core.machine.states.GameData;
import lombok.Getter;
import org.jetbrains.annotations.Unmodifiable;

import java.util.List;
import java.util.Optional;
import java.util.UUID;

import static eu.jonahbauer.wizard.core.machine.states.GameData.CURRENT_PLAYER;
import static eu.jonahbauer.wizard.core.machine.states.GameData.PLAYERS;

@Unmodifiable
public abstract class GameState implements TimeoutState<GameState, Game> {
    @Getter
    private final GameData data;

    public GameState(GameData data) {
        this.data = data.clean();
    }

    //<editor-fold desc="Utility Methods" defaultstate="collapsed">
    protected final Optional<GameState> timeout(Game game) {
        game.timeout(this, getTimeout(game, false));
        return Optional.empty();
    }

    protected final Optional<GameState> syncTimeout(Game game) {
        game.timeout(this, getSyncTimeout(game, false));
        return Optional.empty();
    }

    protected final Optional<GameState> transition(GameState state) {
        return Optional.of(state);
    }

    protected final long getTimeout(Game game, boolean absolute) {
        return (absolute ? System.currentTimeMillis() : 0) + game.getConfig().timeout();
    }

    protected final long getSyncTimeout(Game game, boolean absolute) {
        return (absolute ? System.currentTimeMillis() : 0) + game.getConfig().syncTimeout();
    }
    //</editor-fold>

    public Optional<GameState> onMessage(Game game, UUID player, PlayerMessage message) {
        throw new IllegalStateException("You cannot do that right now.");
    }

    public Optional<GameState> start(Game game, List<UUID> players) {
        throw new IllegalStateException("Game has already started.");
    }


    @Override
    public String toString() {
        return getClass().getSimpleName();
    }

    //<editor-fold desc="GameData accessors" defaultstate="collapsed">
    public <T> T get(GameData.Key<T> key) {
        return getData().get(key);
    }

    public int getPlayerCount() {
        return get(PLAYERS).size();
    }

    public UUID getPlayer(int index) {
        return get(PLAYERS).get(index);
    }

    public UUID getNextPlayer() {
        return getNextPlayer(get(CURRENT_PLAYER));
    }

    public UUID getNextPlayer(UUID player) {
        var players = get(PLAYERS);
        var index = players.indexOf(player);
        if (index == -1) {
            throw new AssertionError("Player is not a valid player.");
        } else if (index == players.size() - 1) {
            return players.get(0);
        } else {
            return players.get(index + 1);
        }

    }
    //</editor-fold>
}
