package com.ygames.ysoccer.screens;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.strongjoshua.console.Console;
import com.strongjoshua.console.GUIConsole;
import com.ygames.ysoccer.framework.Assets;
import com.ygames.ysoccer.framework.Font;
import com.ygames.ysoccer.framework.GLGame;
import com.ygames.ysoccer.framework.GLScreen;
import com.ygames.ysoccer.framework.InputDevice;
import com.ygames.ysoccer.framework.Settings;
import com.ygames.ysoccer.framework.SoundManager;
import com.ygames.ysoccer.gui.Button;
import com.ygames.ysoccer.gui.Label;
import com.ygames.ysoccer.gui.MenuTheme;
import com.ygames.ysoccer.gui.Widget;
import com.ygames.ysoccer.match.Match;
import com.ygames.ysoccer.match.MatchConsoleCommandExecutor;
import com.ygames.ysoccer.match.MatchRenderer;
import com.ygames.ysoccer.match.Player;
import com.ygames.ysoccer.match.Team;

import java.util.ArrayList;
import java.util.Locale;

import static com.ygames.ysoccer.match.Match.AWAY;
import static com.ygames.ysoccer.match.Match.HOME;

class MatchScreen extends GLScreen {

    private enum OverlayMode {NONE, MAIN, CHOOSE_PLAYER}

    private static final int OVERLAY_W = 420;
    private static final int OVERLAY_H = 260;
    private static final int OVERLAY_X = (1280 - OVERLAY_W) / 2;
    private static final int OVERLAY_Y = 190;
    private static final int BUTTON_W = 280;
    private static final int BUTTON_H = 28;
    private static final int BUTTON_X = (1280 - BUTTON_W) / 2;
    private static final int CHOOSE_OVERLAY_W = 520;
    private static final int CHOOSE_OVERLAY_H = 500;
    private static final int CHOOSE_OVERLAY_X = (1280 - CHOOSE_OVERLAY_W) / 2;
    private static final int CHOOSE_OVERLAY_Y = 104;

    private final Match match;
    private final MatchRenderer matchRenderer;
    private boolean matchStarted;
    private boolean matchPaused;
    private boolean matchEnded;
    private OverlayMode overlayMode;
    private InputDevice menuInputDevice;

    private Console console;

    MatchScreen(GLGame game, Match match) {
        super(game);
        this.match = match;

        matchRenderer = new MatchRenderer(game.glGraphics, match);

        playMenuMusic = false;
        usesMouse = false;

        matchStarted = false;
        matchPaused = false;
        matchEnded = false;
        overlayMode = OverlayMode.NONE;
        game.glGraphics.light = 0;

        match.listener = new Match.MatchListener() {
            public void quitMatch(boolean matchCompleted) {
                quit(matchCompleted);
            }
        };

        if (Settings.development) {
            console = new GUIConsole();
            console.setSizePercent(25, 100);
            console.setPositionPercent(0, 0);
            console.setHoverAlpha(0.9f);
            console.setNoHoverAlpha(0.9f);
            console.setCommandExecutor(new MatchConsoleCommandExecutor(match));
        }
    }

    @Override
    protected boolean useMenuZoom() {
        return false;
    }

    @Override
    public void render(float deltaTime) {
        super.render(deltaTime);
        if (matchEnded) {
            return;
        }

        if (!matchStarted) {
            match.start();
            matchStarted = true;
        }

        boolean overlayToggled = updateOverlayToggle();

        if (!matchPaused && !isOverlayOpen() && !overlayToggled) {
            match.update(deltaTime);
            match.updateCurrentData();
            matchRenderer.update();
        }

        if (!matchEnded) {
            matchRenderer.render();
        }

        if (isOverlayOpen()) {
            renderOverlay();
        }

        if (!matchEnded && Settings.development) {
            console.draw();
            matchPaused = console.isVisible();
        }

        if (Settings.development && Settings.showJavaHeap) {
            batch.begin();
            Assets.font10.draw(batch, String.format(Locale.getDefault(), "%,d", Gdx.app.getJavaHeap()), game.gui.WIDTH - 120, 10, Font.Align.LEFT);
            batch.end();
        }
    }

    @Override
    public void resize(int width, int height) {
        super.resize(width, height);

        matchRenderer.resize(width, height);

        if (Settings.development) {
            console.refresh();
        }
    }

    private void quit(boolean matchCompleted) {
        quit(matchCompleted, true, false);
    }

    private void abortMatch() {
        quit(false, false, true);
    }

    private void quit(boolean matchCompleted, boolean updateCompetition, boolean goToMain) {
        matchEnded = true;
        closeOverlay();

        for (int t = HOME; t <= AWAY; t++) {
            int len = match.team[t].lineup.size();
            for (int i = 0; i < len; i++) {
                Player player = match.team[t].lineup.get(i);
                if (player.role == Player.Role.GOALKEEPER) {
                    Assets.unloadKeeper(player);
                } else {
                    Assets.unloadPlayer(player);
                }
                Assets.unloadHair(player);
            }
            match.team[t].lineup.clear();
        }

        SoundManager.stopMatchSounds();
        Assets.Commentary.stop();

        if (updateCompetition) {
            if (matchCompleted) {
                match.competition.matchCompleted();
            } else if (match.clock > 0) {
                match.competition.matchInterrupted();
            }
        }

        game.enableMouse();
        if (goToMain) {
            game.setScreen(new Main(game));
            return;
        }

        switch (match.competition.type) {
            case FRIENDLY:
                game.setScreen(new ReplayMatch(game, match));
                break;

            case LEAGUE:
                game.setScreen(new PlayLeague(game));
                break;

            case CUP:
                game.setScreen(new PlayCup(game));
                break;

            case TOURNAMENT:
                game.setScreen(new PlayTournament(game));
                break;

            case TEST_MATCH:
                game.setScreen(new DeveloperTools(game));
                break;
        }
    }

    private boolean isOverlayOpen() {
        return overlayMode != OverlayMode.NONE;
    }

    private boolean updateOverlayToggle() {
        InputDevice inputDevice = menuDownInputDevice();
        if (inputDevice == null && Gdx.input.isKeyJustPressed(Input.Keys.P)) {
            inputDevice = firstHumanInputDevice();
        }
        if (inputDevice == null) {
            return false;
        }

        if (isOverlayOpen()) {
            closeOverlay();
        } else {
            openMainOverlay(inputDevice);
        }
        return true;
    }

    private InputDevice menuDownInputDevice() {
        for (InputDevice inputDevice : game.inputDevices) {
            if (inputDevice.menuDown()) {
                return inputDevice;
            }
        }
        return null;
    }

    private InputDevice firstHumanInputDevice() {
        for (int t = HOME; t <= AWAY; t++) {
            Team team = match.team[t];
            if (team == null) {
                continue;
            }
            for (Player player : team.lineup) {
                if (player.inputDevice != player.ai) {
                    return player.inputDevice;
                }
            }
        }
        return null;
    }

    private void openMainOverlay(InputDevice inputDevice) {
        menuInputDevice = inputDevice;
        overlayMode = OverlayMode.MAIN;
        rebuildOverlayWidgets();
    }

    private void openChoosePlayerOverlay() {
        overlayMode = OverlayMode.CHOOSE_PLAYER;
        rebuildOverlayWidgets();
    }

    private void closeOverlay() {
        overlayMode = OverlayMode.NONE;
        menuInputDevice = null;
        widgets.clear();
        clearSelectedWidget();
    }

    private void rebuildOverlayWidgets() {
        widgets.clear();
        clearSelectedWidget();

        if (overlayMode == OverlayMode.MAIN) {
            addMainOverlayWidgets();
        } else if (overlayMode == OverlayMode.CHOOSE_PLAYER) {
            addChoosePlayerWidgets();
        }
    }

    private void addMainOverlayWidgets() {
        Widget w = new OverlayTitleLabel("MATCH MENU");
        widgets.add(w);

        w = new ResumeButton();
        widgets.add(w);
        setSelectedWidget(w);

        w = new ChooseNewPlayerButton();
        widgets.add(w);

        w = new ExitGameButton();
        widgets.add(w);

        w = new AbortGameButton();
        widgets.add(w);
    }

    private void addChoosePlayerWidgets() {
        Widget w = new OverlayTitleLabel("CHOOSE A NEW PLAYER");
        widgets.add(w);

        Team team = teamForMenuInputDevice();
        if (team == null) {
            w = new OverlayInfoLabel("NO CONTROLLED TEAM FOUND");
            widgets.add(w);
        } else {
            ArrayList<Player> players = team.selectableControlledPlayers();
            int startY = 160;
            for (int i = 0; i < players.size(); i++) {
                w = new PlayerButton(players.get(i), startY + 34 * i);
                widgets.add(w);
                if (getSelectedWidget() == null) {
                    setSelectedWidget(w);
                }
            }
            if (players.size() == 0) {
                w = new OverlayInfoLabel("NO PLAYER AVAILABLE");
                widgets.add(w);
            }
        }

        w = new BackToMatchMenuButton();
        widgets.add(w);
        if (getSelectedWidget() == null) {
            setSelectedWidget(w);
        }
    }

    private Team teamForMenuInputDevice() {
        if (menuInputDevice != null) {
            for (int t = HOME; t <= AWAY; t++) {
                Team team = match.team[t];
                if (team != null && (team.inputDevice == menuInputDevice || team.controlledPlayer(menuInputDevice) != null)) {
                    return team;
                }
            }
        }

        for (int t = HOME; t <= AWAY; t++) {
            Team team = match.team[t];
            if (team == null) {
                continue;
            }
            for (Player player : team.lineup) {
                if (player.inputDevice != player.ai) {
                    if (menuInputDevice == null) {
                        menuInputDevice = player.inputDevice;
                    }
                    return team;
                }
            }
        }
        return null;
    }

    private void choosePlayer(Player player) {
        Team team = teamForMenuInputDevice();
        if (team != null && menuInputDevice != null) {
            team.assignInputDeviceToPlayer(menuInputDevice, player);
        }
        closeOverlay();
    }

    private void renderOverlay() {
        camera.setToOrtho(true, game.gui.screenWidth, game.gui.screenHeight);
        camera.translate(-game.gui.originX, -game.gui.originY);
        camera.update();
        batch.setProjectionMatrix(camera.combined);
        shapeRenderer.setProjectionMatrix(camera.combined);

        Gdx.gl.glEnable(GL20.GL_BLEND);
        shapeRenderer.begin(ShapeRenderer.ShapeType.Filled);
        shapeRenderer.setColor(0x000000, 0.55f);
        shapeRenderer.rect(0, 0, game.gui.WIDTH, game.gui.HEIGHT);
        shapeRenderer.setColor(0x08040D, 0.92f);
        if (overlayMode == OverlayMode.CHOOSE_PLAYER) {
            shapeRenderer.rect(CHOOSE_OVERLAY_X, CHOOSE_OVERLAY_Y, CHOOSE_OVERLAY_W, CHOOSE_OVERLAY_H);
        } else {
            shapeRenderer.rect(OVERLAY_X, OVERLAY_Y, OVERLAY_W, OVERLAY_H);
        }
        shapeRenderer.end();

        for (Widget widget : widgets) {
            widget.render(batch, shapeRenderer);
        }
    }

    private static class OverlayTitleLabel extends Button {

        OverlayTitleLabel(String text) {
            boolean choosePlayerTitle = text.startsWith("CHOOSE");
            setGeometry(
                choosePlayerTitle ? CHOOSE_OVERLAY_X + 58 : OVERLAY_X + 58,
                choosePlayerTitle ? CHOOSE_OVERLAY_Y + 18 : OVERLAY_Y + 22,
                choosePlayerTitle ? CHOOSE_OVERLAY_W - 116 : OVERLAY_W - 116,
                28);
            setColors(MenuTheme.TITLE);
            setText(text, Font.Align.CENTER, Assets.font10);
        }
    }

    private static class OverlayInfoLabel extends Label {

        OverlayInfoLabel(String text) {
            setGeometry((1280 - 360) / 2, OVERLAY_Y + 95, 360, 28);
            setText(text, Font.Align.CENTER, Assets.font10);
        }
    }

    private class ResumeButton extends Button {

        ResumeButton() {
            setGeometry(BUTTON_X, OVERLAY_Y + 76, BUTTON_W, BUTTON_H);
            setColors(MenuTheme.ACTION.brighter());
            setText("RESUME", Font.Align.CENTER, Assets.font10);
        }

        @Override
        public void onFire1Down() {
            closeOverlay();
        }
    }

    private class ChooseNewPlayerButton extends Button {

        ChooseNewPlayerButton() {
            setGeometry(BUTTON_X, OVERLAY_Y + 112, BUTTON_W, BUTTON_H);
            setColors(MenuTheme.ACTION);
            setText("CHOOSE A NEW PLAYER", Font.Align.CENTER, Assets.font10);
        }

        @Override
        public void onFire1Down() {
            openChoosePlayerOverlay();
        }
    }

    private class ExitGameButton extends Button {

        ExitGameButton() {
            setGeometry(BUTTON_X, OVERLAY_Y + 148, BUTTON_W, BUTTON_H);
            setColors(MenuTheme.VALUE);
            setText("EXIT GAME", Font.Align.CENTER, Assets.font10);
        }

        @Override
        public void onFire1Down() {
            quit(false);
        }
    }

    private class AbortGameButton extends Button {

        AbortGameButton() {
            setGeometry(BUTTON_X, OVERLAY_Y + 184, BUTTON_W, BUTTON_H);
            setColors(MenuTheme.DANGER);
            setText("ABORT GAME", Font.Align.CENTER, Assets.font10);
        }

        @Override
        public void onFire1Down() {
            abortMatch();
        }
    }

    private class BackToMatchMenuButton extends Button {

        BackToMatchMenuButton() {
            setGeometry((game.gui.WIDTH - 220) / 2, CHOOSE_OVERLAY_Y + CHOOSE_OVERLAY_H - 44, 220, 28);
            setColors(MenuTheme.VALUE);
            setText("BACK", Font.Align.CENTER, Assets.font10);
        }

        @Override
        public void onFire1Down() {
            overlayMode = OverlayMode.MAIN;
            rebuildOverlayWidgets();
        }
    }

    private class PlayerButton extends Button {

        private final Player player;

        PlayerButton(Player player, int y) {
            this.player = player;
            setGeometry((game.gui.WIDTH - 360) / 2, y, 360, 24);
            setColors(MenuTheme.ACTION);
            setText(player.number + "  " + player.shirtName, Font.Align.LEFT, Assets.font10);
        }

        @Override
        public void onFire1Down() {
            choosePlayer(player);
        }
    }
}
