package eu.jonahbauer.wizard.core.machine;

import eu.jonahbauer.wizard.core.machine.states.GameData;
import eu.jonahbauer.wizard.core.machine.states.GameState;
import eu.jonahbauer.wizard.core.machine.states.game.Starting;
import eu.jonahbauer.wizard.core.messages.Observer;
import eu.jonahbauer.wizard.common.messages.observer.ObserverMessage;
import eu.jonahbauer.wizard.common.messages.observer.StateMessage;
import eu.jonahbauer.wizard.common.messages.player.PlayerMessage;
import eu.jonahbauer.wizard.core.model.GameConfiguration;
import eu.jonahbauer.wizard.core.util.Util;
import lombok.Getter;

import java.util.List;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;

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

public final class Game extends Context<GameState, Game> {
    @Getter
    private final Random random;
    @Getter
    private final GameConfiguration config;
    private final Observer observer;

    public Game(GameConfiguration config, Observer observer) {
        this.random = new Random();
        this.config = config;
        this.observer = observer;
    }

    public Game(long seed, GameConfiguration config, Observer observer) {
        this.random = new Random(seed);
        this.config = config;
        this.observer = observer;
    }

    public void start(List<UUID> players) {
        start(new Starting(GameData.EMPTY.with(PLAYERS, List.copyOf(players))));
    }

    public void resume(GameState state) {
        start(state);
    }

    public CompletableFuture<Void> onMessage(UUID player, PlayerMessage message) {
        return submit(() -> {
            if (state != null) {
                state.onMessage(this, player, message);
            }
        });
    }

    @Override
    protected void onTransition(GameState from, GameState to) {
        notify(new StateMessage(to != null ? Util.toSnakeCase(to.getClass().getSimpleName()) : "null"));
    }

    public void notify(ObserverMessage message) {
        try {
            observer.notify(message);
        } catch (Throwable t) {
            t.printStackTrace();
        }
    }

    public void notify(UUID player, ObserverMessage message) {
        try {
            observer.notify(player, message);
        } catch (Throwable t) {
            t.printStackTrace();
        }
    }
}
