package electrosphere.engine.loadingthreads;

import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;

import org.joml.Vector3d;
import org.joml.Vector3f;
import org.joml.Vector3i;

import electrosphere.auth.AuthenticationManager;
import electrosphere.engine.Globals;
import electrosphere.entity.types.creature.CreatureTemplate;
import electrosphere.game.data.creature.type.CreatureData;
import electrosphere.game.data.creature.type.visualattribute.VisualAttribute;
import electrosphere.game.server.world.MacroData;
import electrosphere.net.NetUtils;
import electrosphere.net.client.ClientNetworking;
import electrosphere.net.parser.net.message.CharacterMessage;
import electrosphere.net.server.Server;
import electrosphere.net.server.ServerConnectionHandler;
import electrosphere.net.server.player.Player;
import electrosphere.server.datacell.Realm;
import electrosphere.server.simulation.MacroSimulation;
import electrosphere.server.simulation.MicroSimulation;

/**
 * Utilities for all loading thread types
 */
public class LoadingUtils {
    
    
    // static void initCommonWorldData(boolean FLAG_INIT_SERVER){
    //     if(Globals.commonWorldData == null){
    //         if(FLAG_INIT_SERVER){
    //             Globals.commonWorldData = new CommonWorldData(Globals.serverWorldData, Globals.serverTerrainManager);
    //             if(Globals.macroSimulation != null){
    //                 Town startTown = Globals.macroData.getTowns().get(0);
    //                 Vector2i firstPos = startTown.getPositions().get(0);
    //                 // double startX = firstPos.x * Globals.serverTerrainManager.getChunkWidth();
    //                 // double startZ = firstPos.y * Globals.serverTerrainManager.getChunkWidth();
    //                 double startX = Globals.commonWorldData.convertWorldToReal(firstPos.x);
    //                 double startZ = Globals.commonWorldData.convertWorldToReal(firstPos.y);
    //                 Globals.spawnPoint.set((float)startX,(float)Globals.commonWorldData.getElevationAtPoint(new Vector3d(startX,0,startZ)),(float)startZ);
    //             }
    //         } else {
    //             //basically wait for the client to receive the world metadata
    //             while(!Globals.clientConnection.getClientProtocol().hasReceivedWorld()){
    //                 try {
    //                     TimeUnit.MILLISECONDS.sleep(5);
    //                 } catch (InterruptedException ex) {
    //                 }
    //             }
    //             //then create common world data
    //             Globals.commonWorldData = new CommonWorldData(Globals.clientWorldData, Globals.clientTerrainManager);
    //         }
    //     }
    // }
    
    
    
    static void initServerThread(){
        //start server networking
        if(Globals.RUN_SERVER){
            Globals.server = new Server(NetUtils.getPort());
            Globals.serverThread = new Thread(Globals.server);
            Globals.serverThread.start();
        }
    }

    static void initAuthenticationManager(){
        if(Globals.RUN_SERVER){
            Globals.authenticationManager = new AuthenticationManager();
        }
    }
    
    
    
    static void initClientThread(){
        //start client networking
        Thread clientThread = null;
        if(Globals.RUN_CLIENT){
            Globals.clientConnection = new ClientNetworking(NetUtils.getAddress(),NetUtils.getPort());
            clientThread = new Thread(Globals.clientConnection);
            clientThread.start();
        }
    }

    static final int STREAM_BUFFER_SIZE = 32 * 1024 * 1024;
    static ServerConnectionHandler initLocalConnection(){
        ServerConnectionHandler rVal = null;
        try {
            Globals.server = new Server(NetUtils.getPort());
            //client -> server pipe
            PipedInputStream clientInput = new PipedInputStream(STREAM_BUFFER_SIZE);
            PipedOutputStream serverOutput = new PipedOutputStream(clientInput);
            //server -> client pipe
            PipedInputStream serverInput = new PipedInputStream();
            PipedOutputStream clientOutput;
            clientOutput = new PipedOutputStream(serverInput);
            //start server communication thread
            rVal = Globals.server.addLocalPlayer(serverInput, serverOutput);
            //start client communication thread
            Globals.clientConnection = new ClientNetworking(clientInput,clientOutput);
            Thread clientThread = null;
            clientThread = new Thread(Globals.clientConnection);
            clientThread.start();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return rVal;
    }


    /**
     * Loads graphics assets necessary for the client of the game engine. This should be stuff that is used essentially universally (ie textures for debugging).
     */
    static void initGameGraphicalEntities(){

        Globals.assetManager.addTexturePathtoQueue("Textures/transparent_red.png");
        Globals.assetManager.addTexturePathtoQueue("Textures/transparent_blue.png");
        Globals.assetManager.addTexturePathtoQueue("Textures/transparent_grey.png");


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

        float groundR = 50;
        float groundG = 100;
        float groundB = 150;

        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));

    }


    /**
     * Spawns the character, and sets server side connection player object values to the appropriate chunk
     */
    static void spawnLocalPlayerTestEntity(ServerConnectionHandler serverPlayerConnection){
        //
        //Create entity
        //
        //send default template back
        String race = Globals.gameConfigCurrent.getCreatureTypeLoader().getPlayableRaces().get(0);
        CreatureData type = Globals.gameConfigCurrent.getCreatureTypeLoader().getCreature(race);
        CreatureTemplate template = CreatureTemplate.create(race);
        for(VisualAttribute attribute : type.getVisualAttributes()){
            if(attribute.getType().equals(VisualAttribute.TYPE_BONE)){
                float min = attribute.getMinValue();
                float max = attribute.getMaxValue();
                float defaultValue = min + (max - min)/2.0f;
                //add attribute to creature template
                template.putAttributeValue(attribute.getAttributeId(), defaultValue);
            } else if(attribute.getType().equals(VisualAttribute.TYPE_REMESH)){
                template.putAttributeValue(attribute.getAttributeId(), attribute.getVariants().get(0).getId());
            }
        }
        //set player character template
        serverPlayerConnection.setCreatureTemplate(template);
        Globals.clientConnection.queueOutgoingMessage(CharacterMessage.constructRequestSpawnCharacterMessage());

        //set player world-space coordinates
        Player playerObject = Globals.playerManager.getPlayerFromId(0);
        Realm realm = Globals.realmManager.getRealms().iterator().next();
        Vector3d spawnPoint = realm.getSpawnPoint();
        playerObject.setWorldPos(new Vector3i(
            realm.getServerWorldData().convertRealToChunkSpace(spawnPoint.x),
            realm.getServerWorldData().convertRealToChunkSpace(spawnPoint.y),
            realm.getServerWorldData().convertRealToChunkSpace(spawnPoint.z)
        ));
    }

    static void initMacroSimulation(){
        Globals.macroData = MacroData.generateWorld(0);
//        Globals.macroData.describeWorld();
        Globals.macroSimulation = new MacroSimulation();
        Globals.macroSimulation.simulate();
//        Town startTown = Globals.macroData.getTowns().get(0);
//        Vector2i firstPos = startTown.getPositions().get(0);
//        double startX = firstPos.x * Globals.serverTerrainManager.getChunkWidth();
//        double startZ = firstPos.y * Globals.serverTerrainManager.getChunkWidth();
//        Globals.spawnPoint.set((float)startX,(float)Globals.commonWorldData.getElevationAtPoint(new Vector3d(startX,0,startZ)),(float)startZ);
//        Globals.macroSimulation.setReady(true);
    }

    static void initMicroSimulation(){
        if(Globals.microSimulation == null){
            Globals.microSimulation = new MicroSimulation();
        }
    }
    
    static void setSimulationsToReady(){
        Globals.microSimulation.setReady(true);
        if(Globals.macroSimulation != null){
            Globals.macroSimulation.setReady(true);
        }
    }

}
