/*
   client/hud.cc
   This file is part of the Osirion project and is distributed under
   the terms and conditions of the GNU General Public License version 2
*/


#include "core/core.h"
#include "client/hud.h"
#include "client/input.h"
#include "client/targets.h"
#include "render/render.h"
#include "render/renderext.h"
#include "ui/ui.h"

namespace client
{

HUD::HUD(ui::Widget *parent) : Widget(parent)
{
	set_label("hud");
	set_border(false);
	set_background(false);

	/*hud_toolbar = new ui::Toolbar(this);

	hud_toolbar->add_button("", "Menu", "ui_menu");
	hud_toolbar->add_button("", "Chat", "ui_chat");
	hud_toolbar->add_button("", "Map", "ui_map");
	*/
	hud_center = new ui::Bitmap(this, "bitmaps/pointers/center");
	hud_center->set_color(palette()->pointer());

}

void HUD::resize()
{
	//hud_toolbar->set_geometry(0.0f, 0.0f, width(), font()->height() *2 );

	hud_center->set_size(ui::pointer_size, ui::pointer_size);
	hud_center->set_location((size() - hud_center->size()) * 0.5f);
}

void HUD::draw_offscreen_target(core::Entity *entity, bool is_active_target)
{

	math::Vector3f target(entity->location() - render::Camera::eye());
	target = render::Camera::axis().transpose() * target;

	float cx = 0;
	float cy = 0;

	if (target.y() * target.y() + target.z() * target.z() < 0.0001) {
		// X - bound, behind (front is visible)
		cx = 0.0f;
		cy = -0.5f;

	} else if (fabs(target.y()) > fabs(target.z())) {
		// Y-bound
		cx = math::sgnf(target.y()) * 0.5f;
		cy = 0.5f * target.z() / fabs(target.y());
	} else {
		// Z-bound
		cx = 0.5f * target.y() / fabs(target.z());
		cy = math::sgnf(target.z()) * 0.5f;
	}

	const float r = 16;
	const float margin =  24;
	cx = (0.5f - cx) * ((float) render::State::width() - margin * 2);
	cx += margin;
	cy = (0.5f - cy) * ((float) render::State::height() - margin * 2);
	cy += margin;

	gl::disable(GL_TEXTURE_2D);
	gl::color(0, 0, 0, 1);
	gl::begin(gl::LineLoop);
	glVertex3f(cx + r, cy + 2, 0);
	glVertex3f(cx, cy + r + 2, 0);
	glVertex3f(cx - r, cy + 2, 0);
	glVertex3f(cx, cy - r + 2, 0);
	gl::end();

	if (entity == core::localplayer()->mission_target()) {
		gl::color(palette()->mission());
	} else if (entity->type() == core::Entity::Controlable) {
		gl::color(0, 1, 0, 1); // FIXME allegiance color
	} else {
		gl::color(1, 1, 1, 1); // FIXME neutral color
	}

	gl::begin(gl::LineLoop);
	glVertex3f(cx + r, cy, 0);
	glVertex3f(cx, cy + r, 0);
	glVertex3f(cx - r, cy, 0);
	glVertex3f(cx, cy - r, 0);
	gl::end();
	gl::enable(GL_TEXTURE_2D);
}

void HUD::draw_target(core::Entity *entity, bool is_active_target)
{
	using math::Vector3f;

	// don't draw target if we're very close to it
	if (render::ext_render(entity)->distance() < 0.001f)
		return;

	// don't draw target if it is outside the visible cone
	Vector3f target(entity->location() - render::Camera::eye());
	if (math::dotproduct(render::Camera::axis().forward(), Vector3f::normalized(target)) < 0.75) {
		draw_offscreen_target(entity, is_active_target);
		return;
	}

	// transform the target into the camera coordinate system
	target = render::Camera::axis().transpose() * target;

	// calculate the intersection between the line (0,0,0)-target and the frustum front
	float t = (render::FRUSTUMFRONT + 0.001f) / target.x();
	Vector3f center(target *t);

	float cx = render::State::width() * (0.5 - center.y());
	float cy = render::State::height() * (0.5 - center.z()  * render::State::aspect());

	if ((cx < 0) || (cy < 0) || (cx > render::State::width()) || (cy > render::State::height())) {
		draw_offscreen_target(entity, is_active_target);
		return;
	}

	float r = ui::pointer_size;
	if (!is_active_target)
		r *= 0.5;

	gl::disable(GL_TEXTURE_2D);
	// outer square shadow
	gl::color(0, 0, 0, 1);
	gl::begin(gl::LineLoop);
	gl::vertex(cx + r, cy + 2);
	gl::vertex(cx, cy + r + 2);
	gl::vertex(cx - r, cy + 2);
	gl::vertex(cx, cy - r + 2);
	gl::end();

	if ((entity->flags() & core::Entity::Dockable) == core::Entity::Dockable) {
		gl::begin(gl::LineLoop);
		gl::vertex(cx + (r*0.25f), cy + 2);
		gl::vertex(cx, cy + (r*0.25f) + 2);
		gl::vertex(cx - (r*0.25f), cy + 2);
		gl::vertex(cx, cy - (r*0.25f) + 2);
		gl::end();
	}

	if (entity == core::localplayer()->mission_target()) {
		gl::color(palette()->mission());
	} else if (entity->type() == core::Entity::Controlable) {
		gl::color(0, 1, 0, 1); // FIXME allegiance color
	} else {
		gl::color(1, 1, 1, 1); // FIXME neutral color
	}

	// outer square0
	gl::begin(gl::LineLoop);
	gl::vertex(cx + r, cy);
	gl::vertex(cx, cy + r);
	gl::vertex(cx - r, cy);
	gl::vertex(cx, cy - r);
	gl::end();

	if ((entity->flags() & core::Entity::Dockable) == core::Entity::Dockable) {
		gl::begin(gl::LineLoop);
		gl::vertex(cx + (r*0.25f), cy);
		gl::vertex(cx, cy + (r*0.25f));
		gl::vertex(cx - (r*0.25f), cy);
		gl::vertex(cx, cy - (r*0.25f));
		gl::end();
	}

	gl::enable(GL_TEXTURE_2D);
	if (is_active_target) {

		// entity name	and distance
		std::ostringstream strdistance;
		float d = math::distance(core::localcontrol()->location(), entity->location()) - entity->radius() - core::localcontrol()->radius();
		if (d > 0) {
			if (d > 100.0f) {
				strdistance << roundf(d * 0.1f) << "km";
			} else {
				strdistance << roundf(d * 100.0f) << "m";
			}
		} else {
			strdistance << "--";
		}

		// owner name
		if (entity->type() == core::Entity::Controlable) {
			const core::EntityControlable *ec = static_cast<core::EntityControlable *>(entity);
			if (ec->owner()) {
				render::Text::setcolor('B');
				render::Text::draw(cx - aux::text_length(ec->owner()->name()) * render::Text::fontwidth()*0.5f,
						   cy - r - 4 - 2*render::Text::fontheight(), ec->owner()->name());
			}
			render::Text::setcolor('B');

		} else if (entity == core::localplayer()->mission_target()) {
			gl::color(palette()->mission());

		} else {
			render::Text::setcolor('N');
		}

		render::Text::draw(cx - aux::text_length(entity->name()) * render::Text::fontwidth()*0.5f,
				   cy - r - 4 - render::Text::fontheight(), entity->name());

		render::Text::draw(cx - aux::text_length(strdistance.str()) * render::Text::fontwidth() * 0.5f,
				   cy + r + 4, strdistance.str());
	}
}

bool HUD::on_keypress(const int key, const unsigned int modifier)
{
	switch (key) {
		case SDLK_ESCAPE:
			if (targets::current()) {
				targets::reset();
			} else {
				ui::root()->show_menu("game");
			}
			return true;
	}

	return false;
}

void HUD::draw()
{
	using namespace render;

	if (core::localcontrol() && (input::mouse_control || input::joystick_control) &&
			(render::Camera::mode() == render::Camera::Cockpit || render::Camera::mode() == render::Camera::Track)) {
		hud_center->set_visible(true);
	} else {
		hud_center->set_visible(false);
	}

	gl::enable(GL_TEXTURE_2D);
	Text::setfont("gui", 12, 18);
	Text::setcolor('N'); //set normal color

	core::Zone *zone = core::localcontrol()->zone();

	// draw HUD targets
	for (core::Zone::Content::iterator it = zone->content().begin(); it != zone->content().end(); it++) {
		core::Entity *entity = (*it);

		if (entity == targets::current()) {
			draw_target(entity, true);

		} else 	if (entity == core::localplayer()->mission_target()) {
			draw_target(entity, false);

		} else if ((entity->type() == core::Entity::Controlable) && (targets::is_valid_hud_target(entity))) {
			draw_target(entity, false);
		}
	}

	/*
	unsigned int state = core::localcontrol()->state();
	if (state) {
		std::ostringstream statestr;
		statestr.clear();
		if (state == core::Entity::ImpulseInitiate) {
			statestr << "^FInitializing kinetic impulse drive " << core::localcontrol()->timer();
		} else if (state == core::Entity::Impulse) {
			//statestr << "^FKinetic impulse";
		} else if (state == core::Entity::JumpInitiate) {
			statestr << "^FInitializing hyperspace jump drive " << core::localcontrol()->timer();
		} else if (state == core::Entity::Jump) {
			statestr << "^FJumping...";
		}

		Text::draw(4, render::State::height() - Text::fontheight()*3 - 4, statestr.str());
	}
	*/

	const core::Entity *target = targets::current();
	if (target) {
		std::ostringstream strtarget;
		strtarget << "^B" << target->name() << "\n^B";

		float d = math::distance(core::localcontrol()->location(), target->location())
			  - target->radius() - core::localcontrol()->radius();

		if (d > 0) {
			strtarget << "^Ndist:^B ";
			if (d > 100.0f) {
				strtarget << roundf(d * 0.1f) << "km";
			} else {
				strtarget << roundf(d * 100.0f) << "m";
			}

			if (core::localcontrol()->speed() > 0.1f) {
				strtarget << "^N eta:^B ";
				float eta = floorf(d / core::localcontrol()->speed());
				if (eta > 60.0f) {
					float etamin = floorf(eta / 60.0f);
					strtarget << etamin << "min ";
					eta -= etamin * 60;
				}
				strtarget << eta << "sec";
			}
		} else {
			strtarget << "      --";
		}
		Text::draw(width() - 4 - Text::fontwidth()*30, height() - Text::fontheight()*2 - 4, strtarget.str());
	}

	// draw player info
	std::ostringstream playerinfostr;
	playerinfostr << "^B" << core::localcontrol()->name() << '\n' << "^Ncredits:  " << core::localplayer()->credits();
	Text::draw(width() - 4 - Text::fontwidth()*52, height() - Text::fontheight()*2 - 4, playerinfostr.str());

	// draw thruster indicator
	Textures::bind("bitmaps/hud/thruster_base"); // 316 x 32 bitmap
	gl::color(1, 1, 1, 1);
	gl::begin(gl::Quads);

	glTexCoord2f(0, 0);
	gl::vertex(4, height() - 4 - 32, 0);

	glTexCoord2f(1, 0);
	gl::vertex(4 + 316, height() - 4 - 32, 0);

	glTexCoord2f(1, 1);
	gl::vertex(4 + 316, height() - 4 , 0);

	glTexCoord2f(0, 1);
	gl::vertex(4, height() - 4 , 0);

	gl::end();

	float u = core::localcontrol()->thrust();
	
	if (core::localcontrol()->state() != core::Entity::Normal) {
	
		if ((core::localcontrol()->state() == core::Entity::Docked ) || (core::localcontrol()->state() == core::Entity::NoPower )) {
			u = 0.0f;
		} else {
	
			u = 1.0f;
		}
	}

	if ((u > 0) || (core::localcontrol()->state() == core::Entity::Impulse)) {

		if ((core::localcontrol()->state() == core::Entity::Impulse) || (core::localcontrol()->state() == core::Entity::ImpulseInitiate)) {
			gl::color(0, .8, 0);
		} else if ((core::localcontrol()->state() == core::Entity::Jump) || (core::localcontrol()->state() == core::Entity::JumpInitiate)) {
			gl::color(0.8f, 0.0f, 0.0f);
		} else {
			float d  = math::absf(input::local_thrust - u);
			if (d > 0.1)  {
				d = 0.1f;
			}
			gl::color(1, 1, .5f + d * 5.0f);
		}
		Textures::bind("bitmaps/hud/thruster_indicator"); // 316 x 32 bitmap
		gl::begin(gl::Quads);
		glTexCoord2f(0, 0);
		gl::vertex(4, height() - 4 - 32, 0);

		glTexCoord2f(u, 0);
		gl::vertex(4.0f + u * 316.0f, height() - 4 - 32, 0);

		glTexCoord2f(u, 1);
		gl::vertex(4.0f + u * 316.0f, height() - 4 , 0);

		glTexCoord2f(0, 1);
		gl::vertex(4, height() - 4 , 0);

		gl::end();
	}

	Text::setfont("gui", 14, 24);
	Text::setcolor('B'); //set normal color

	std::ostringstream speedstr;
	speedstr << "^B" << roundf(core::localcontrol()->speed() * 100.0f);
	Text::draw(316 + 4 + 10, height() - 6 - 16 - render::Text::fontwidth() / 2, speedstr.str());

	Text::setfont("gui", 12, 18);
	Text::setcolor('N'); //set normal color


	gl::disable(GL_TEXTURE_2D);
}

}