package com.oddlabs.tt.render;

import com.oddlabs.tt.gui.GUIRoot;
import com.oddlabs.tt.gui.Icons;
import com.oddlabs.tt.camera.CameraState;
import com.oddlabs.tt.global.Globals;
import com.oddlabs.tt.landscape.AbstractTreeGroup;
import com.oddlabs.tt.landscape.World;
import com.oddlabs.tt.resource.WorldInfo;
import com.oddlabs.tt.resource.FogInfo;
import com.oddlabs.tt.scenery.Water;
import com.oddlabs.tt.scenery.Sky;
import com.oddlabs.tt.model.AbstractElementNode;
import com.oddlabs.tt.model.Model;
import com.oddlabs.tt.model.Building;
import com.oddlabs.tt.model.Race;
import com.oddlabs.tt.player.Player;
import com.oddlabs.tt.particle.Lightning;
import com.oddlabs.tt.particle.Emitter;
import com.oddlabs.tt.resource.WorldGenerator;
import com.oddlabs.tt.procedural.Landscape;
import com.oddlabs.tt.gui.LocalInput;
import com.oddlabs.tt.util.Target;
import com.oddlabs.tt.util.ToolTip;
import com.oddlabs.tt.viewer.Selection;
import com.oddlabs.tt.viewer.Cheat;
import com.oddlabs.tt.viewer.AmbientAudio;
import com.oddlabs.tt.util.DebugRender;
import com.oddlabs.tt.util.BoundingBox;

import java.util.List;
import java.nio.FloatBuffer;

import org.lwjgl.opengl.GL11;
import org.lwjgl.BufferUtils;

public final strictfp class DefaultRenderer implements UIRenderer {
	private final Picker picker;
	private final FogInfo fog_info;
	private final Water water;
	private final Sky sky;
	private final LandscapeRenderer landscape_renderer;
	private final World world;
	private final ElementRenderer element_renderer;
	private final TreeRenderer tree_renderer;
	private final SpriteSorter sprite_sorter;
	private final RenderQueues render_queues;
	private final FloatBuffer light_array;
	private final Cheat cheat;

	private Building selected_building;

	private void drawAxes() {
		if (Globals.draw_axes) {
			float center = world.getHeightMap().getMetersPerWorld()/2;
			float z = world.getHeightMap().getNearestHeight(center, center);
			GL11.glTranslatef(center, center, z);
			GL11.glDisable(GL11.GL_DEPTH_TEST);
			GL11.glDisable(GL11.GL_TEXTURE_2D);
			GL11.glBegin(GL11.GL_LINES);
			// X axis
			GL11.glColor3f(1, 0, 0);
			GL11.glVertex3f(0, 0, 0);
			GL11.glVertex3f(10, 0, 0);
			// Y axis
			GL11.glColor3f(0, 1, 0);
			GL11.glVertex3f(0, 0, 0);
			GL11.glVertex3f(0, 10, 0);
			// Z axis
			GL11.glColor3f(0, 0, 1);
			GL11.glVertex3f(0, 0, 0);
			GL11.glVertex3f(0, 0, 10);
			GL11.glEnd();

			GL11.glEnable(GL11.GL_DEPTH_TEST);
			GL11.glEnable(GL11.GL_TEXTURE_2D);
		}
	}

	public DefaultRenderer(Cheat cheat, Player local_player, RenderQueues render_queues, int terrain_type, WorldInfo world_info, LandscapeRenderer landscape_renderer, Picker picker, Selection selection, WorldGenerator generator) {
		this.world = local_player.getWorld();
		this.cheat = cheat;
		this.light_array = BufferUtils.createByteBuffer(4*4).asFloatBuffer();
		light_array.put(new float[]{-1.0f, 0.0f, 1.0f, 0.0f});
		light_array.rewind();
		this.render_queues = render_queues;
		this.sprite_sorter = new SpriteSorter();
		this.picker = picker;
		this.element_renderer = new ElementRenderer(local_player, landscape_renderer, render_queues, picker, false, sprite_sorter, selection);
		this.tree_renderer = new TreeRenderer(world, cheat, terrain_type, world_info.trees, world_info.palm_trees, sprite_sorter, picker.getRespondManager());
		this.landscape_renderer = landscape_renderer;
		this.fog_info = Landscape.getFogInfo(generator.getTerrainType(), generator.getMetersPerWorld());
		this.water = new Water(world.getHeightMap(), generator.getTerrainType());
		this.sky = new Sky(landscape_renderer, generator.getTerrainType());
	}

	public final void renderGUI(GUIRoot gui_root) {
		if (cheat.isEnabled())
			Icons.getIcons().getCheatIcon().render(gui_root.getWidth() - Icons.getIcons().getCheatIcon().getWidth() - 10, 5);
	}

	public final void setSelectedBuilding(Building building) {
		this.selected_building = building;
	}

	private void renderRallyPoint(CameraState camera_state) {
		if (selected_building != null && !selected_building.isDead() && selected_building.hasRallyPoint())
			doRenderRallyPoint(camera_state);
	}

	private final void doRenderRallyPoint(CameraState camera_state) {
		float rally_point_dir_x = 1f;
		float rally_point_dir_y = 0f;
		Target rally_point = selected_building.getRallyPoint();
		float dx = camera_state.getCurrentX() - rally_point.getPositionX();
		float dy = camera_state.getCurrentY() - rally_point.getPositionY();
		float len = (float)StrictMath.sqrt(dx*dx + dy*dy);
		if (len > 0.1f) {
			float inv_len = 1f/len;
			rally_point_dir_x = dx*inv_len;
			rally_point_dir_y = dy*inv_len;
		}
		Race race = selected_building.getOwner().getRace();
		SpriteRenderer rally_point_renderer = render_queues.getRenderer(race.getRallyPoint());
		rally_point_renderer.setup(0, false);
		float x = rally_point.getPositionX();
		float y = rally_point.getPositionY();
		float z = world.getHeightMap().getNearestHeight(rally_point.getPositionX(), rally_point.getPositionY());
		if (rally_point instanceof Building) {
			Building rally_building = (Building)rally_point;
			x += rally_building.getBuildingTemplate().getRallyX();
			y += rally_building.getBuildingTemplate().getRallyY();
			z += rally_building.getBuildingTemplate().getRallyZ();
		}
		RenderTools.translateAndRotate(x, y, z, rally_point_dir_x, rally_point_dir_y);
		Sprite.setupDecalColor(SelectableVisitor.getTeamColor(selected_building));
		rally_point_renderer.getSpriteList().render(0, 0, 0f);
		rally_point_renderer.getSpriteList().reset(0, false, false);
	}

	public final void pickHover(boolean can_hover_behind, CameraState camera, int x, int y) {
		if (can_hover_behind) {
			picker.pickHover(camera, LocalInput.getMouseX(), LocalInput.getMouseY());
		} else {
			picker.resetCurrentHovered();
		}
	}

	public final ToolTip getToolTip() {
		return picker.getCurrentToolTip();
	}

	public final TreeRenderer getTreeRenderer() {
		return tree_renderer;
	}

	public final boolean clearColorBuffer() {
		return Globals.clear_frame_buffer || cheat.line_mode;
	}

	public void render(AmbientAudio ambient, CameraState frustum_state, GUIRoot gui_root) {
		ambient.updateSoundListener(frustum_state, world.getHeightMap());
		if (Globals.line_mode || cheat.line_mode) {
			GL11.glPolygonMode(GL11.GL_FRONT, GL11.GL_LINE);
			GL11.glPolygonMode(GL11.GL_BACK, GL11.GL_LINE);
		}

		GL11.glLight(GL11.GL_LIGHT0, GL11.GL_POSITION, light_array);
		if (Globals.draw_sky) {
			sky.render();
		}

		fog_info.enableFog(frustum_state.getCurrentZ());
		if (Globals.draw_sky) {
			sky.renderSeaBottom();
		}

		// render landscape (must be before trees and misc, cause they use data calculated here)
		if (Globals.process_landscape) {
			landscape_renderer.prepareAll(frustum_state, false);
			landscape_renderer.renderAll();
		}
		// frustum check and placement
		if (Globals.process_trees) {
			tree_renderer.setup(frustum_state);
			world.getTreeRoot().visit(tree_renderer);
		}
		if (Globals.process_misc) {
			element_renderer.setup(frustum_state);
			world.getElementRoot().visit(element_renderer);
		}
		
		// Sort sprites
		sprite_sorter.distributeModels();
		
		// render shadows
		if (Globals.process_shadows) {
			render_queues.renderShadows(landscape_renderer);
		}

		gui_root.getDelegate().render3D(landscape_renderer, render_queues);
		
		// render
		if (Globals.process_trees) {
			tree_renderer.renderAll();
		}
		if (Globals.process_misc) {
			render_queues.renderAll();
		}

		drawAxes();

		if (Globals.draw_water)
			water.render(sky);

		if (Globals.process_misc) 
			render_queues.renderBlends();

		LightningRenderer.render(render_queues, element_renderer.getRenderState().getLightningQueue(), frustum_state);
		EmitterRenderer.render(render_queues, element_renderer.getRenderState().getEmitterQueue(), frustum_state);
		renderRallyPoint(frustum_state);

/*		float landscape_x = GUIRoot.getGUIRoot().getLandscapeLocationX();
		float landscape_y = GUIRoot.getGUIRoot().getLandscapeLocationY();
		if (Globals.isBoundsEnabled(Globals.BOUNDING_REGIONS)) {
			UnitGrid.getGrid().debugRenderRegions(landscape_x, landscape_y);
		}
*/
		if (Globals.isBoundsEnabled(Globals.BOUNDING_OCCUPATION)) {
			picker.debugRender();
		}

/*		if (Globals.isBoundsEnabled(Globals.BOUNDING_UNIT_GRID)) {
			java.util.Iterator it = com.oddlabs.tt.model.SelectionArmy.getSelection().getSet().iterator();
			while (it.hasNext()) {
				Object next = it.next();
				if (next instanceof com.oddlabs.tt.model.Unit) {
					com.oddlabs.tt.model.Unit s_unit = (com.oddlabs.tt.model.Unit)next;
					s_unit.debugRender();
				}
			}
//			UnitGrid.getGrid().debugRender(landscape_x, landscape_y);
		}*/
		fog_info.disableFog();
		if (Globals.line_mode || (cheat.line_mode)) {
			GL11.glPolygonMode(GL11.GL_FRONT, GL11.GL_FILL);
			GL11.glPolygonMode(GL11.GL_BACK, GL11.GL_FILL);
		}
	}
}