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.scenes.scene2d.utils.Drawable;
import eu.jonahbauer.wizard.client.libgdx.UiskinAtlas;
import eu.jonahbauer.wizard.client.libgdx.WizardGame;
import eu.jonahbauer.wizard.client.libgdx.actors.IconList;
import eu.jonahbauer.wizard.client.libgdx.listeners.KeyboardFocusManager;
import eu.jonahbauer.wizard.client.libgdx.listeners.ResetErrorListener;
import eu.jonahbauer.wizard.client.libgdx.state.Lobby;
import eu.jonahbauer.wizard.common.messages.data.SessionData;
import lombok.extern.log4j.Log4j2;

import java.util.UUID;

@Log4j2
public class LobbyScreen extends MenuScreen {
    
    private TextButton buttonBack;
    private TextButton buttonJoin;
    private TextButton buttonCreate;
    
    private TextField playerName;
    private Label labelSessionName;
    private Label labelSessionPlayerCount;
    private Label labelSessionConfiguration;

    private UUID selectedSession;
    private boolean rejoin = false;
    private List<SessionData> sessions;
    private ScrollPane sessionListContainer;
    
    private final ChangeListener listener = new ChangeListener() {
        @Override
        public void changed(ChangeEvent event, Actor actor) {
            if (actor == buttonBack) {
                game.getClient().execute(Lobby.class, Lobby::disconnect);
            } else if (actor == buttonJoin) {
                if (rejoin) {
                    game.getClient().execute(Lobby.class, Lobby::showRejoinScreen);
                } else {
                    join();
                }
            } else if (actor == buttonCreate) {
                game.getClient().execute(Lobby.class, Lobby::showCreateScreen);
            }
        }
    };
    
    public LobbyScreen(WizardGame game) {
        super(game);
    }
    
    @Override
    public void show() {
        super.show();
        
        buttonBack = new TextButton(messages.get("menu.lobby.back"), skin);
        buttonBack.addListener(listener);
        getButtonGroup().addActor(buttonBack);

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

        buttonJoin = new TextButton(messages.get("menu.lobby.join"), skin);
        buttonJoin.addListener(listener);
        getButtonGroup().addActor(buttonJoin);

        getButtonGroup().setWidth(0.55f * WizardGame.WIDTH);
        
        sessions = new IconList<>(skin) {
            // TODO better icons
            private final Drawable running = skin.getDrawable(UiskinAtlas.NOT_READY);
            private final Drawable notRunning = skin.getDrawable(UiskinAtlas.READY);

            @Override
            public String toString(SessionData session) {
                return session.getName();
            }

            @Override
            public Drawable getIcon(SessionData item) {
                return item.isRunning() ? running : notRunning;
            }
        };
        sessions.addListener(new ChangeListener() {
            @Override
            public void changed(ChangeEvent event, Actor actor) {
                var selected = sessions.getSelected();
                updateData(selected);
            }
        });

        sessionListContainer = new ScrollPane(sessions, skin);
        sessionListContainer.layout();

        sessions.addListener(new ResetErrorListener(skin, sessionListContainer));

        var content = new HorizontalGroup().grow().space(20);
        content.setPosition(0.25f * WizardGame.WIDTH, 0.3f * WizardGame.HEIGHT);
        content.setSize(0.5f * WizardGame.WIDTH, 400);
        content.addActor(new Container<>(sessionListContainer).width(0.2f * WizardGame.WIDTH).height(400));
        content.addActor(createInfoTable());
        content.layout();

        stage.addActor(content);
        stage.addCaptureListener(new KeyboardFocusManager(
                sessions, playerName, buttonBack, buttonCreate, buttonJoin
        ));

        buttonBack.setName("button_back");
        buttonJoin.setName("button_join");
        buttonCreate.setName("button_create");
        sessions.setName("session_list");
        playerName.setName("player_name");
        labelSessionName.setName("session_name");
        labelSessionConfiguration.setName("session_configuration");
        labelSessionPlayerCount.setName("session_player_count");
    }

    private Table createInfoTable() {
        float infoTableWidth = 0.3f * WizardGame.WIDTH - 20;

        playerName = new TextField(game.storage.playerName, skin);
        playerName.addListener(new ChangeListener() {
            @Override
            public void changed(ChangeEvent event, Actor actor) {
                game.storage.playerName = playerName.getText();
            }
        });
        playerName.setMaxLength(20);
        playerName.addListener(new ResetErrorListener(skin));

        labelSessionName = new Label("", skin, "textfield");
        labelSessionConfiguration = new Label("", skin, "textfield");
        labelSessionPlayerCount = new Label("", skin, "textfield");

        labelSessionName.setEllipsis(true);
        labelSessionConfiguration.setEllipsis(true);
        labelSessionPlayerCount.setEllipsis(true);

        var infoTable = new Table().center().left();
        infoTable.columnDefaults(0).growX().width(infoTableWidth);
        infoTable.setSize(infoTableWidth, 400);

        infoTable.add(new Label(messages.get("menu.lobby.player_name.label"), skin)).row();
        infoTable.add(playerName).row();
        infoTable.add(new Label(messages.get("menu.lobby.session_name.label"), skin)).row();
        infoTable.add(labelSessionName).row();
        infoTable.add(new Label(messages.get("menu.lobby.session_configuration.label"), skin)).row();
        infoTable.add(labelSessionConfiguration).row();
        infoTable.add(new Label(messages.get("menu.lobby.session_player_count.label"), skin)).row();
        infoTable.add(labelSessionPlayerCount).row();

        return infoTable;
    }

    public void addSession(SessionData session) {
        this.sessions.getItems().add(session);
        this.sessions.invalidateHierarchy();
    }

    public void removeSession(UUID session) {
        var index = indexOf(session);
        if (index != -1) {
            this.sessions.getItems().removeIndex(index);
            this.sessions.invalidateHierarchy();
        }

        if (selectedSession != null && selectedSession.equals(session)) {
            updateData(null);
        }
    }

    public void modifySession(SessionData session) {
        var index = indexOf(session.getUuid());
        this.sessions.getItems().set(index, session);
        this.sessions.invalidateHierarchy();

        if (selectedSession != null && selectedSession.equals(session.getUuid())) {
            updateData(session);
        }
    }

    public void setSessions(SessionData... sessions) {
        var items = this.sessions.getItems();
        items.clear();
        items.addAll(sessions);
        this.selectedSession = null;
        this.sessions.invalidateHierarchy();
    }

    private void updateData(SessionData data) {
        if (data != null) {
            labelSessionName.setText(data.getName());
            labelSessionPlayerCount.setText(Integer.toString(data.getPlayerCount()));
            labelSessionConfiguration.setText(data.getConfiguration().toString());
            selectedSession = data.getUuid();
            updateRejoin(data.isRunning());
        } else {
            labelSessionName.setText("");
            labelSessionPlayerCount.setText("");
            labelSessionConfiguration.setText("");
            selectedSession = null;
            updateRejoin(false);
        }
    }

    private void updateRejoin(boolean rejoin) {
        this.rejoin = rejoin;
        buttonJoin.setText(messages.get("menu.lobby." + (rejoin ? "rejoin" : "join")));
    }

    private void join() {
        boolean error = false;

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

        if (selectedSession == null) {
            log.warn("Please select a session.");
            this.sessionListContainer.setStyle(getScrollPaneErrorStyle());
            error = true;
        }

        if (!error) {
            var client = game.getClient();
            try {
                client.execute(Lobby.class, (s, c) -> s.joinSession(c, selectedSession, playerName));
            } catch (IllegalArgumentException e) {
                // only if session is not known
                log.warn(e.getMessage());
                this.sessionListContainer.setStyle(getScrollPaneErrorStyle());
            }
        }
    }

    private int indexOf(UUID session) {
        var items = this.sessions.getItems();
        for (int i = 0; i < items.size; i++) {
            if (items.get(i).getUuid().equals(session)) {
                return i;
            }
        }
        return -1;
    }
}
