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

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.ui.*;
import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener;
import eu.jonahbauer.wizard.client.libgdx.WizardGame;
import eu.jonahbauer.wizard.client.libgdx.actors.AutoFocusScrollPane;
import eu.jonahbauer.wizard.client.libgdx.listeners.ResetErrorListener;
import eu.jonahbauer.wizard.client.libgdx.state.AwaitingJoinSession;
import eu.jonahbauer.wizard.client.libgdx.state.Lobby;
import eu.jonahbauer.wizard.client.libgdx.state.Menu;
import eu.jonahbauer.wizard.common.messages.client.JoinSessionMessage;
import eu.jonahbauer.wizard.common.messages.data.SessionData;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.stream.StreamSupport;

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 List<SessionData> sessions;
    private ScrollPane sessionListContainer;
    private final Map<UUID, SessionData> sessionData = new HashMap<>();

    private final Lobby state;
    
    private final ChangeListener listener = new ChangeListener() {
        @Override
        public void changed(ChangeEvent event, Actor actor) {
            if (actor == buttonBack) {
                game.getClient().transition(new Menu());
                sfxClick();
            } else if (actor == buttonJoin) {
                join();
                sfxClick();
            } else if (actor == buttonCreate) {
                game.setScreen(new CreateGameScreen(game));
                sfxClick();
            }
        }
    };
    
    public LobbyScreen(WizardGame game) {
        super(game);
        this.state = (Lobby) game.getClient().getState();
    }
    
    @Override
    public void show() {
        super.show();
        
        buttonBack = new TextButton(game.messages.get("menu.lobby.back"), game.data.skin);
        buttonJoin = new TextButton(game.messages.get("menu.lobby.join"), game.data.skin);
        buttonCreate = new TextButton(game.messages.get("menu.lobby.create"), game.data.skin);
        
        sessions = new List<>(game.data.skin) {
            @Override
            public String toString(SessionData session) {
                return session.getName();
            }
        };
        sessions.addListener(new ChangeListener() {
            @Override
            public void changed(ChangeEvent event, Actor actor) {
                var selected = sessions.getSelected();
                updateData(selected);
            }
        });

        sessionListContainer = new AutoFocusScrollPane(sessions, game.data.skin);
        sessionListContainer.layout();

        sessions.addListener(new ResetErrorListener(game.data.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();
        
        var buttons = new HorizontalGroup();
        buttons.setPosition(WizardGame.WIDTH * 0.2f, BUTTON_BAR_Y);
        buttons.setSize(WizardGame.WIDTH * 0.60f, 125);
        buttons.addActor(buttonBack);
        buttons.addActor(buttonCreate);
        buttons.addActor(buttonJoin);
        buttons.space(Math.max(0, (float) (buttons.getWidth() - StreamSupport.stream(buttons.getChildren().spliterator(), false).mapToDouble(Actor::getWidth).sum()) / (buttons.getChildren().size - 1)));
        
        Gdx.input.setInputProcessor(game.data.stage);
        game.data.stage.addActor(content);
        game.data.stage.addActor(buttons);
        
        buttonBack.addListener(listener);
        buttonJoin.addListener(listener);
        buttonCreate.addListener(listener);
    }
    
    public void addSession(SessionData session) {
        this.sessionData.put(session.getUuid(), session);
        this.sessions.getItems().add(session);
        this.sessions.invalidateHierarchy();
    }
    
    public void removeSession(UUID session) {
        this.sessionData.remove(session);
        var items = this.sessions.getItems();
        for (int i = 0; i < items.size; i++) {
            if (items.get(i).getUuid().equals(session)) {
                items.removeIndex(i);
                break;
            }
        }
        this.sessions.invalidateHierarchy();
    
        if (session.equals(selectedSession)) {
            updateData(null);
        }
    }
    
    public void modifySession(SessionData session) {
        this.sessionData.put(session.getUuid(), session);
        var items = this.sessions.getItems();
        for (int i = 0; i < items.size; i++) {
            if (items.get(i).getUuid().equals(session.getUuid())) {
                items.set(i, session);
                break;
            }
        }
        this.sessions.invalidateHierarchy();
        
        if (session.getUuid().equals(selectedSession)) {
            updateData(session);
        }
    }
    
    public void setSessions(SessionData... sessions) {
        this.sessionData.clear();
        Arrays.stream(sessions).forEach(s -> this.sessionData.put(s.getUuid(), s));
        var items = this.sessions.getItems();
        items.clear();
        items.addAll(sessions);
        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();
        } else {
            labelSessionName.setText("");
            labelSessionPlayerCount.setText("");
            labelSessionConfiguration.setText("");
            selectedSession = null;
        }
    }
    
    private Table createInfoTable() {
        float infoTableWidth = 0.3f * WizardGame.WIDTH - 20;
        
        playerName = new TextField(state.getPlayerName(), game.data.skin);
        playerName.addListener(new ResetErrorListener(game.data.skin));
        playerName.addListener(new ChangeListener() {
            @Override
            public void changed(ChangeEvent event, Actor actor) {
                state.setPlayerName(playerName.getText());
            }
        });

        labelSessionName = new Label("", game.data.skin, "textfield");
        labelSessionConfiguration = new Label("", game.data.skin, "textfield");
        labelSessionPlayerCount = new Label("", game.data.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(game.messages.get("menu.lobby.player_name.label"), game.data.skin)).row();
        infoTable.add(playerName).row();
        infoTable.add(new Label(game.messages.get("menu.lobby.session_name.label"), game.data.skin)).row();
        infoTable.add(labelSessionName).row();
        infoTable.add(new Label(game.messages.get("menu.lobby.session_configuration.label"), game.data.skin)).row();
        infoTable.add(labelSessionConfiguration).row();
        infoTable.add(new Label(game.messages.get("menu.lobby.session_player_count.label"), game.data.skin)).row();
        infoTable.add(labelSessionPlayerCount).row();
        
        return infoTable;
    }

    private void join() {
        boolean error = false;

        String playerName = this.playerName.getText();
        if (playerName.isBlank()) {
            System.out.println("Please choose a player name");
            this.playerName.setStyle(game.data.skin.get("error", TextField.TextFieldStyle.class));
            error = true;
        }

        SessionData session = sessionData.get(selectedSession);
        if (session == null) {
            System.out.println("Please select a session.");
            this.sessionListContainer.setStyle(game.data.skin.get("error", ScrollPane.ScrollPaneStyle.class));
            error = true;
        }

        if (!error) {
            var client = game.getClient();
            client.transition(new AwaitingJoinSession(session.getUuid(), session.getName(), session.getConfiguration(), playerName));
            client.send(new JoinSessionMessage(selectedSession, playerName));
        }
    }
}
