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

#include "auxiliary/functions.h"
#include "core/info.h"
#include "sys/sys.h"
#include "core/info.h"
#include "core/gameinterface.h"

#include <iomanip>

namespace core
{

/* ---- class InfoType --------------------------------------------- */

InfoType::InfoType(const char *label) : Label(label)
{
	infotype_registry.push_back(this);
}

InfoType::~InfoType()
{
}

/* ---- static InfoType registry ----------------------------------- */

InfoType::Registry InfoType::infotype_registry;

void InfoType::clear()
{
	for (Registry::iterator it = infotype_registry.begin(); it != infotype_registry.end(); it++) {
		InfoType *infotype = (*it);
		delete infotype;
	}
	infotype_registry.clear();
}

InfoType *InfoType::find(const std::string & label)
{
	if (!label.size())
		return 0;

	for (Registry::iterator it = infotype_registry.begin(); it != infotype_registry.end(); it++) {
		InfoType *infotype = (*it);
		if (infotype->label().compare(label) == 0) {
			return infotype;
		}
	}
	return 0;
}

void InfoType::list()
{
	if (infotype_registry.size()) {
		con_print << "  ";
		for (Registry::const_iterator it = infotype_registry.begin(); it != infotype_registry.end(); it++) {
			InfoType *infotype = (*it);
			
			con_print << infotype->label() << " ";
		}
		con_print << std::endl;
	}
	con_print << "^B  " << infotype_registry.size() <<  " info types" << std::endl;
}


/* ---- class Info ------------------------------------------------- */
unsigned int info_id_counter = 0;

// server-side constructor, assigns an id
Info::Info(const InfoType *type, const char *label) : Label(label)
{
	info_id_counter++;
	info_id = info_id_counter;
	info_type = type;
	info_registry.push_back(this);	
	
	info_timestamp = 0;
	info_level = 1;
	info_price = 0;
	info_volume = 0;
}

// client-side constructor, id is passed as param
Info::Info(const unsigned int id)
{
	info_id = id;
	info_type = 0;
	info_registry.push_back(this);

	info_timestamp = 1;
	info_level = 1;
	info_price = 0;
	info_volume = 0;
	
	add_text("Requesting server information...");
}

Info::~Info()
{
	info_text.clear();
	info_modelname.clear();
	info_text.clear();
	info_timestamp = 0;
	info_type = 0;
}

void Info::set_id(const unsigned int id)
{
	info_id = id;
}

void Info::set_type(const InfoType *type)
{
	info_type = type;
}

void Info::set_modelname(const std::string & modelname)
{
	info_modelname.assign(modelname);
}

void Info::set_modelname(const char *modelname)
{
	info_modelname.assign(modelname);
}

void Info::set_level(const Level level)
{
	info_level = level;
}
	
void Info::set_price(const long price)
{
	info_price = price;
}

void Info::set_volume(const float volume)
{
	info_volume = volume;
}
void Info::set_timestamp(const unsigned long timestamp)
{
	info_timestamp = timestamp;
}

void Info::clear_timestamp()
{
	info_timestamp = 0;
}

void Info::add_line(const std::string & text)
{
	add_line(text.c_str());
}

void Info::add_line(const char *text)
{
	std::string str(text);
	aux::strip_quotes(str);
	info_text.push_back(str);
}

void Info::add_text(const char *text)
{
	std::string str(text);
	aux::strip_quotes(str);
	
	if (!info_text.size()) {
		info_text.push_back(str);
		
	} else if (str.size()) {
		if ((*info_text.rbegin()).size()) {
			(*info_text.rbegin()) += ' ';
			(*info_text.rbegin()).append(str);
		} else {
			info_text.push_back(str);
		}
	} else {	
		info_text.push_back(str);
	}
}

void Info::add_text(const std::string & text)
{
	add_text(text.c_str());
}

void Info::clear_text()
{
	info_text.clear();
}

void Info::serialize_server_update(std::ostream & os) const
{
	os << '"' << name() << "\" \"" << modelname() << "\" " 
		<< price() << " "
		<< volume() << " "
		<< info_text.size() << " ";

	for (Text::const_iterator it = info_text.begin(); it != info_text.end(); it++) {
		if (it != info_text.begin())
			os << ' ';

		os << '"' << (*it) << '"';
	}
}

void Info::receive_server_update(std::istream &is)
{
	std::string n;
	char c;

	// read name
	n.clear();
	while ((is.get(c)) && (c != '"'));
	while ((is.get(c)) && (c != '"'))
		n += c;
	set_name(n);

	// read model name
	n.clear();
	while ((is.get(c)) && (c != '"'));
	while ((is.get(c)) && (c != '"'))
		n += c;
	set_modelname(n);
	
	is >> info_price;
	is >> info_volume;

	// read info text
	size_t s;	
	if (!(is >> s))
		s = 0;
	
	clear_text();
	for (size_t i = 0; (i < s)  && is.good(); i++) {
		n.clear();
		while ((is.get(c)) && (c != '"'));
		while ((is.get(c)) && (c != '"'))
			n += c;

		add_line(n);
	}
	
	// set timestamp to 0
	info_timestamp = 0;
}

void Info::print() const
{
	con_print << "  ^Ninfo id ^B" << std::setw(4) << std::setfill(' ') << id() << " ^Ntype ^B" << (type() ? type()->label() : "NULL") << " ^Nlabel ^B" << label() << " ^Nname ^B" << name() << " ^Nmodel ^B" << modelname() << "^N" << std::endl;
	if (info_text.size()) {
		for (Text::const_iterator it = info_text.begin(); it != info_text.end(); it++) {
			con_print << "  " << (*it) << std::endl;
		}
	}
}

/* ---- static info registry --------------------------------------- */

Info::Registry Info::info_registry;

Info *Info::find(const Info *info)
{
	if (!info)
		return 0;
		
	for (Registry::iterator it = info_registry.begin(); it != info_registry.end(); it++) {
		if (info == (*it)) {
			return *it;
		}
	}
	return 0;
}

Info *Info::find(unsigned int id)
{
	if (!id)
		return 0;
		
	for (Registry::iterator it = info_registry.begin(); it != info_registry.end(); it++) {
		Info *info = (*it);
		if (info->id() == id) {
			return info;
		}
	}
	return 0;
}

Info *Info::find(const char *label)
{
	if (!label)
		return 0;

	for (Registry::iterator it = info_registry.begin(); it != info_registry.end(); it++) {
		Info *info = (*it);
		if (info->label().compare(label) == 0) {
			return info;
		}
	}
	return 0;
}

Info *Info::find(const std::string & label)
{
	if (!label.size())
		return 0;
		
	for (Registry::iterator it = info_registry.begin(); it != info_registry.end(); it++) {
		Info *info = (*it);
		if (info->label().compare(label) == 0) {
			return info;
		}
	}
	return 0;
}

Info *Info::search(const std::string & searchstr)
{
	if (!searchstr.size())
		return 0;

	std::string strsearchkey(aux::lowercase(searchstr));
	if (strsearchkey.size() < 3) {
		return 0;
	}

	for (Registry::iterator it = info_registry.begin(); it != info_registry.end(); it++) {
		Info *info = *(it);

		std::string labelstr(info->label());
		if (labelstr.size() && (labelstr.find(strsearchkey) != std::string::npos)) {
			return info;
		}

		std::string namestr(aux::lowercase(info->name()));
		if (namestr.size() && (namestr.find(strsearchkey) != std::string::npos)) {
			return info;
		}
	}

	return 0;
}

Info *Info::find(const InfoType *type, const char *label)
{
	if (!label)
		return 0;

	for (Registry::iterator it = info_registry.begin(); it != info_registry.end(); it++) {
		Info *info = (*it);
		if ((info->type() == type) && (info->label().compare(label) == 0)) {
			return info;
		}
	}
	return 0;
}

Info *Info::find(const InfoType *type, const std::string & label)
{
	if (!label.size())
		return 0;

	for (Registry::iterator it = info_registry.begin(); it != info_registry.end(); it++) {
		Info *info = (*it);
		if ((info->type() == type) && (info->label().compare(label) == 0)) {
			return info;
		}
	}
	return 0;
}

Info *Info::search(const InfoType *type, const std::string & searchstr)
{
	if (!searchstr.size())
		return 0;

	std::string strsearchkey(aux::lowercase(searchstr));
	if (strsearchkey.size() < 3) {
		return 0;
	}

	for (Registry::iterator it = info_registry.begin(); it != info_registry.end(); it++) {
		Info *info = *(it);

		if (info->type() == type) {
			std::string labelstr(info->label());
			if (labelstr.size() && (labelstr.find(strsearchkey) != std::string::npos)) {
				return info;
			}

			std::string namestr(aux::lowercase(info->name()));
			if (namestr.size() && (namestr.find(strsearchkey) != std::string::npos)) {
				return info;
			}
		}
	}

	return 0;
}

void Info::clear()
{
	for (Registry::iterator it = info_registry.begin(); it != info_registry.end(); it++) {
		Info *info = (*it);
		delete info;
	}
	info_registry.clear();
	info_id_counter = 0;

	InfoType::clear();
}

void Info::list_header()
{
	con_print << "  "
		<< "  id "
		<< "type     "
		<< "label        "
		<< "name" << std::endl;
}
void Info::list(const Info *info)
{
	con_print << "  "
		<< "^B" << std::setw(4) << info->id() << " "
		<< "^N" << (info->type() ? aux::pad_right(info->type()->label(), 8) : "NULL    ") << " "
		<< "^N" << aux::pad_right(info->label(), 12) << " "
		<< "^N" << info->name() << std::endl;
}

void Info::list()
{
	InfoType::list();
	list_header();
	for (Registry::iterator it = info_registry.begin(); it != info_registry.end(); it++) {
		Info *info = (*it);
		list(info);
	}
	con_print << "^B  " << info_registry.size() << " info " << aux::plural("record", info_registry.size()) << std::endl;
}

void Info::list(const InfoType *type)
{
	size_t count = 0;
	list_header();
	for (Registry::iterator it = info_registry.begin(); it != info_registry.end(); it++) {
		core::Info *info = (*it);
		if (info->type() == type) {
			count++;
			list(info);
		}
	}
	con_print << "^B  " << count << " " << type->label() << " info " << aux::plural("record", count) << std::endl;
}

}