package electrosphere.main;

import electrosphere.renderer.light.PointLight;
import electrosphere.renderer.light.SpotLight;
import electrosphere.renderer.Material;
import electrosphere.renderer.Model;
import electrosphere.renderer.texture.Texture;
import electrosphere.renderer.texture.TextureMap;
import com.google.gson.Gson;
import electrosphere.audio.AudioEngine;
import electrosphere.controls.ControlHandler;
import electrosphere.entity.Entity;
import electrosphere.entity.EntityManager;
import electrosphere.game.collision.CollisionEngine;
import electrosphere.entity.types.hitbox.HitboxManager;
import electrosphere.game.client.cells.DrawCellManager;
import electrosphere.game.client.player.ClientPlayerData;
import electrosphere.game.client.terrain.manager.ClientTerrainManager;
import electrosphere.game.client.world.ClientWorldData;
import electrosphere.game.collision.CommonWorldData;
import electrosphere.engine.LoadingThread;
import electrosphere.game.config.UserSettings;
import electrosphere.game.server.ai.AIManager;
import electrosphere.game.server.character.Character;
import electrosphere.game.data.creature.type.model.CreatureTypeMap;
import electrosphere.game.data.item.type.model.ItemTypeMap;
import electrosphere.game.data.structure.type.model.StructureTypeMap;
import electrosphere.game.server.db.DatabaseController;
import electrosphere.game.server.structure.virtual.Structure;
import electrosphere.game.server.structure.virtual.StructureManager;
import electrosphere.game.simulation.MacroSimulation;
import electrosphere.game.server.terrain.manager.ServerTerrainManager;
import electrosphere.game.server.town.Town;
import electrosphere.game.server.world.ServerWorldData;
import electrosphere.game.server.world.MacroData;
import electrosphere.game.server.datacell.DataCellManager;
import electrosphere.game.simulation.MicroSimulation;
import electrosphere.menu.Menu;
import electrosphere.net.client.ClientNetworking;
import electrosphere.net.server.Player;
import electrosphere.net.server.PlayerManager;
import electrosphere.net.server.Server;
import electrosphere.renderer.ModelUtils;
import electrosphere.renderer.RenderUtils;
import electrosphere.renderer.RenderingEngine;
import electrosphere.renderer.ShaderProgram;
import electrosphere.engine.assetmanager.AssetDataStrings;
import electrosphere.engine.assetmanager.AssetManager;
import electrosphere.game.server.pathfinding.NavMeshManager;
import electrosphere.renderer.ui.Widget;
import electrosphere.renderer.ui.WidgetManager;
import electrosphere.renderer.ui.WidgetUtils;
import electrosphere.renderer.ui.font.FontUtils;
import electrosphere.renderer.ui.font.RawFontMap;
import electrosphere.renderer.ui.font.TextBox;
import electrosphere.util.FileUtils;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.joml.Matrix4f;
import org.joml.Vector3f;
import electrosphere.util.ModelLoader;
import electrosphere.util.Utilities;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import org.lwjgl.glfw.GLFWErrorCallback;

/**
 *
 * @author amaterasu
 */
public class Globals {
    
    //
    //Top level user settings object
    //
    public static UserSettings userSettings;
    
    //
    //Rendering Engine
    //
    public static RenderingEngine renderingEngine;
    
    //
    //Audio Engine
    //
    public static AudioEngine audioEngine;
    
    
    //
    //Core Engine signals
    //
    public static boolean ENGINE_SHUTDOWN_FLAG = false;
    
    
    //
    //Client connection to server
    //
    public static ClientNetworking clientConnection;
    public static boolean RUN_CLIENT = true;
    public static int clientCharacterID;
    
    //
    //Server manager thing
    //
    public static Thread serverThread;
    public static Server server;
    public static boolean RUN_SERVER = true;
    
    
    //
    //Controls Handler
    //
    public static ControlHandler controlHandler;
    public static boolean updateCamera = true;
    
    
    //
    // Collision engine
    //
    public static CollisionEngine collisionEngine;
    
    
    //
    // Game Save stuff
    //
    public static String currentSaveName = "default";
    
    
    //
    // Game config
    //
    public static electrosphere.game.data.Config gameConfigDefault;
    public static electrosphere.game.data.Config gameConfigCurrent;
    
    //
    // Database stuff
    //
    public static DatabaseController dbController = new DatabaseController();
    
    //
    //current world
    //
    public static ServerWorldData serverWorldData;
    public static DataCellManager dataCellManager;
    
    //
    //Player manager
    //
    public static PlayerManager playerManager;
    public static Player serverPlayer;
    
    //
    //Generic OpenGL Statements
    //
    //Callback function for opengl errors
    public final static GLFWErrorCallback ERROR_CALLBACK = GLFWErrorCallback.createPrint(System.err);
    public static long window;
    
    
    
    //
    //OpenGL - Other
    //
    
    public static int WINDOW_WIDTH = 1920;
    public static int WINDOW_HEIGHT = 1080;
    
    //title bar dimensions
    public static int WINDOW_TITLE_BAR_HEIGHT = 0;
    
    public static float FOV = 90;
    
    //matrices for drawing models
    public static Matrix4f viewMatrix = new Matrix4f();
    public static Matrix4f projectionMatrix;
    public static Matrix4f lightDepthMatrix = new Matrix4f();
    
    //locations for shadow map specific variables
    public static int shadowMapTextureLoc = 0;
    public static int depthMapShaderProgramLoc = 0;
    
    
    
    
    
    
    //
    //OpenGL - Abstracted engine objects
    //
    public static String textureDiffuseDefault;
    public static String textureSpecularDefault;
    public static Material materialDefault;
    
    public static String blackTexture;
    public static String testingTexture;
    public static String whiteTexture;
    public static String offWhiteTexture;
    
    public static String planeModelID;
    
    public static ArrayList<PointLight> lightPointListDefault;
    public static SpotLight lightSpotDefault;
    
    public static TextureMap textureMapDefault;
    
    public static ShaderProgram defaultMeshShader;
    public static ShaderProgram terrainShaderProgram;
    
    public static Menu currentMenu;
    
    public static List<Widget> inGameUI = new LinkedList();
    
    public static String particleBillboardModel;
    
    
    
    
    //
    //Engine - Main managers/variables
    //
    //keeps track of all entities in game
    public static EntityManager entityManager;
    
    //terrain manager
//    public static boolean LOAD_TERRAIN = true;
    public static ServerTerrainManager serverTerrainManager;
    public static Vector3f spawnPoint = new Vector3f(0,0,0);
    
    //manages all models loaded into memory
    public static AssetManager assetManager;
    
    //macro simulation
    public static MacroSimulation macroSimulation;
    public static MacroData macroData;
    
    //micro simulation
    public static MicroSimulation microSimulation;
    
    
    //manages hitboxes
    public static HitboxManager hitboxManager;
    
    //client world data
    public static ClientWorldData clientWorldData;
    
    //client terrain manager
    public static ClientTerrainManager clientTerrainManager;
    
    //client player data
    public static ClientPlayerData clientPlayerData = new ClientPlayerData();
    
    //chunk stuff
    //constant for how far in game units you have to move to load chunks
    public static DrawCellManager drawCellManager;
    
    //navmesh manager
    public static NavMeshManager navMeshManager;
    
    //famous fuckin last words, but temporary solution
    //global arraylist of values for the skybox colors
    //edit(6/1/21):    :upside_down_smile:
    public static ArrayList<Vector3f> skyboxColors;
    
    //thread for loading different game states
    public static LoadingThread loadingThread;
    
    //manager for all widgets currently being drawn to screen
    public static WidgetManager widgetManager;
    
    //ui text box for loading text
    public static TextBox loadingBox;
    
    //collision world data
    public static CommonWorldData commonWorldData;
    
    //structure manager
    public static StructureManager structureManager;
    
    
    //the player camera entity
    public static Entity playerCamera;
    
    //the creature the player camera will orbit and will receive controlHandler movementTree updates
    public static Entity playerCharacter;
    
    //skybox entity
    public static Entity skybox;
    
    
    //ai manager
    public static AIManager aiManager;
    
    
    
    
    
    
    
    //
    //
    // Renderer flags
    //
    //
    public static boolean RENDER_FLAG_RENDER_SHADOW_MAP = false;
    public static boolean RENDER_FLAG_RENDER_SCREEN_FRAMEBUFFER_CONTENT = false;
    public static boolean RENDER_FLAG_RENDER_SCREEN_FRAMEBUFFER = false;
    public static boolean RENDER_FLAG_RENDER_BLACK_BACKGROUND = false;
    public static boolean RENDER_FLAG_RENDER_WHITE_BACKGROUND = true;
    public static boolean RENDER_FLAG_RENDER_UI = true;
    
    
    
    public static void initGlobals(){
        
        //load in default texture map
        Gson gson = new Gson();
//        try {
            //deserializes the texture map from its default path using gson
            //also done in one line
            textureMapDefault = FileUtils.loadObjectFromAssetPath("Textures/default_texture_map.json", TextureMap.class);
//            textureMapDefault = gson.fromJson(Files.newBufferedReader(new File(Thread.currentThread().getContextClassLoader().getResource("Textures/default_texture_map.json").getFile()).toPath()), TextureMap.class); //only the best of coding practices :)
//        } catch (IOException ex) { ex.printStackTrace(); } //TODO: handle better :tm:
        //create entity manager
        entityManager = new EntityManager();
        //temporary hold for skybox colors
        skyboxColors = new ArrayList();
        //load asset manager
        assetManager = new AssetManager();
        //load widget manager
        widgetManager = new WidgetManager();
        //hitbox manager
        hitboxManager = new HitboxManager();
        //ai manager
        aiManager = new AIManager();
        //collision engine
        collisionEngine = new CollisionEngine();
        //nav mesh manager
        navMeshManager = new NavMeshManager();
        //game config
        gameConfigDefault = electrosphere.game.data.Config.loadDefaultConfig();
        gameConfigCurrent = gameConfigDefault;
        //player manager
        playerManager = new PlayerManager();
    }
    
    public static void initDefaultAudioResources(){
        Globals.assetManager.addAudioPathToQueue("/Audio/MenuBackspace.ogg");
        Globals.assetManager.addAudioPathToQueue("/Audio/MenuBadOption.ogg");
        Globals.assetManager.addAudioPathToQueue("/Audio/MenuChangeOption.ogg");
        Globals.assetManager.addAudioPathToQueue("/Audio/MenuType.ogg");
    }
    
    public static void initDefaultGraphicalResources(){
        //create default textures
        Globals.assetManager.addTexturePathtoQueue("Textures/default_diffuse.png");
        Globals.assetManager.addTexturePathtoQueue("Textures/default_specular.png");
        //create default material
        materialDefault = new Material();
        materialDefault.set_diffuse("Textures/default_diffuse.png");
        materialDefault.set_specular("Textures/default_specular.png");
        //create default lights
        assetManager.registerModelToSpecificString(ModelUtils.createBitmapDisplay(), AssetDataStrings.ASSET_STRING_BITMAP_FONT);
        assetManager.registerModelToSpecificString(ModelUtils.createBitmapCharacter(), AssetDataStrings.BITMAP_CHARACTER_MODEL);
        RawFontMap fontMap = FileUtils.loadObjectFromAssetPath("Textures/Fonts/myFontMap.json", RawFontMap.class);
        FontUtils.setFontDataMap(fontMap);
        //particle billboard model
        particleBillboardModel = assetManager.registerModel(RenderUtils.createParticleModel());
        //black texture for backgrouns
        blackTexture = "Textures/b1.png";
        Globals.assetManager.addTexturePathtoQueue(blackTexture);
        //white texture for backgrounds
        whiteTexture = "Textures/w1.png";
        Globals.assetManager.addTexturePathtoQueue(whiteTexture);
        //off white texture for backgrounds
        offWhiteTexture = "Textures/ow1.png";
        Globals.assetManager.addTexturePathtoQueue(offWhiteTexture);
        //loading box
        loadingBox = WidgetUtils.createVerticallyAlignedTextBox(520, 100, 50, 7, 1, "LOADING", true);
        //init default shaderProgram
        defaultMeshShader = ShaderProgram.smart_assemble_shader(false,true);
        //init terrain shader program
        terrainShaderProgram = ShaderProgram.loadSpecificShader("/Shaders/terrain/terrain.vs", "/Shaders/terrain/terrain.fs");
        //init skybox
        assetManager.registerModelToSpecificString(RenderUtils.createSkyboxModel(null), AssetDataStrings.ASSET_STRING_SKYBOX_BASIC);
        //init models
        assetManager.addModelPathToQueue("Models/unitsphere.fbx");
        assetManager.addModelPathToQueue("Models/unitsphere_1.fbx");
        assetManager.addModelPathToQueue("Models/unitsphere_grey.fbx");
        assetManager.addModelPathToQueue("Models/SmallCube.fbx");
        assetManager.addModelPathToQueue("Models/unitcylinder.fbx");
        assetManager.addModelPathToQueue("Models/unitplane.fbx");
        assetManager.addModelPathToQueue("Models/unitcube.fbx");
        planeModelID = assetManager.registerModel(RenderUtils.createPlaneModel());
        
        //init ui images
        assetManager.addTexturePathtoQueue("Textures/ui/WindowBorder.png");
        
        testingTexture = "Textures/Testing1.png";
        Globals.assetManager.addTexturePathtoQueue(testingTexture);
        
        //in game ui stuff
        inGameUI.add(WidgetUtils.createInGameMainMenuButton());
        
        
        
        //as these assets are required for the renderer to work, we go ahead and
        //load them into memory now. The loading time penalty is worth it I think.
        Globals.assetManager.loadAssetsInQueue();
    }
}
