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

#include <iostream>
#include <fstream>
#include <sstream>
#include <iomanip>

#include "auxiliary/functions.h"
#include "core/application.h"
#include "core/gameinterface.h"
#include "filesystem/filesystem.h"
#include "model/model.h"
#include "model/material.h"
#include "model/material.h"
#include "render/gl.h"
#include "render/state.h"
#include "render/dust.h"
#include "render/particles.h"
#include "render/render.h"
#include "render/screenshot.h"
#include "render/textures.h"
#include "sys/sys.h"

namespace render
{

core::Cvar *r_axis = 0;
core::Cvar *r_bbox = 0;
core::Cvar *r_grid = 0;
core::Cvar *r_lights = 0;
core::Cvar *r_particles = 0;
core::Cvar *r_radius = 0;
core::Cvar *r_sky = 0;
core::Cvar *r_wireframe = 0;
core::Cvar *r_mipmap = 0;
core::Cvar *r_physics = 0;
core::Cvar *r_normals = 0;
core::Cvar *r_normalize = 0;

void func_list_textures(std::string const &args)
{
	Textures::list();
}

void func_list_particles(std::string const &args)
{
	ParticleScript::list();
}

void func_load_info_models(std::string const &args)
{
	load_info_models();
}

void init(int width, int height)
{
	con_print << "^BInitializing renderer..." << std::endl;

	con_print << "  renderer   ^B" << gl::renderer() << std::endl;
	con_print << "  vendor     ^B" << gl::vendor() << std::endl;
	con_print << "  version    ^B" << gl::version() << std::endl;
	
	// initialize render state
	State::init(width, height);

	// engine variables
	r_radius = core::Cvar::get("r_radius", "0", core::Cvar::Archive);
	r_radius->set_info("[bool] render entity radius");

	r_wireframe = core::Cvar::get("r_wireframe", "0", core::Cvar::Archive);
	r_wireframe->set_info("[bool] render wireframe");

	r_normals = core::Cvar::get("r_normals", "0", core::Cvar::Archive);
	r_normals->set_info("[bool] render face normals");

	r_normalize = core::Cvar::get("r_normalize", "0", core::Cvar::Archive);
	r_normalize->set_info("[bool] use GL_NORMALIZE instead of GL_RESCALE_NORMAL (recommended off)");

	r_grid = core::Cvar::get("r_grid", "0", core::Cvar::Archive);
	r_grid->set_info("[bool] render the space grid");

	r_axis = core::Cvar::get("r_axis", "0", core::Cvar::Archive);
	r_axis->set_info("[bool] render entity axis");

	r_bbox = core::Cvar::get("r_bbox", "0", core::Cvar::Archive);
	r_bbox->set_info("[bool] render model bounding box");

	r_sky = core::Cvar::get("r_sky", "1", core::Cvar::Archive);
	r_sky->set_info("[bool] render the sky");

	r_particles = core::Cvar::get("r_particles", "1", core::Cvar::Archive);
	r_particles->set_info("[bool] render particles");
	
	r_lights = core::Cvar::get("r_lights", "1", core::Cvar::Archive);
	r_lights->set_info("[bool] render lights");

	r_physics = core::Cvar::get("r_physics", "0", core::Cvar::Archive);
	r_physics->set_info("[bool] render physics (local game only)");

	Screenshot::screenshotformat = core::Cvar::get("screenshot_format", "jpg", core::Cvar::Archive);
	Screenshot::screenshotformat->set_info("[string] screenshot format: jpg png tga");

	Screenshot::screenshotquality = core::Cvar::get("screenshot_quality", "90", core::Cvar::Archive);
	Screenshot::screenshotquality->set_info("[int] screenshot jpeg quality");

	// hardware generate mipmaps
	if (State::has_generate_mipmaps()) {
		r_mipmap = core::Cvar::get("r_mipmap", "1", core::Cvar::Archive);
	} else {
		r_mipmap = core::Cvar::get("r_mipmap", "0", core::Cvar::Archive);
	}
	r_mipmap->set_info("[bool] use hardware generated mipmaps (recommended on)");

	Camera::init();

	Textures::init();

	Text::init();

	Dust::init();

	// engine functions
	core::Func *func = core::Func::add("list_textures", func_list_textures);
	func->set_info("list registered textures");

	func = core::Func::add("list_particles", func_list_particles);
	func->set_info("list registered particle scripts");
	
	func = core::Func::add("r_loadmodels", func_load_info_models);
	func->set_info("load all models referenced by info record");
}

// unload game assets (zone change)
void unload()
{
	for (core::Entity::Registry::iterator it = core::Entity::registry().begin(); it != core::Entity::registry().end(); it++) {
		core:: Entity *entity = (*it).second;

		if (entity->type() == core::Entity::Globe) {
			core::EntityGlobe *globe = static_cast<core::EntityGlobe *>(entity);
			
			if (globe->texture_id()) {
				render::Textures::unload("textures/" + globe->texturename());
				globe->set_texture_id(0);
			}
			if (globe->corona_id()) {
				render::Textures::unload("textures/corona/" + globe->coronaname());
				globe->set_corona_id(0);
			}
		}

		if (ext_render(entity)) {
			delete ext_render(entity);
		}
	}
}

// clear all assets
void clear()
{
	// clear entity models, and globe textures, this will force a reload
	for (core::Entity::Registry::iterator it = core::Entity::registry().begin(); it != core::Entity::registry().end(); it++) {
		core::Entity *entity = (*it).second;

		if (entity->model())
			entity->set_model(0);

		if (entity->type() == core::Entity::Globe) {
			core::EntityGlobe *globe = static_cast<core::EntityGlobe *>(entity);
			if (globe->texture_id()) {
				render::Textures::unload("textures/" + globe->texturename());
				globe->set_texture_id(0);
			}
			if (globe->corona_id()) {
				render::Textures::unload("textures/corona/" + globe->coronaname());
				globe->set_corona_id(0);
			}
		}

		if (ext_render(entity)) {
			delete ext_render(entity);
		}
	}
	
	// clear particle system scripts
	ParticleScript::clear();
}

// load assets
void load()
{
	// load entity models
	for (core::Entity::Registry::iterator it = core::Entity::registry().begin(); it != core::Entity::registry().end(); it++) {
		core::Entity *entity = (*it).second;

		if (entity->modelname().size()) {
			entity->set_model(model::Model::load(entity->modelname()));
		}
	}
}

// load all models referenced by info records
// this function can be useful when checking models for parsing errors
void load_info_models()
{
	// load all models referenced by info records
	for (core::Info::Registry::iterator it = core::Info::registry().begin(); it != core::Info::registry().end(); it++) {
		core::Info *info = (*it);
		if (info->modelname().size()) {
			model::Model::load(info->modelname());
		}
	}
	
	// load all particle scripts referenced by models
	for (model::Model::Registry::iterator it = model::Model::registry().begin(); it != model::Model::registry().end(); it++) {
		
		for (model::Model::ParticleSystems::iterator pit = (*it).second->particles().begin(); pit != (*it).second->particles().end(); pit++) {
			model::Particles *particles = (*pit);
			if (particles->script().size()) {
				ParticleScript::load(particles->script());
			}
		}
	}

}

// reset render subsystem (module disconnect)
void reset()
{
	clear();

	State::clear();

	Textures::shutdown();

	Textures::init();

	Dust::reset();
}

void resize(int width, int height)
{
	State::resize(width, height);
}

void shutdown()
{
	con_print << "^BShutting down renderer..." << std::endl;

	core::Func::remove("list_particles");
	core::Func::remove("list_textures");
	core::Func::remove("r_loadmodels");

	clear();

	Text::shutdown();

	Textures::shutdown();

	Camera::shutdown();

	Dust::shutdown();

	State::shutdown();
}

} // namespace render