package com.ygames.ysoccer.framework;

import com.badlogic.gdx.Game;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Screen;
import com.badlogic.gdx.assets.AssetManager;
import com.badlogic.gdx.controllers.Controller;
import com.badlogic.gdx.controllers.Controllers;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
import com.ygames.ysoccer.competitions.Competition;
import com.ygames.ysoccer.gui.Gui;
import com.ygames.ysoccer.gui.MenuTheme;
import com.ygames.ysoccer.gui.WidgetColor;
import com.ygames.ysoccer.match.Const;
import com.ygames.ysoccer.match.Tactics;
import com.ygames.ysoccer.match.Team;

import java.util.ArrayList;
import java.util.Stack;

public class GLGame extends Game {

    public static final String BACKGROUND_IMAGE = "images/backgrounds/e29238c7-9a1a-4b7c-945d-623ef4626d5c.png";

    public static final int SUBFRAMES = 8;
    public static final int VIRTUAL_REFRESH_RATE = 64;
    public static final int SERVER_UPDATES_PER_SECOND = 60;
    public static final int SUBFRAMES_PER_SECOND = VIRTUAL_REFRESH_RATE * SUBFRAMES;
    public static final float SUBFRAME_DURATION = 1.0f / SUBFRAMES_PER_SECOND;

    private final String GUI_ATLAS_FILE = "images/gui.atlas";

    public Settings settings;
    public AssetManager assetManager;
    public GLGraphics glGraphics;
    public Gui gui;
    public TextureAtlas guiAtlas;
    private float deltaTime;
    public InputDeviceList inputDevices;
    Mouse mouse;
    MenuInput menuInput;

    public int tacticsToEdit;
    public Tactics editedTactics;
    public Stack<Tactics> tacticsUndo;
    public boolean tacticsFlip = true;
    public Team tacticsTeam;

    public enum State {
        NONE, FRIENDLY, COMPETITION, EDIT, TRAINING
    }

    private State state;
    public Texture stateBackground;
    public WidgetColor stateColor;

    public TeamList teamList;
    public Competition competition;

    public MenuMusic menuMusic;

    public SoundManager soundManager;

    @Override
    public void create() {
        settings = new Settings();
        assetManager = new AssetManager();
        Gdx.app.setLogLevel(Settings.logLevel);
        Const.loadGameplayConfig();
        glGraphics = new GLGraphics();
        gui = new Gui();

        setScreenMode(settings.fullScreen);
        loadAssets();
        assetManager.finishLoading();
        getAssets();
        Assets.load(settings);

        inputDevices = new InputDeviceList();
        reloadInputDevices();

        menuInput = new MenuInput(this);

        mouse = new Mouse();

        state = State.NONE;
        stateColor = new WidgetColor();

        teamList = new TeamList();

        menuMusic = new MenuMusic("music");

        restoreSaveGame();

        soundManager = new SoundManager();
        SoundManager.volume = settings.soundVolume;
    }

    private void loadAssets() {
        assetManager.load(GUI_ATLAS_FILE, TextureAtlas.class);
    }

    private void getAssets() {
        guiAtlas = assetManager.get(GUI_ATLAS_FILE);
        gui.setTextures(guiAtlas);
    }

    @Override
    public void setScreen(Screen screen) {
        GLGame.debug(LogType.GUI, "setScreen", screen.getClass().getSimpleName());
        super.setScreen(screen);
    }

    private void createSaveGame() {
        if (hasCompetition()) {
            competition.save(Assets.saveGame);
        } else if (Assets.saveGame.exists()) {
            Assets.saveGame.delete();
        }
    }

    private void restoreSaveGame() {
        if (Assets.saveGame.exists()) {
            Competition competition = Competition.load(Assets.saveGame);
            setCompetition(competition);
        }
    }

    public static void setScreenMode(boolean fullScreen) {
        if (fullScreen) {
            if (!Gdx.graphics.isFullscreen()) {
                Gdx.graphics.setFullscreenMode(Gdx.graphics.getDisplayMode());
            }
        } else if (Gdx.graphics.isFullscreen()) {
            Gdx.graphics.setWindowedMode(Gui.WIDTH, Gui.HEIGHT);
        }
    }

    public void enableMouse() {
        mouse.enable();
    }

    public void disableMouse() {
        mouse.disable();
    }

    @Override
    public void render() {
        try {
            deltaTime += Gdx.graphics.getDeltaTime();

            int subFrames = (int) (deltaTime / SUBFRAME_DURATION);

            if (screen != null) {
                screen.render(subFrames * SUBFRAME_DURATION);
            }

            deltaTime -= subFrames * SUBFRAME_DURATION;

        } catch (Exception e) {
            e.printStackTrace();

            FileHandle file = errorLogFile();
            boolean append = useConfiguredLogFile();
            file.writeString("\nException in thread \"" + Thread.currentThread().getName() + "\" " + e.toString() + "\n", append);
            for (StackTraceElement s : e.getStackTrace()) {
                file.writeString("\tat " + s.toString() + "\n", true);
            }
            file.writeString("\n\n", true);

            Gdx.app.exit();
        }
    }

    private boolean useConfiguredLogFile() {
        String path = System.getProperty("ysoccer.logFile");
        return path != null && path.trim().length() > 0;
    }

    private FileHandle errorLogFile() {
        String path = System.getProperty("ysoccer.logFile");
        if (path != null && path.trim().length() > 0) {
            return Gdx.files.absolute(path.trim());
        }
        return Gdx.files.local("error.log");
    }

    @Override
    public void pause() {
        super.pause();
        createSaveGame();
        unloadAssets();
    }

    private void unloadAssets() {
        assetManager.unload(GUI_ATLAS_FILE);
    }

    @Override
    public void resume() {
        loadAssets();
        assetManager.finishLoading();
        getAssets();
        super.resume();
    }

    @Override
    public void dispose() {
        super.dispose();
        glGraphics.dispose();
        assetManager.dispose();
    }

    public void setState(State state, Competition.Category category) {

        this.state = state;

        switch (state) {
            case COMPETITION:
                stateBackground = new Texture(BACKGROUND_IMAGE);
                MenuTheme.applyState(stateColor);
                break;

            case FRIENDLY:
                stateBackground = new Texture(BACKGROUND_IMAGE);
                MenuTheme.applyState(stateColor);
                break;

            case EDIT:
                stateBackground = new Texture(BACKGROUND_IMAGE);
                MenuTheme.applyState(stateColor);
                break;

            case TRAINING:
                stateBackground = new Texture(BACKGROUND_IMAGE);
                MenuTheme.applyState(stateColor);
                break;
        }
    }

    public State getState() {
        return state;
    }

    public boolean hasCompetition() {
        return competition != null;
    }

    public void setCompetition(Competition competition) {
        this.competition = competition;
        setState(GLGame.State.COMPETITION, competition.category);
    }

    public void clearCompetition() {
        this.competition = null;
    }

    public void reloadInputDevices() {
        inputDevices.clear();

        // keyboard
        ArrayList<KeyboardConfig> keyboardConfigs = settings.getKeyboardConfigs();
        inputDevices.add(new Keyboard(0, keyboardConfigs.get(0)));
        inputDevices.add(new Keyboard(1, keyboardConfigs.get(1)));

        // joysticks
        int port = 0;
        boolean hasSpecificXboxPads = hasSpecificXboxPads();
        for (Controller controller : Controllers.getControllers()) {
            if (isGenericXboxDuplicate(controller, hasSpecificXboxPads)) {
                continue;
            }

            JoystickConfig joystickConfig = settings.getJoystickConfigByName(controller.getName());
            if (joystickConfig != null) {
                inputDevices.add(new Joystick(controller, joystickConfig, port));
                port++;
            }
        }
    }

    private boolean hasSpecificXboxPads() {
        for (Controller controller : Controllers.getControllers()) {
            if (controller.getName().startsWith("Microsoft X-Box 360 pad")) {
                return true;
            }
        }
        return false;
    }

    private boolean isGenericXboxDuplicate(Controller controller, boolean hasSpecificXboxPads) {
        return hasSpecificXboxPads && controller.getName().equals("Generic X-Box pad");
    }

    Gui getGui() {
        return gui;
    }

    public enum LogType {
        AI_ATTACKING,
        AI_KICKING,
        BALL,
        GUI,
        PASSING,
        PLAYER_SELECTION
    }

    public static void debug(LogType type, Object Object, String message) {
        debug(type, Object.getClass().getSimpleName(), message);
    }

    public static void debug(LogType type, String tag, String message) {
        if (Settings.development && isSetLogFilter(type)) {
            Gdx.app.debug(tag, message);
        }
    }

    public static boolean isSetLogFilter(LogType type) {
        return (type == null) || (Settings.logFilter & (1 << type.ordinal())) != 0;
    }

    public static void toggleLogFilter(LogType type) {
        Settings.logFilter = Settings.logFilter ^ (1 << type.ordinal());
    }
}
