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

#include <errno.h>
#include <signal.h>

#include <iostream>
#include <vector>
#include <sstream>
#include <fstream>

#include "sys/sys.h"
#include "math/mathlib.h"
#include "filesystem/filesystem.h"
#include "core/application.h"
#include "core/cvar.h"
#include "core/entity.h"
#include "core/func.h"
#include "core/gameconnection.h"
#include "core/gameserver.h"

namespace core
{

// --------------- engine functions  ------------------------------
void func_print(std::string const &args)
{
	con_print << args << "\n";
}

void func_help(std::string const &args)
{
	con_print << "This is the help function\n";
}

void func_quit(std::string const  &args)
{
	application()->shutdown();
	application()->quit(0);
}

void func_connect(std::string const &args)
{
	std::istringstream argstream(args);
	std::string host;
	if (!(argstream >> host))
		host.clear();

	application()->connect(host);
}

void func_disconnect(std::string const  &args)
{
	application()->disconnect();
}

/*
void func_name(std::string const &args) {
	std::istringstream argstream(args);
	std::string name;
	if (argstream >> name) {
		if (name.size() > 16) 
			name = name.substr(0,16);
	} else {
		con_print << "name " << Player::local.name() << "\n";
		return;
	}

	if (name == Player::local.name()) {
		con_print << "name " << name << "\n";
		return;
	}

	if (application()->netserver) {
		std::ostringstream osstream;
		osstream << "msg info " << Player::local.name() << " renamed to " << name << "\n";
		application()->netserver->broadcast(osstream.str());	

		con_print << "msg info " << Player::local.name() << " renamed to " << name << "\n";
	} else if (application()->netconnection.connected()) {
		std::ostringstream osstream;
		osstream << "name " << name << "\n";
		application()->netconnection.send(osstream.str());

		con_print << "name " << name << "\n";
	} else {
		con_print << "name " << name << "\n";
	}

	Player::local.player_name = name;
}
*/ 

// --------------- signal_handler -----------------------------------

extern "C" void signal_handler(int signum)
{
	switch (signum) {
		case SIGHUP:
		case SIGINT:
		case SIGQUIT:
		case SIGTERM:
			if (Application::instance()) {
				con_warn << "Received signal " << signum << ", shutting down...\n";
				application()->shutdown();
				application()->quit(0);
			} else {
				std::cerr << "Received signal " << signum << ", terminated...\n";
				application()->quit(1);
			}
			break;
		default:
			std::cerr << "Received signal " << signum << ", terminated...\n";
			application()->quit(1);
			break;
	}
}

// --------------- Application -----------------------------

Application *Application::application_instance = 0;

Application::Application()
{
	if (application_instance) {
		std::cerr << "multiple core::Application instances!\n";
		sys::quit(2);
	}

	application_instance = this;
	application_time = 0;
	application_game = 0;

	sys::signal(SIGHUP, signal_handler);
	sys::signal(SIGINT, signal_handler);
	sys::signal(SIGQUIT, signal_handler);
	sys::signal(SIGTERM, signal_handler);
}

Application::~Application()
{
	application_instance = 0;
}

void Application::init()
{
	con_debug << "Debug messages enabled\n";
	con_print << "Initializing core...\n";
	
	filesystem::init();

	CommandBuffer::init();
	// dedicated server has set this to 1
	Cvar::sv_dedicated = Cvar::get("sv_dedicated", "0", Cvar::ReadOnly);
	// client can set this to 1
	Cvar::sv_private = Cvar::get("sv_private", "0");

	// load save cvars
	load_config();

	// framerate settings
	Cvar::sv_framerate = Cvar::get("sv_framerate", "25");

	// network settings
	Cvar::net_host = Cvar::get("net_host", "0.0.0.0");
	Cvar::net_port = Cvar::get("net_port", "8042");
	
	// register our engine functions
	Func::add("print", func_print);
	Func::add("help", func_help);
	Func::add("quit", func_quit);
	
	Func::add("connect", func_connect);
	Func::add("disconnect", func_disconnect);
	
	//Func::add("name", func_name);
}

void Application::shutdown()
{
	con_print << "Shutting down core...\n";

	if (connected())
		disconnect();

	if (application_game) {
		delete application_game;
		application_game = 0;
	}

	save_config();

	// remove our engine functions
	Func::remove("print");
	Func::remove("help");
	Func::remove("quit");
	
	Func::remove("connect");
	Func::remove("disconnect");
	//Func::remove("name");

	CommandBuffer::shutdown();

	filesystem::shutdown();
}

void Application::quit(int status)
{
	sys::quit(status);
}


void Application::connect(std::string const &host)
{
	if (connected()) {
		con_warn << "Connected. Disconnect first.\n";
		return;
	}

	if (application_game) {
		delete application_game;
		application_game = 0;
	}
		
	if (host.size()) {
		application_game = new GameConnection(host);

		if (application_game->running()) {
			con_print << "Connected to '" << host << "'\n";
		} else {
			delete application_game;
			application_game = 0;
			con_warn << "Could not connect to '" << host << "'!\n";
		}
	} else {
		application_game = new GameServer();

		if (application_game->running()) {
			con_print << "Connected to local game.\n";
		} else {
			delete application_game;
			application_game = 0;
			con_warn << "Could not connect to local game!\n";
		}
	}
}

void Application::disconnect()
{

	if(application_game) {
		delete application_game;
		application_game = 0;
		con_print << "Disconnected.\n";
	}
}

void Application::frame(float seconds)
{
	// execute commands in the buffer
	CommandBuffer::exec();

	application_time += seconds;
	
	// don't run zero lenght time frames
	if (seconds == 0.0f)
		return;

	if (!connected())
		return;
	
	// run a game interface frame	
	application_game->frame(seconds);
			
	if (!application_game->running()) 
		disconnect();
}

void Application::save_config()
{
	std::string filename(filesystem::writedir);
	filename.append("config.txt");
	std::ofstream ofs(filename.c_str());

	if (!ofs.is_open()) {
		con_warn << "Could not write " << filename << std::endl;
		return;
	}
	
	std::map<std::string, Cvar*>::iterator it;
	for (it = Cvar::registry.begin(); it != Cvar::registry.end(); it++) {

		if (((*it).second->flags() & Cvar::Archive) == Cvar::Archive)
			ofs << "set " << (*it).first << " " << (*it).second->str() << std::endl;		
	}
	ofs.close();
}

void Application::load_config()
{
	std::string filename(filesystem::writedir);
	filename.append("config.txt");
	std::ifstream ifs(filename.c_str(), std::ifstream::in);

	if (!ifs.is_open()) {
		con_warn << "Could not read " << filename << std::endl;
		return;
	}

	char line[MAXCMDSIZE];
	while (ifs.getline(line, MAXCMDSIZE-1)) {
		cmd() << line << "\n";
		
	}
	
	// execute commands in the buffer
	CommandBuffer::exec();
}
}