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

#include <string>
#include <sstream>

#include "sys/sys.h"
#include "filesystem/filesystem.h"
#include "core/cvar.h"
#include "core/gameconnection.h"
#include "core/net.h"

namespace core
{
const unsigned long INFOTIMEOUT = 2500; // 2500ms info request timeout

GameConnection* GameConnection::connection_instance = 0;

GameConnection::GameConnection(std::string const &connectionstr)
{
	connection_instance = this;
	connection_network = 0;
	connection_netframe = 0;

	unsigned int port = DEFAULTPORT;
	std::string host(connectionstr);
	size_t found = host.find(':');

	if (found != std::string::npos) {
		std::istringstream str(host.substr(found + 1));
		if (str >> port) {
			host.erase(found, std::string::npos);
		} else {
			con_print << "Invalid hostname '" << host << "'\n";
			abort();
			return;
		}

	}

	connection_network = new NetConnection();
	connection_network->connect(host, port);

	if (!connection_network->connected()) {
		abort();
		return;
	}

	// send connect request
	connection_network->send_connect();
	connection_network->transmit();

	if (!connection_network->connected()) {
		abort();
		return;
	}

	game_players.push_back(localplayer());
	set_playerlist_timestamp(timestamp());
	
	// generate player GUID
	// we do it here because offline play doesn't require it
	if (!localplayer()->guid().size()) {

		// read keys.ini
		
		// write keys.ini
		std::string filename(filesystem::homedir());
		filename.append("keys.ini");
		
		std::ofstream ofs(filename.c_str());
		if (!ofs.is_open()) {
			con_warn << "Could not write " << filename << std::endl;
		} else  {
			ofs << "; keys.ini - osirion client identification" << std::endl;
			ofs << "; DO NOT EDIT OR DELETE THIS FILE" << std::endl;
			ofs << "; If you do you will not be able to use existing characters on a remote server" << std::endl;
			ofs.close();
		}
	}
	
	set_interactive(true);
	set_running(true);
}

GameConnection::~GameConnection()
{
	if (connection_network) {
		connection_network->disconnect();
		delete connection_network;
	}

	connection_instance = 0;
}

Info *GameConnection::request_info(const unsigned int id)
{
	if (!id) {
		con_warn << "Information requested for illegal id 0!" << std::endl;
		return 0;
	}
	
	// find the info record
	Info *info = Info::find(id);
	if (!info) {
		info = new Info(id);
	}

	if (info->type() || (timestamp() < info->timestamp() + INFOTIMEOUT) )
		return info;

	// send an information request to the server
	if (connection_network) {
		connection_network->send_info_request(info);
		connection_network->transmit();
	} else {
		info->add_text("^RNot connected.");
		info->set_timestamp(0);
	}
	return info;
}

Inventory *GameConnection::request_inventory(Entity *entity)
{
	if (!entity) {
		con_warn << "Inventory request for NULL entity" << std::endl;
		return 0;
	}
	
	if (entity->inventory() && connection_network) {
		connection_network->send_inventory_request(entity);
		connection_network->transmit();
	}	
	
	return (entity->inventory());
}

void GameConnection::forward(std::string const &cmdline)
{
	if (!connection_network->connected())
		return;

	connection_network->send_command(cmdline);
}

void GameConnection::rcon(std::string const &cmdline)
{
	if (!connection_network->connected())
		return;

	connection_network->send_rcon(cmdline);
}

void GameConnection::say(std::string const &args)
{
	if (!connection_network->connected())
		return;

	connection_network->send_say(args);
}

void GameConnection::shout(std::string const &args)
{
	if (!connection_network->connected())
		return;

	connection_network->send_shout(args);
}

void GameConnection::private_message(std::string const &args)
{
	if (!connection_network->connected())
		return;

	connection_network->send_private_message(args);
}

void GameConnection::frame(unsigned long timestamp)
{
	if (!running())
		return;

	if (!connection_network->connected()) {
		abort();
		return;
	}

	//update_clientstate();

	// get incoming messages
	connection_network->frame();

	float f = 0;
	if (core::Cvar::net_framerate->value()) {
		f =  1000.0f / core::Cvar::net_framerate->value();
		if (connection_netframe + f > timestamp) {
			return;
		}
	}

	connection_netframe = timestamp;

	if (connection_network->state() == NetConnection::Connected) {

		if (localcontrol() && localcontrol()->dirty()) {
			connection_network->send_client_update(localcontrol());
			localcontrol()->set_dirty(false);

		}

		if (localplayer()->dirty()) {
			connection_network->send_playerinfo();

		}
	}

	set_timestamp(connection_network->timestamp());
	
	connection_network->transmit();
}

}