package eu.jonahbauer.wizard.client.libgdx.screens;

import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.ui.*;
import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener;
import com.badlogic.gdx.utils.Array;
import eu.jonahbauer.wizard.client.libgdx.WizardGame;
import eu.jonahbauer.wizard.client.libgdx.listeners.KeyboardFocusManager;
import eu.jonahbauer.wizard.client.libgdx.listeners.ResetErrorListener;
import eu.jonahbauer.wizard.client.libgdx.listeners.SelectBoxListener;
import eu.jonahbauer.wizard.client.libgdx.state.Lobby;
import eu.jonahbauer.wizard.common.model.Configuration;
import lombok.extern.log4j.Log4j2;

@Log4j2
public class CreateGameScreen extends MenuScreen {
    private static final int MAX_SESSION_NAME_LENGTH = 20;
    
    private TextButton buttonBack;
    private TextButton buttonContinue;

    private TextField sessionName;
    private TextField playerName;
    private TextField timeOut;
    private SelectBox<String> configurations;

    private String oldPlayerName;

    private final ChangeListener listener = new ChangeListener() {
        @Override
        public void changed(ChangeEvent event, Actor actor) {
            if (actor == buttonBack) {
                game.getClient().execute(Lobby.class, Lobby::showListScreen);
            } else if (actor == buttonContinue) {
                create();
            }
        }
    };
    
    public CreateGameScreen(WizardGame game) {
        super(game);
        oldPlayerName = game.storage.playerName;
    }
    
    @Override
    public void show() {
        super.show();

        buttonBack = new TextButton(messages.get("menu.create_game.back"), skin);
        buttonBack.addListener(listener);
        getButtonGroup().addActor(buttonBack);

        buttonContinue = new TextButton(messages.get("menu.create_game.create"), skin);
        buttonContinue.addListener(listener);
        getButtonGroup().addActor(buttonContinue);

        var errorListener = new ResetErrorListener(skin);

        sessionName = new TextField("", skin);
        sessionName.setPosition(WizardGame.WIDTH * 0.3f, WizardGame.HEIGHT * 0.5f);
        sessionName.setSize(0.4f * WizardGame.WIDTH, 64);
        sessionName.setMaxLength(MAX_SESSION_NAME_LENGTH);
        sessionName.addListener(errorListener);
        sessionName.setProgrammaticChangeEvents(true);

        playerName = new TextField(oldPlayerName, skin);
        playerName.setPosition(WizardGame.WIDTH * 0.3f, WizardGame.HEIGHT * 0.45f);
        playerName.setSize(0.4f * WizardGame.WIDTH, 64);
        playerName.addListener(errorListener);
        var playerNameListener = new ChangeListener() {
            private final String format = messages.get("menu.create_game.session_name.default");

            @Override
            public void changed(ChangeEvent event, Actor actor) {
                var player = playerName.getText();
                var session = sessionName.getText();
                var previousSuggestion = format.formatted(oldPlayerName);

                boolean shouldApplySuggestion = session.isEmpty()
                        || previousSuggestion.startsWith(session) && (session.length() == MAX_SESSION_NAME_LENGTH || session.length() == previousSuggestion.length());
                if (shouldApplySuggestion) {
                    if (player.isEmpty()) {
                        sessionName.setText("");
                    } else {
                        sessionName.setText(format.formatted(player));
                    }
                }

                game.storage.playerName = player;
                oldPlayerName = player;
            }
        };
        playerName.addListener(playerNameListener);
        playerNameListener.changed(null, null);

        timeOut = new TextField("", skin);
        timeOut.setPosition(WizardGame.WIDTH * 0.3f, WizardGame.HEIGHT * 0.4f);
        timeOut.setSize(0.4f * WizardGame.WIDTH, 64);
        timeOut.setTextFieldFilter(new TextField.TextFieldFilter.DigitsOnlyFilter());
        timeOut.addListener(errorListener);
        
        configurations = new SelectBox<>(skin);
        configurations.setSize(400, 64);
        configurations.setPosition(WizardGame.WIDTH * 0.3f, WizardGame.HEIGHT * 0.3f);
        configurations.addListener(errorListener);
        configurations.addListener(new SelectBoxListener(configurations));
        
        Array<String> values = new Array<>();
        for (Configuration value : Configuration.values()) {
            values.add(value.toString());
        }
        configurations.setItems(values);
    
        var contentTable = new Table(skin).center().left();
        contentTable.columnDefaults(0).growX().width(0.4f * WizardGame.WIDTH - 20);
        contentTable.setSize(0.4f * WizardGame.WIDTH - 20, 400);
        contentTable.setPosition(WizardGame.WIDTH * 0.3f, WizardGame.HEIGHT * 0.3f);
        
        contentTable.add(messages.get("menu.create_game.player_name.label")).row();
        contentTable.add(playerName).row();
        contentTable.add(messages.get("menu.create_game.session_name.label")).row();
        contentTable.add(sessionName).row();
        contentTable.add(messages.get("menu.create_game.session_timeout.label")).row();
        contentTable.add(timeOut).row();
        contentTable.add(messages.get("menu.create_game.session_configuration.label")).row();
        contentTable.add(configurations).row();

        stage.addActor(contentTable);
        stage.addCaptureListener(new KeyboardFocusManager(playerName, sessionName, timeOut, configurations, buttonBack, buttonContinue));

        buttonBack.setName("button_back");
        buttonContinue.setName("button_continue");
        sessionName.setName("session_name");
        playerName.setName("player_name");
        timeOut.setName("timeout");
        configurations.setName("configurations");
    }

    private void create() {
        boolean error = false;

        String sessionName = this.sessionName.getText();
        if (sessionName.isBlank()) {
            log.warn("Please choose a session name.");
            this.sessionName.setStyle(getTextFieldErrorStyle());
            error = true;
        }

        String playerName = this.playerName.getText();
        if (playerName.isBlank()) {
            log.warn("Please choose a name.");
            this.playerName.setStyle(getTextFieldErrorStyle());
            error = true;
        }

        long timeout = 0;
        try {
            timeout = Long.parseLong(this.timeOut.getText());
        } catch (NumberFormatException e) {
            log.warn("Please choose a valid timeout.");
            this.timeOut.setStyle(getTextFieldErrorStyle());
            error = true;
        }

        Configuration config = null;
        try {
            int selected = configurations.getSelectedIndex();
            config = Configuration.values()[selected];
        } catch (ArrayIndexOutOfBoundsException e) {
            log.warn("Please select a valid configuration.");
            this.configurations.setStyle(getSelectBoxErrorStyle());
            error = true;
        }

        var fConfig = config;
        var fTimeout = timeout;
        if (!error) {
            var client = game.getClient();
            client.execute(Lobby.class, (s, c) -> s.createSession(c, sessionName, fConfig, 1000 * fTimeout, playerName));
        }
    }
}
