package electrosphere.engine.loadingthreads;

import java.util.concurrent.TimeUnit;

import org.joml.Vector3f;

import electrosphere.client.fluid.cells.FluidCellManager;
import electrosphere.client.foliagemanager.ClientFoliageManager;
import electrosphere.client.sim.ClientSimulation;
import electrosphere.client.targeting.crosshair.Crosshair;
import electrosphere.client.terrain.cells.DrawCellManager;
import electrosphere.controls.ControlHandler;
import electrosphere.engine.Globals;
import electrosphere.engine.threads.LabeledThread.ThreadLabel;
import electrosphere.entity.DrawableUtils;
import electrosphere.entity.Entity;
import electrosphere.entity.EntityCreationUtils;
import electrosphere.entity.EntityUtils;
import electrosphere.entity.types.camera.CameraEntityUtils;
import electrosphere.logger.LoggerInterface;
import electrosphere.menu.MenuGenerators;
import electrosphere.menu.WindowStrings;
import electrosphere.menu.WindowUtils;
import electrosphere.menu.mainmenu.MenuGeneratorsMultiplayer;
import electrosphere.net.NetUtils;
import electrosphere.net.client.ClientNetworking;
import electrosphere.renderer.ui.elements.Window;

public class ClientLoading {


    protected static void loadCharacterServer(Object[] params){
        Window loadingWindow = (Window)Globals.elementManager.getWindow(WindowStrings.WINDOW_LOADING);
        WindowUtils.recursiveSetVisible(Globals.elementManager.getWindow(WindowStrings.WINDOW_MENU_MAIN), false);
        WindowUtils.replaceMainMenuContents(MenuGenerators.createEmptyMainMenu());
        loadingWindow.setVisible(true);
        //disable menu input
        Globals.controlHandler.hintUpdateControlState(ControlHandler.ControlsState.NO_INPUT);
        //initialize the client thread (client)
        initClientThread();
        //while we don't know what races are playable, wait
        while(Globals.gameConfigCurrent.getCreatureTypeLoader().getPlayableRaces().size() == 0){
            try {
                TimeUnit.MILLISECONDS.sleep(5);
            } catch (InterruptedException ex) {}
        }
        //once we have them, bring up the character creation interface
        //init character creation window
        //eventually should replace with at ui to select an already created character or create a new one
        WindowUtils.replaceMainMenuContents(MenuGeneratorsMultiplayer.createMultiplayerCharacterCreationWindow());
        //make loading dialog disappear
        loadingWindow.setVisible(false);
        //make character creation window visible
        WindowUtils.recursiveSetVisible(Globals.elementManager.getWindow(WindowStrings.WINDOW_MENU_MAIN), true);
        //recapture window
        Globals.controlHandler.setRecapture(true);
        //log
        LoggerInterface.loggerEngine.INFO("[Client]Finished loading character creation menu");
        //set menu controls again
        Globals.controlHandler.hintUpdateControlState(ControlHandler.ControlsState.TITLE_MENU);
    }


    protected static void loadClientWorld(Object[] params){
        Window loadingWindow = (Window)Globals.elementManager.getWindow(WindowStrings.WINDOW_LOADING);
        WindowUtils.recursiveSetVisible(Globals.elementManager.getWindow(WindowStrings.WINDOW_MENU_MAIN), false);
        WindowUtils.replaceMainMenuContents(MenuGenerators.createEmptyMainMenu());
        loadingWindow.setVisible(true);
        //disable menu input
        Globals.controlHandler.hintUpdateControlState(ControlHandler.ControlsState.NO_INPUT);
        //initialize the "real" objects simulation
        initClientSimulation();
        //init foliage manager
        initFoliageManager();
        //initialize the cell manager (client)
        initDrawCellManager();
        //init the fluid cell manager
        initFluidCellManager();
        //initialize the basic graphical entities of the world (skybox, camera)
        initWorldBaseGraphicalEntities();
        //init arena specific stuff (ie different skybox colors)
        initArenaGraphicalEntities();
        //sets micro and macro sims to ready if they exist
        setSimulationsToReady();
        //make loading window disappear
        loadingWindow.setVisible(false);
        //recapture screen
        Globals.controlHandler.setRecapture(true);
        //set rendering flags to main game mode
        Globals.RENDER_FLAG_RENDER_SHADOW_MAP = true;
        Globals.RENDER_FLAG_RENDER_SCREEN_FRAMEBUFFER_CONTENT = true;
        Globals.RENDER_FLAG_RENDER_SCREEN_FRAMEBUFFER = true;
        Globals.RENDER_FLAG_RENDER_UI = true;
        Globals.RENDER_FLAG_RENDER_BLACK_BACKGROUND = false;
        Globals.RENDER_FLAG_RENDER_WHITE_BACKGROUND = false;
        LoggerInterface.loggerEngine.INFO("[Client]Finished loading main game");
        //set controls state
        Globals.controlHandler.hintUpdateControlState(ControlHandler.ControlsState.MAIN_GAME);
    }










    /**
     * Inits the client networking thread and socket
     */
    private static void initClientThread(){
        //start client networking
        if(Globals.RUN_CLIENT){
            Globals.clientConnection = new ClientNetworking(NetUtils.getAddress(),NetUtils.getPort());
            Globals.threadManager.start(ThreadLabel.NETWORKING_CLIENT, new Thread(Globals.clientConnection));
        }
    }

    /**
     * Creates client simulation object
     */
    private static void initClientSimulation(){
        if(Globals.clientSimulation == null){
            Globals.clientSimulation = new ClientSimulation();
        }
    }

    /**
     * Sets client simulation object state to ready
     */
    private static void setSimulationsToReady(){
        Globals.clientSimulation.setReady(true);
    }


    private static void initWorldBaseGraphicalEntities(){
        /*
        
        Skybox
        
        */
        // Model skyboxModel = Globals.assetManager.fetchModel(AssetDataStrings.ASSET_STRING_SKYBOX_BASIC);
        // Globals.skybox = EntityUtils.spawnDrawableEntity(AssetDataStrings.ASSET_STRING_SKYBOX_BASIC);
        
        
        /*
        
        Player Camera
        
        */
        CameraEntityUtils.initCamera();

        
        /*
        Targeting crosshair
        */
        Crosshair.initCrossHairEntity();
        
    }


    static void initArenaGraphicalEntities(){

        float skyR = 150;
        float skyG = 200;
        float skyB = 250;

        float groundR = 20;
        float groundG = 20;
        float groundB = 20;

        Globals.skyboxColors.add(new Vector3f(skyR,skyG,skyB));
        Globals.skyboxColors.add(new Vector3f(skyR,skyG,skyB));
        Globals.skyboxColors.add(new Vector3f(groundR,groundG,groundB));
        Globals.skyboxColors.add(new Vector3f(groundR,groundG,groundB));
        Globals.skyboxColors.add(new Vector3f(skyR,skyG,skyB));
        Globals.skyboxColors.add(new Vector3f(skyR,skyG,skyB));
        Globals.skyboxColors.add(new Vector3f(groundR,groundG,groundB));
        Globals.skyboxColors.add(new Vector3f(groundR,groundG,groundB));
        
        //starry sky true skybox
        Entity skybox = EntityCreationUtils.createClientSpatialEntity();
        EntityCreationUtils.makeEntityDrawable(skybox, "Models/environment/skyboxSphere.fbx");
        DrawableUtils.disableCulling(skybox);
        EntityUtils.getRotation(skybox).rotateX((float)(-Math.PI/2.0f));
        EntityUtils.getScale(skybox).mul(200000.0f);
        Globals.assetManager.queueOverrideMeshShader("Models/environment/skyboxSphere.fbx", "Sphere", "Shaders/skysphere/skysphere.vs", "Shaders/skysphere/skysphere.fs");

        //cloud ring pseudo skybox
        // Entity cloudRing = EntityCreationUtils.createClientSpatialEntity();
        // EntityCreationUtils.makeEntityDrawable(cloudRing, "Models/environment/cloudRing.fbx");
        // DrawableUtils.makeEntityTransparent(cloudRing);
        // DrawableUtils.disableCulling(cloudRing);
        // EntityUtils.getRotation(cloudRing).rotateX((float)(-Math.PI/2.0f));
        // EntityUtils.getScale(cloudRing).mul(100000.0f);
        // Globals.clientScene.registerBehaviorTree(new ApplyRotationTree(cloudRing,new Quaterniond().rotationZ(0.0001)));
        // Globals.assetManager.queueOverrideMeshShader("Models/environment/cloudRing.fbx", "Sphere", "Shaders/skysphere/skysphere.vs", "Shaders/skysphere/skysphere.fs");

        //player's cursor
        Globals.playerCursor = EntityCreationUtils.createClientSpatialEntity();
        EntityCreationUtils.makeEntityDrawable(Globals.playerCursor, "Models/basic/geometry/unitsphere_1.fbx");
        DrawableUtils.makeEntityTransparent(Globals.playerCursor);
        EntityUtils.getScale(Globals.playerCursor).set(30f);
    }

    static final int MAX_DRAW_CELL_WAIT = 1000;

    static void initDrawCellManager(){
        int iterations = 0;
        while(Globals.clientWorldData == null || Globals.initialAssetLoadingThread.isLoading()){
            try {
                TimeUnit.MILLISECONDS.sleep(10);
                iterations++;
            } catch (InterruptedException ex) {
                LoggerInterface.loggerEngine.ERROR(ex);
            }
            if(iterations > MAX_DRAW_CELL_WAIT){
                String message = "Draw cell took too long to init!\n" +
                Globals.clientWorldData + "\n" +
                Globals.initialAssetLoadingThread.isLoading();
                throw new IllegalStateException(message);
            }
        }
        //initialize draw cell manager
        Globals.drawCellManager = new DrawCellManager(Globals.clientTerrainManager, 0, 0, 0);
        //construct texture atlas
        Globals.drawCellManager.attachTextureAtlas(Globals.voxelTextureAtlas);
        //set our draw cell manager to actually generate drawable chunks
        Globals.drawCellManager.setGenerateDrawables(true);
        //Alerts the client simulation that it should start loading terrain
        Globals.clientSimulation.setLoadingTerrain(true);
        //wait for all the terrain data to arrive
        while(Globals.drawCellManager.containsUnrequestedCell()){
//            Globals.drawCellManager.updateInvalidCell();
            try {
                TimeUnit.MILLISECONDS.sleep(10);
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            }
//            System.out.println("invalid cell");
        }
        
        while(Globals.drawCellManager.containsUndrawableCell()){
//            Globals.drawCellManager.makeCellDrawable();
            try {
                TimeUnit.MILLISECONDS.sleep(10);
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            }
//            System.out.println("undrawable");
        }
        
        // while(Globals.drawCellManager.containsPhysicsNeedingCell()){
        //     try {
        //         TimeUnit.MILLISECONDS.sleep(10);
        //     } catch (InterruptedException ex) {
        //     }
        // }
        // System.out.println("Draw Cell Manager ready");
    }

    static void initFluidCellManager(){
        while(Globals.clientWorldData == null){
            try {
                TimeUnit.MILLISECONDS.sleep(10);
            } catch (InterruptedException ex) {
            }
        }
        //initialize draw cell manager
        Globals.fluidCellManager = new FluidCellManager(Globals.clientTerrainManager, 0, 0, 0);
        //set our draw cell manager to actually generate drawable chunks
        Globals.fluidCellManager.setGenerateDrawables(true);
        //Alerts the client simulation that it should start loading terrain
        Globals.clientSimulation.setLoadingTerrain(true);
        //wait for all the terrain data to arrive
        while(Globals.fluidCellManager.containsUnrequestedCell()){
//            Globals.drawCellManager.updateInvalidCell();
            try {
                TimeUnit.MILLISECONDS.sleep(10);
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            }
//            System.out.println("invalid cell");
        }
        
        while(Globals.fluidCellManager.containsUndrawableCell()){
//            Globals.drawCellManager.makeCellDrawable();
            try {
                TimeUnit.MILLISECONDS.sleep(10);
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            }
//            System.out.println("undrawable");
        }
    }

    /**
     * Starts up the foliage manager
     */
    private static void initFoliageManager(){
        Globals.clientFoliageManager = new ClientFoliageManager();
        Globals.clientFoliageManager.start();
    }




}
