package CK.IA;

import java.io.File;
import java.util.HashSet;
import java.util.Hashtable;

import org.lwjgl.LWJGLException;
import org.lwjgl.input.Keyboard;
import org.lwjgl.opengl.Display;

import com.threed.jpct.Camera;
import com.threed.jpct.Config;
import com.threed.jpct.FrameBuffer;
import com.threed.jpct.IRenderer;
import com.threed.jpct.Loader;
import com.threed.jpct.Object3D;
import com.threed.jpct.OcTree;
import com.threed.jpct.SimpleVector;
import com.threed.jpct.Texture;
import com.threed.jpct.TextureManager;
import com.threed.jpct.World;

public class Engine {
	public static void main(String[] args) throws Exception {
		new Engine();
	}

	private HashSet<Entity> blue_gifts;
	private HashSet<SoldierAgent> blue_soldier_agents;
	private SpawnAgent blue_spawn;
	private FrameBuffer buffer;
	private Camera camera;
	private EnvironmentAgent environment;
	private boolean executing;
	private GoalSimulator goal_simulator;
	private boolean key_c_pressed;
	private boolean key_f1_pressed;
	private float movement_multiplier;
	private Object3D o3d_axis;
	private Object3D o3d_blue_gift;
	private Hashtable<Entity, Object3D> o3d_blue_gifts;
	private Object3D o3d_blue_robot;
	private Hashtable<SoldierAgent, Object3D> o3d_blue_robots;
	private Object3D o3d_blue_spawn;
	private Object3D o3d_map;
	private Object3D o3d_map_transparent;
	private Object3D o3d_red_gift;
	private Hashtable<Entity, Object3D> o3d_red_gifts;
	private Object3D o3d_red_robot;
	private Hashtable<SoldierAgent, Object3D> o3d_red_robots;
	private Object3D o3d_red_spawn;
	private Object3D o3d_skybox;
	private Object3D o3d_skybox_layer1;
	private Vector3d position;
	private HashSet<Entity> red_gifts;
	private HashSet<SoldierAgent> red_soldier_agents;
	private SpawnAgent red_spawn;
	private float skybox_layer1_slide;
	private float skybox_slide;
	private TextureManager texMan;
	private World world;

	public Engine() {
		initialize();
		goal_simulator.setPaused(false);
		loop();
	}

	private void checkKeyboard() {
		if (Keyboard.isKeyDown(Keyboard.KEY_ESCAPE)) {
			executing = false;
		}
		movement_multiplier = GoalSimulatorConfig.CAMERA_MOVEMENT_MULTIPLIER_NORMAL;
		// RUN
		if (Keyboard.isKeyDown(Keyboard.KEY_LSHIFT) || Keyboard.isKeyDown(Keyboard.KEY_RSHIFT)) {
			movement_multiplier = GoalSimulatorConfig.CAMERA_MOVEMENT_MULTIPLIER_FAST;
		}
		// JUMP
		if (Keyboard.isKeyDown(Keyboard.KEY_SPACE)) {
			camera.moveCamera(Camera.CAMERA_MOVEUP, GoalSimulatorConfig.CAMERA_MOVEMENT_CROUCH_JUMP_SPEED
					* movement_multiplier);
		}
		// CROUCH
		if (Keyboard.isKeyDown(Keyboard.KEY_LCONTROL) || Keyboard.isKeyDown(Keyboard.KEY_RCONTROL)) {
			camera.moveCamera(Camera.CAMERA_MOVEDOWN, GoalSimulatorConfig.CAMERA_MOVEMENT_CROUCH_JUMP_SPEED
					* movement_multiplier);
		}
		// FORWARD
		if (Keyboard.isKeyDown(Keyboard.KEY_W)) {
			camera.moveCamera(Camera.CAMERA_MOVEIN, GoalSimulatorConfig.CAMERA_MOVEMENT_FORWARD_BACKWARD_SPEED
					* movement_multiplier);
		}
		// BACKWARD
		if (Keyboard.isKeyDown(Keyboard.KEY_S)) {
			camera.moveCamera(Camera.CAMERA_MOVEOUT, GoalSimulatorConfig.CAMERA_MOVEMENT_FORWARD_BACKWARD_SPEED
					* movement_multiplier);
		}
		// STRAFE LEFT
		if (Keyboard.isKeyDown(Keyboard.KEY_A)) {
			camera.moveCamera(Camera.CAMERA_MOVELEFT, GoalSimulatorConfig.CAMERA_MOVEMENT_STRAFE_SPEED
					* movement_multiplier);
		}
		// STRAFE RIGHT
		if (Keyboard.isKeyDown(Keyboard.KEY_D)) {
			camera.moveCamera(Camera.CAMERA_MOVERIGHT, GoalSimulatorConfig.CAMERA_MOVEMENT_STRAFE_SPEED
					* movement_multiplier);
		}
		// ROLL LEFT
		if (Keyboard.isKeyDown(Keyboard.KEY_Q)) {
			camera.rotateZ(GoalSimulatorConfig.CAMERA_ROTATION_ROLL_SPEED * movement_multiplier);
		}
		// ROLL RIGHT
		if (Keyboard.isKeyDown(Keyboard.KEY_E)) {
			camera.rotateZ(-GoalSimulatorConfig.CAMERA_ROTATION_ROLL_SPEED * movement_multiplier);
		}
		// PITCH UP
		if (Keyboard.isKeyDown(Keyboard.KEY_UP)) {
			camera.rotateX(GoalSimulatorConfig.CAMERA_ROTATION_PITCH_SPEED * movement_multiplier);
		}
		// PITCH DOWN
		if (Keyboard.isKeyDown(Keyboard.KEY_DOWN)) {
			camera.rotateX(-GoalSimulatorConfig.CAMERA_ROTATION_PITCH_SPEED * movement_multiplier);
		}
		// YAW LEFT
		if (Keyboard.isKeyDown(Keyboard.KEY_LEFT)) {
			camera.rotateY(GoalSimulatorConfig.CAMERA_ROTATION_YAW_SPEED * movement_multiplier);
		}
		// YAW RIGHT
		if (Keyboard.isKeyDown(Keyboard.KEY_RIGHT)) {
			camera.rotateY(-GoalSimulatorConfig.CAMERA_ROTATION_YAW_SPEED * movement_multiplier);
		}
		// CAMERA DEBUG
		if (Keyboard.isKeyDown(Keyboard.KEY_C)) {
			// if before this key press check the key wasn't pressed, show the debug
			if (!key_c_pressed) {
				debugCamera();
			}
			key_c_pressed = true;
		} else {
			key_c_pressed = false;
		}
		// CAMERA RESET
		if (Keyboard.isKeyDown(Keyboard.KEY_R)) {
			camera.setPosition(GoalSimulatorConfig.CAMERA_POSITION_DEFAULT);
			camera.setOrientation(GoalSimulatorConfig.CAMERA_DIRECTION_DEFAULT,
					GoalSimulatorConfig.CAMERA_UP_DEFAULT);
		}
		// CAMERA REDIRECT
		if (Keyboard.isKeyDown(Keyboard.KEY_T)) {
			camera.setOrientation(GoalSimulatorConfig.CAMERA_DIRECTION_DEFAULT,
					GoalSimulatorConfig.CAMERA_UP_DEFAULT);
		}
		// SIMULATOR PAUSE
		if (Keyboard.isKeyDown(Keyboard.KEY_P)) {
			goal_simulator.setPaused(true);
		}
		// SIMULATOR UNPAUSE
		if (Keyboard.isKeyDown(Keyboard.KEY_U)) {
			goal_simulator.setPaused(false);
		}
		if (Keyboard.isKeyDown(Keyboard.KEY_F1)) {
			if (!key_f1_pressed) {
				try {
					Display.setFullscreen(!Display.isFullscreen());
				} catch (LWJGLException e) {
					e.printStackTrace();
				}
				key_f1_pressed = true;
			} else {
				key_f1_pressed = false;
			}
		}
	}

	private void cloneGifts() {
		log("cloneGifts()");
		log("cloneGifts() done");
		Object3D o3d;
		if (red_gifts != null && red_gifts.size() > 0)
			for (Entity red_gift : red_gifts) {
				o3d = o3d_red_gift.cloneObject();
				world.addObject(o3d);
				o3d_red_gifts.put(red_gift, o3d);
			}
		if (blue_gifts != null && blue_gifts.size() > 0)
			for (Entity blue_gift : blue_gifts) {
				o3d = o3d_blue_gift.cloneObject();
				world.addObject(o3d);
				o3d_blue_gifts.put(blue_gift, o3d);
			}
	}

	private void cloneRobots() {
		log("cloneRobots()");
		Object3D cloneObject;
		for (SoldierAgent rsa : red_soldier_agents) {
			cloneObject = o3d_red_robot.cloneObject();
			world.addObject(cloneObject);
			o3d_red_robots.put(rsa, cloneObject);
		}
		for (SoldierAgent bsa : blue_soldier_agents) {
			cloneObject = o3d_blue_robot.cloneObject();
			world.addObject(cloneObject);
			o3d_blue_robots.put(bsa, cloneObject);
		}
		log("cloneRobots() done");
	}

	private void debugCamera() {
		System.out.println("Camera: position = " + camera.getPosition() + " direction = " + camera.getDirection()
				+ " up = " + camera.getUpVector());
	}

	@SuppressWarnings("unused")
	private void handleKeyboard() {
		while (Keyboard.next()) {
			int eventKey = Keyboard.getEventKey();
			boolean eventKeyState = Keyboard.getEventKeyState();
			if (eventKeyState == true) {
				// checkKeyPresses(eventKey);
				System.out.println("key code " + eventKey + " pressed");
			} else {
				System.out.println("key code " + eventKey + " released");
			}
		}
	}

	private void initialize() {
		log("initialize()");
		System.out.println("working directory: " + GoalSimulatorConfig.CURRENT_PATH);
		executing = true;
		initializeSimulator();
		initializeRenderer();
		initializeScene();
		initializeKeyboard();
		log("initialize() done");
	}

	private void initializeKeyboard() {
		log("initializeKeyboard()");
		synchronized (FrameBuffer.SYNCHRONIZER) {
			if (!Keyboard.isCreated())
				if (Display.isCreated())
					try {
						Keyboard.create();
					} catch (LWJGLException e) {
						e.printStackTrace();
					}
		}
		key_c_pressed = false;
		key_f1_pressed = false;
		log("initializeKeyboard() done");
	}

	private void initializeRenderer() {
		log("initializeRenderer()");
		log("jPCT version: " + Config.getVersion());
		buffer = new FrameBuffer(GoalSimulatorConfig.RENDERER_RESOLUTION_WIDTH,
				GoalSimulatorConfig.RENDERER_RESOLUTION_HEIGHT, FrameBuffer.SAMPLINGMODE_HARDWARE_ONLY);
		// trilinear is good and is not as heavy as anisotropic filtering
		Config.glTrilinear = true;
		// we don't want triangles to disappear...
		Config.maxPolysVisible = 1 << 18;
		// 1024/0.25 ~12 bits depth buffer precision
		Config.farPlane = 1024f;
		Config.nearPlane = 0.25f;
		// jPCT optimizations...
		Config.glTriangleStrips = GoalSimulatorConfig.TRIANGLE_STRIPS_ITERATIONS > 0;
		Config.isIndoor = true;
		// jPCT says this is good so...
		World.setDefaultThread(Thread.currentThread());
		// OpenGL rulez, software here will be sluggish, CPUs needed elsewhere
		buffer.enableRenderer(IRenderer.RENDERER_OPENGL);
		buffer.disableRenderer(IRenderer.RENDERER_SOFTWARE);
		Display.setTitle(GoalSimulatorConfig.WINDOW_TITLE_STARTING);
		// we don't want tearing
		Display.setVSyncEnabled(true);
		log("initializeRenderer() done");
	}

	private void initializeScene() {
		log("initializeScene()");
		skybox_slide = GoalSimulatorConfig.SKYBOX_SLIDE_INITIAL;
		skybox_layer1_slide = GoalSimulatorConfig.SKYBOX_LAYER_1_SLIDE_INITIAL;
		world = new World();
		world.setAmbientLight(255, 255, 255);
		loadTextures();
		loadAndSetupModels();
		// map's stationary
		o3d_map.enableLazyTransformations();
		o3d_map_transparent.enableLazyTransformations();
		o3d_blue_robots = new Hashtable<SoldierAgent, Object3D>();
		o3d_red_robots = new Hashtable<SoldierAgent, Object3D>();
		o3d_blue_gifts = new Hashtable<Entity, Object3D>();
		o3d_red_gifts = new Hashtable<Entity, Object3D>();
		cloneRobots();
		cloneGifts();
		camera = world.getCamera();
		camera.setPosition(GoalSimulatorConfig.CAMERA_POSITION_DEFAULT);
		camera.setOrientation(GoalSimulatorConfig.CAMERA_DIRECTION_DEFAULT, GoalSimulatorConfig.CAMERA_UP_DEFAULT);
		log("initializeScene() done");
	}

	private void initializeSimulator() {
		log("initializeSimulator()");
		goal_simulator = new GoalSimulator();
		goal_simulator.setRunning(true);
		goal_simulator.setPaused(true);
		environment = goal_simulator.getEnvironment();
		red_spawn = environment.getSpawnAgent(0);
		blue_spawn = environment.getSpawnAgent(1);
		red_soldier_agents = environment.getSoldierAgents(0);
		blue_soldier_agents = environment.getSoldierAgents(1);
		red_gifts = environment.getCarriedEntities(0);
		blue_gifts = environment.getCarriedEntities(1);
		log("initializeSimulator() done");
	}

	private void loadAndSetupModels() {
		OcTree oct;
		// where are we loading from?
		String parent_dir = GoalSimulatorConfig.DIRECTORY_MODELS + GoalSimulatorConfig.DIRECTORY_SEPARATOR;

		// skybox
		log("loading " + GoalSimulatorConfig.OBJECT_SKYBOX);
		o3d_skybox = loadObj(parent_dir + GoalSimulatorConfig.OBJECT_SKYBOX
				+ GoalSimulatorConfig.OBJECT_WAVEFRONT_EXTENSION, parent_dir + GoalSimulatorConfig.OBJECT_SKYBOX
				+ GoalSimulatorConfig.OBJECT_MATERIAL_EXTENSION, GoalSimulatorConfig.SKYBOX_SCALE, true);
		o3d_skybox.invert();
		if (GoalSimulatorConfig.TRIANGLE_STRIPS_ITERATIONS > 0) {
			log("creating triangle strips for " + GoalSimulatorConfig.OBJECT_SKYBOX);
			o3d_skybox.createTriangleStrips(GoalSimulatorConfig.TRIANGLE_STRIPS_ITERATIONS);
		}
		o3d_skybox.translate(0.0f, GoalSimulatorConfig.SKYBOX_OFFSET_Y, 0.0f);
		o3d_skybox.translateMesh();
		o3d_skybox.getTranslationMatrix().setIdentity();
		// oct = new OcTree(o3d_skybox, 16, OcTree.MODE_OPTIMIZED);
		// o3d_skybox.setOcTree(oct);
		world.addObject(o3d_skybox);

		// skybox layer 1
		log("loading " + GoalSimulatorConfig.OBJECT_SKYBOX_LAYER_1);
		o3d_skybox_layer1 = loadObj(parent_dir + GoalSimulatorConfig.OBJECT_SKYBOX_LAYER_1
				+ GoalSimulatorConfig.OBJECT_WAVEFRONT_EXTENSION, parent_dir
				+ GoalSimulatorConfig.OBJECT_SKYBOX_LAYER_1 + GoalSimulatorConfig.OBJECT_MATERIAL_EXTENSION,
				GoalSimulatorConfig.SKYBOX_LAYER_1_SCALE, true);
		if (GoalSimulatorConfig.TRIANGLE_STRIPS_ITERATIONS > 0) {
			log("creating triangle strips for " + GoalSimulatorConfig.OBJECT_SKYBOX_LAYER_1);
			o3d_skybox_layer1.createTriangleStrips(GoalSimulatorConfig.TRIANGLE_STRIPS_ITERATIONS);
		}
		o3d_skybox_layer1.invert();
		o3d_skybox_layer1.setTransparency(2);
		// let's set this skybox way down in rendering sort (we don't want it to overlap another objects=
		o3d_skybox_layer1.setSortOffset(1023.0f);
		o3d_skybox_layer1.translate(0.0f, GoalSimulatorConfig.SKYBOX_LAYER_1_OFFSET_Y, 0.0f);
		o3d_skybox_layer1.translateMesh();
		o3d_skybox_layer1.rotateY(GoalSimulatorConfig.SKYBOX_LAYER_1_ANGLE);
		o3d_skybox_layer1.rotateMesh();
		o3d_skybox_layer1.getTranslationMatrix().setIdentity();
		// oct = new OcTree(o3d_skybox_layer1, 16, OcTree.MODE_OPTIMIZED);
		// o3d_skybox_layer1.setOcTree(oct);
		world.addObject(o3d_skybox_layer1);

		// axis
		log("loading " + GoalSimulatorConfig.OBJECT_AXIS);
		o3d_axis = loadObj(parent_dir + GoalSimulatorConfig.OBJECT_AXIS
				+ GoalSimulatorConfig.OBJECT_WAVEFRONT_EXTENSION, parent_dir + GoalSimulatorConfig.OBJECT_AXIS
				+ GoalSimulatorConfig.OBJECT_MATERIAL_EXTENSION, 1.0f, true);
		if (GoalSimulatorConfig.TRIANGLE_STRIPS_ITERATIONS > 0) {
			log("creating triangle strips for " + GoalSimulatorConfig.OBJECT_AXIS);
			o3d_axis.createTriangleStrips(GoalSimulatorConfig.TRIANGLE_STRIPS_ITERATIONS);
		}
		// oct = new OcTree(o3d_axis, 64, OcTree.MODE_OPTIMIZED);
		// o3d_axis.setOcTree(oct);
		world.addObject(o3d_axis);

		// map
		log("loading " + GoalSimulatorConfig.OBJECT_MAP);
		o3d_map = loadObj(parent_dir + GoalSimulatorConfig.OBJECT_MAP
				+ GoalSimulatorConfig.OBJECT_WAVEFRONT_EXTENSION, parent_dir + GoalSimulatorConfig.OBJECT_MAP
				+ GoalSimulatorConfig.OBJECT_MATERIAL_EXTENSION, 1.0f, true);
		if (GoalSimulatorConfig.TRIANGLE_STRIPS_ITERATIONS > 0) {
			log("creating triangle strips for " + GoalSimulatorConfig.OBJECT_MAP);
			o3d_map.createTriangleStrips(GoalSimulatorConfig.TRIANGLE_STRIPS_ITERATIONS);
		}
		oct = new OcTree(o3d_map, 1024, OcTree.MODE_OPTIMIZED);
		o3d_map.setOcTree(oct);
		world.addObject(o3d_map);

		// transparent section of the map
		log("loading " + GoalSimulatorConfig.OBJECT_MAP_TRANSPARENT);
		o3d_map_transparent = loadObj(parent_dir + GoalSimulatorConfig.OBJECT_MAP_TRANSPARENT
				+ GoalSimulatorConfig.OBJECT_WAVEFRONT_EXTENSION, parent_dir
				+ GoalSimulatorConfig.OBJECT_MAP_TRANSPARENT + GoalSimulatorConfig.OBJECT_MATERIAL_EXTENSION, 1.0f,
				true);
		if (GoalSimulatorConfig.TRIANGLE_STRIPS_ITERATIONS > 0) {
			log("creating triangle strips for " + GoalSimulatorConfig.OBJECT_MAP_TRANSPARENT);
			o3d_map_transparent.createTriangleStrips(GoalSimulatorConfig.TRIANGLE_STRIPS_ITERATIONS);
		}
		// this section is transparent
		o3d_map_transparent.setTransparency(2);
		// o3d_map.setSortOffset(-16.0f);
		// oct = new OcTree(o3d_map_transparent, 16, OcTree.MODE_OPTIMIZED);
		// o3d_map_transparent.setOcTree(oct);
		world.addObject(o3d_map_transparent);

		// red spawn
		log("loading " + GoalSimulatorConfig.OBJECT_RED_SPAWN);
		o3d_red_spawn = loadObj(parent_dir + GoalSimulatorConfig.OBJECT_RED_SPAWN
				+ GoalSimulatorConfig.OBJECT_WAVEFRONT_EXTENSION, parent_dir + GoalSimulatorConfig.OBJECT_RED_SPAWN
				+ GoalSimulatorConfig.OBJECT_MATERIAL_EXTENSION, 1.0f, true);
		if (GoalSimulatorConfig.TRIANGLE_STRIPS_ITERATIONS > 0) {
			log("creating triangle strips for " + GoalSimulatorConfig.OBJECT_RED_SPAWN);
			o3d_red_spawn.createTriangleStrips(GoalSimulatorConfig.TRIANGLE_STRIPS_ITERATIONS);
		}
		// oct = new OcTree(o3d_red_spawn, 16, OcTree.MODE_OPTIMIZED);
		// o3d_red_spawn.setOcTree(oct);
		world.addObject(o3d_red_spawn);

		// blue spawn
		log("loading " + GoalSimulatorConfig.OBJECT_BLUE_SPAWN);
		o3d_blue_spawn = loadObj(parent_dir + GoalSimulatorConfig.OBJECT_BLUE_SPAWN
				+ GoalSimulatorConfig.OBJECT_WAVEFRONT_EXTENSION, parent_dir
				+ GoalSimulatorConfig.OBJECT_BLUE_SPAWN + GoalSimulatorConfig.OBJECT_MATERIAL_EXTENSION, 1.0f, true);
		if (GoalSimulatorConfig.TRIANGLE_STRIPS_ITERATIONS > 0) {
			log("creating triangle strips for " + GoalSimulatorConfig.OBJECT_BLUE_SPAWN);
			o3d_blue_spawn.createTriangleStrips(GoalSimulatorConfig.TRIANGLE_STRIPS_ITERATIONS);
		}
		// oct = new OcTree(o3d_blue_spawn, 16, OcTree.MODE_OPTIMIZED);
		// o3d_blue_spawn.setOcTree(oct);
		world.addObject(o3d_blue_spawn);

		// red bot
		log("loading " + GoalSimulatorConfig.OBJECT_RED_ROBOT);
		o3d_red_robot = loadObj(parent_dir + GoalSimulatorConfig.OBJECT_RED_ROBOT
				+ GoalSimulatorConfig.OBJECT_WAVEFRONT_EXTENSION, parent_dir + GoalSimulatorConfig.OBJECT_RED_ROBOT
				+ GoalSimulatorConfig.OBJECT_MATERIAL_EXTENSION, 1.0f, true);
		if (GoalSimulatorConfig.TRIANGLE_STRIPS_ITERATIONS > 0) {
			log("creating triangle strips for " + GoalSimulatorConfig.OBJECT_RED_ROBOT);
			o3d_red_robot.createTriangleStrips(GoalSimulatorConfig.TRIANGLE_STRIPS_ITERATIONS);
		}
		// oct = new OcTree(o3d_red_robot, 16, OcTree.MODE_OPTIMIZED);
		// o3d_red_robot.setOcTree(oct);

		// blue bot
		log("loading " + GoalSimulatorConfig.OBJECT_BLUE_ROBOT);
		o3d_blue_robot = loadObj(parent_dir + GoalSimulatorConfig.OBJECT_BLUE_ROBOT
				+ GoalSimulatorConfig.OBJECT_WAVEFRONT_EXTENSION, parent_dir
				+ GoalSimulatorConfig.OBJECT_BLUE_ROBOT + GoalSimulatorConfig.OBJECT_MATERIAL_EXTENSION, 1.0f, true);
		if (GoalSimulatorConfig.TRIANGLE_STRIPS_ITERATIONS > 0) {
			log("creating triangle strips for " + GoalSimulatorConfig.OBJECT_BLUE_ROBOT);
			o3d_blue_robot.createTriangleStrips(GoalSimulatorConfig.TRIANGLE_STRIPS_ITERATIONS);
		}
		// oct = new OcTree(o3d_blue_robot, 16, OcTree.MODE_OPTIMIZED);
		// o3d_blue_robot.setOcTree(oct);

		// blue gift
		log("loading " + GoalSimulatorConfig.OBJECT_BLUE_GIFT);
		o3d_blue_gift = loadObj(parent_dir + GoalSimulatorConfig.OBJECT_BLUE_GIFT
				+ GoalSimulatorConfig.OBJECT_WAVEFRONT_EXTENSION, parent_dir + GoalSimulatorConfig.OBJECT_BLUE_GIFT
				+ GoalSimulatorConfig.OBJECT_MATERIAL_EXTENSION, 1.0f, true);
		if (GoalSimulatorConfig.TRIANGLE_STRIPS_ITERATIONS > 0) {
			log("creating triangle strips for " + GoalSimulatorConfig.OBJECT_BLUE_GIFT);
			o3d_blue_gift.createTriangleStrips(GoalSimulatorConfig.TRIANGLE_STRIPS_ITERATIONS);
		}
		// oct = new OcTree(o3d_blue_gift, 16, OcTree.MODE_OPTIMIZED);
		// o3d_blue_gift.setOcTree(oct);

		// red gift
		log("loading " + GoalSimulatorConfig.OBJECT_RED_GIFT);
		o3d_red_gift = loadObj(parent_dir + GoalSimulatorConfig.OBJECT_RED_GIFT
				+ GoalSimulatorConfig.OBJECT_WAVEFRONT_EXTENSION, parent_dir + GoalSimulatorConfig.OBJECT_RED_GIFT
				+ GoalSimulatorConfig.OBJECT_MATERIAL_EXTENSION, 1.0f, true);
		if (GoalSimulatorConfig.TRIANGLE_STRIPS_ITERATIONS > 0) {
			log("creating triangle strips for " + GoalSimulatorConfig.OBJECT_RED_GIFT);
			o3d_red_gift.createTriangleStrips(GoalSimulatorConfig.TRIANGLE_STRIPS_ITERATIONS);
		}
		// oct = new OcTree(o3d_red_gift, 16, OcTree.MODE_OPTIMIZED);
		// o3d_red_gift.setOcTree(oct);

		world.buildAllObjects();
	}

	private Object3D loadObj(String obj_filename, String mtl_filename, float scale, boolean correct) {
		log("loadObj() " + obj_filename + ":" + mtl_filename);
		Object3D[] parts = Loader.loadOBJ(obj_filename, mtl_filename, scale);
		Object3D obj = new Object3D(0);
		for (int i = 0; i < parts.length; i++) {
			Object3D part = parts[i];
			if (correct) {
				part.setCenter(SimpleVector.ORIGIN);
			}
			obj = Object3D.mergeObjects(obj, part);
		}
		obj.build();
		log("loadObj() done");
		return obj;
	}

	private void loadTextures() {
		texMan = TextureManager.getInstance();
		String texture_dir_absolute = GoalSimulatorConfig.CURRENT_PATH + GoalSimulatorConfig.DIRECTORY_SEPARATOR
				+ GoalSimulatorConfig.DIRECTORY_TEXTURES;
		File dir = new File(texture_dir_absolute);
		String[] files = dir.list();
		for (int i = 0; i < files.length; i++) {
			String name = files[i];
			String filter = name.toLowerCase();
			if (filter.endsWith(".jpg") || filter.endsWith(".png")) {
				String texture_filename = texture_dir_absolute + GoalSimulatorConfig.DIRECTORY_SEPARATOR + name;
				System.out.println("Loading texture " + texture_filename);
				// boolean alpha = filter.contains("alpha");
				Texture texture = new Texture(texture_filename, true);
				texMan.addTexture(name, texture);
			}
		}
	}

	private void log(String string) {
		System.out.println(string);
	}

	private void loop() {
		Display.setTitle(GoalSimulatorConfig.WINDOW_TITLE_RUNNING);
		while (!Display.isCloseRequested() && executing) {
			renderScene();
			checkKeyboard();
			//give a cycle to the AI engine
			Thread.yield();
		}
		goal_simulator.setRunning(false);
		buffer.disableRenderer(IRenderer.RENDERER_OPENGL);
		buffer.dispose();
		System.exit(0);
	}

	private void renderScene() {
		setupSkyboxes();
		setupGifts();
		setupSoldierAgents();
		setupSpawnAgents();
		buffer.clear(GoalSimulatorConfig.COLOR_BACKGROUND);
		world.renderScene(buffer);
		world.draw(buffer);
		buffer.update();
		buffer.displayGLOnly();
	}

	private void setupGifts() {
		if (red_gifts != null && red_gifts.size() > 0)
			for (Entity gift : red_gifts) {
				Object3D model = o3d_red_gifts.get(gift);
				if (model != null) {
					// nullify translation
					model.getTranslationMatrix().setIdentity();
					// orient
					model.getRotationMatrix().setIdentity();
					model.rotateX(gift.getPitch());
					model.rotateY(gift.getYaw());
					model.rotateZ(gift.getRoll());
					// position the red soldier agent
					position = gift.getPosition();
					if (position == null) {
						model.translate(GoalSimulatorConfig.POSITION_VERY_FAR_AWAY);
					} else {
						model.translate((float) position.x, (float) position.y, (float) position.z);
					}
				}
			}
		if (blue_gifts != null && blue_gifts.size() > 0)
			for (Entity gift : blue_gifts) {
				Object3D model = o3d_blue_gifts.get(gift);
				if (model != null) {
					// nullify translation
					model.getTranslationMatrix().setIdentity();
					// orient
					model.getRotationMatrix().setIdentity();
					model.rotateX(gift.getPitch());
					model.rotateY(gift.getYaw());
					model.rotateZ(gift.getRoll());
					// position the red soldier agent
					position = gift.getPosition();
					if (position == null) {
						model.translate(GoalSimulatorConfig.POSITION_VERY_FAR_AWAY);
					} else {
						model.translate((float) position.x, (float) position.y, (float) position.z);
					}
				}

			}
	}

	private void setupSkyboxes() {
		// slide the skybox
		o3d_skybox.translate(GoalSimulatorConfig.SKYBOX_SLIDE_DELTA, 0.0f, GoalSimulatorConfig.SKYBOX_SLIDE_DELTA);
		skybox_slide += GoalSimulatorConfig.SKYBOX_SLIDE_DELTA;
		// reposition the skybox when it's translation gets over one texture tile
		// Example: skybox has size 512, it's faces have 1 / 0.2(2) = 4.5 tiles per unit, so it 're-slides' (translates back to initial position) when
		// total sliding reaches 4.5 * 512 = 113.7(7) units
		if (skybox_slide > GoalSimulatorConfig.SKYBOX_TEXTURE_REPETITION_PERIOD * GoalSimulatorConfig.SKYBOX_SCALE) {
			skybox_slide = GoalSimulatorConfig.SKYBOX_SLIDE_INITIAL;
			o3d_skybox.getTranslationMatrix().setIdentity();
		}
		// slide the lower skybox
		o3d_skybox_layer1.translate(GoalSimulatorConfig.SKYBOX_LAYER_1_SLIDE_DELTA_X, 0.0f,
				GoalSimulatorConfig.SKYBOX_LAYER_1_SLIDE_DELTA_Z);
		skybox_layer1_slide += GoalSimulatorConfig.SKYBOX_LAYER_1_SLIDE_DELTA;
		// reposition the skybox when it's translation gets over one texture tile
		if (skybox_layer1_slide > GoalSimulatorConfig.SKYBOX_LAYER_1_TEXTURE_REPETITION_PERIOD
				* GoalSimulatorConfig.SKYBOX_LAYER_1_SCALE) {
			skybox_layer1_slide = GoalSimulatorConfig.SKYBOX_LAYER_1_SLIDE_INITIAL;
			o3d_skybox_layer1.getTranslationMatrix().setIdentity();
		}
	}

	private void setupSoldierAgents() {
		//		synchronized (red_soldier_agents) {
		for (SoldierAgent agent : red_soldier_agents) {
			//				synchronized (agent) {
			Object3D model = o3d_red_robots.get(agent);
			// orient
			model.getRotationMatrix().setIdentity();
			float pitch = agent.getPitch();
			float yaw = agent.getYaw();
			float roll = agent.getRoll();
			model.rotateX(pitch);
			model.rotateY(yaw);
			model.rotateZ(roll);
			// agent.setRoll((float) (agent.getRoll() + 0.1));
			// position the red soldier agent
			position = agent.getPosition();
			// nullify past translations
			model.getTranslationMatrix().setIdentity();
			if (position == null) {
				model.setVisibility(false);
				// model.translate(GoalSimulatorConfig.POSITION_VERY_FAR_AWAY);
			} else {
				model.setVisibility(true);
				model.translate((float) position.x, (float) position.y, (float) position.z);
				// System.out.println( position.y);
			}
		}
		//synchronized (blue_soldier_agents) {
		for (SoldierAgent agent : blue_soldier_agents) {
			//	synchronized (agent) {
			Object3D model = o3d_blue_robots.get(agent);
			// orient
			model.getRotationMatrix().setIdentity();
			model.rotateX(agent.getPitch());
			model.rotateY(agent.getYaw());
			model.rotateZ(agent.getRoll());
			// position the blue soldier agent
			position = agent.getPosition();
			// nullify past translations
			model.getTranslationMatrix().setIdentity();
			if (position == null) {
				model.setVisibility(false);
				// model.translate(GoalSimulatorConfig.POSITION_VERY_FAR_AWAY);
			} else {
				model.setVisibility(true);
				model.translate((float) position.x, (float) position.y, (float) position.z);
				// System.out.println( position.y);
			}
		}
	}

	private void setupSpawnAgents() {
		// revert translation matrices
		o3d_red_spawn.getTranslationMatrix().setIdentity();
		o3d_blue_spawn.getTranslationMatrix().setIdentity();
		// scale to show progress
		float scale = red_spawn.getSpawnProgress();
		if (scale > 0)
			o3d_red_spawn.setScale(scale);
		scale = blue_spawn.getSpawnProgress();
		if (scale > 0)
			o3d_blue_spawn.setScale(scale);
		// translate to agent's center
		position = red_spawn.getPosition();
		o3d_red_spawn.translate((float) position.x, (float) position.y, (float) position.z);
		position = blue_spawn.getPosition();
		o3d_blue_spawn.translate((float) position.x, (float) position.y, (float) position.z);
		// System.out.println( + "," + );
	}
}
