/*
   base/faction.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/func.h"
#include "core/parser.h"
#include "core/range.h"
#include "base/faction.h"

namespace game {

core::InfoType *Faction::faction_infotype = 0;
Faction *Faction::faction_default = 0;

void func_list_faction(const std::string &args)
{
	Faction::list();
}

Faction *Faction::find(const std::string & label)
{
	if (!label.size()) {
		return 0;
	}
	
	return (Faction *) core::Info::find(faction_infotype, label);
}

void Faction::list()
{
	core::Info::list(faction_infotype);
}

bool Faction::init()
{
	// initialize faction InfoType
	faction_infotype = new core::InfoType("faction");
	
	std::string inifilename("ini/factions");

	filesystem::IniFile inifile;
	inifile.open(inifilename);

	if (!inifile.is_open()) {
		con_warn << "Could not open " << inifile.name() << std::endl;
		return false;
	}

	con_print << "^BLoading factions..." << std::endl;
	
	core::Func *func = core::Func::add("list_faction", func_list_faction);
	func->set_info("list available factions");

	size_t count = 0;
	Faction *faction = 0;
	std::string strvalue;
	float floatvalue;
	math::Color colorvalue;
	
	std::string factionlabel;
	
	while (inifile.getline()) {
	
		if (inifile.got_section()) {
			
			if (inifile.got_section("faction")) {
				count++;
				faction = new Faction();
				
			} else if (inifile.got_section("reputation")) {
				factionlabel.clear();

			} else {
				inifile.unknown_section();
				
				faction = 0;
				factionlabel.clear();
			}

		} else if (inifile.got_key()) {

			if (inifile.in_section("faction")) {
				
				if (inifile.got_key_label("label", strvalue)) {
					faction->set_label(strvalue);
					continue;
				
				} else if (inifile.got_key_string("name", strvalue)) {
					faction->set_name(strvalue);
					continue;
				
				} else if (inifile.got_key_string("info", strvalue)) {
					faction->add_text(strvalue);
					continue;
					
				} else if (inifile.got_key_color("color", colorvalue)) {
					faction->set_color(colorvalue);
					continue;
				
				} else if (inifile.got_key_color("colorsecond", colorvalue)) {
					faction->set_color_second(colorvalue);
					continue;

				} else {
					inifile.unknown_key();
				}
			} else if (inifile.in_section("reputation")) {
				
				if (faction) {				
					if (inifile.got_key_label("faction", factionlabel)) {
						continue;
						
					} else if (inifile.got_key_float("reputation", floatvalue)) {
						if (factionlabel.size()) {
							faction->reputation().set_reputation(factionlabel, floatvalue);
						} else {
							inifile.unknown_error("reputation key without faction");
						}
						continue;
					} else {
						inifile.unknown_key();
					}
				}
			}
		}
	}

	inifile.close();

	if (!count) {
		con_warn << "No factions found!" << std::endl;
		return true; // non-fatal
	}
	
	// create default faction
	faction_default = Faction::find("default");
	if (!faction_default) {
		faction_default = new Faction();
		faction_default->set_label("default");
		faction_default->set_name("Default");
	} else {
		faction_default->reputation().clear();
	}
	
	// validate reputation
	for (core::Info::Registry::iterator it = core::Info::registry().begin(); it != core::Info::registry().end(); it++) {
		core::Info *info = (*it);
		
		if (info->type() == faction_infotype) {
			faction = static_cast<Faction *>(info);
			core::Reputation::FactionReps::iterator rip = faction->reputation().factionreps().begin();
			while (rip != faction->reputation().factionreps().end()) {
				Faction *otherfaction = Faction::find((*rip)->label());
				
				if (!otherfaction) {
					con_warn << "  faction '" << faction->label() << "': unknown faction '" << (*rip)->label() << "' in reputation" << std::endl;
					faction->reputation().factionreps().erase(rip++);
				} else {
					if (otherfaction == faction_default) {
						faction_default->reputation().set_reputation(faction, (*rip)->reputation());
					}
					(*rip)->set_faction(otherfaction);
					++rip;
				}
			}
		}
	}

	con_debug << "  " << inifile.name() << " " << count << " factions" << std::endl;
	
	return true;
}

void Faction::done()
{
	core::Func::remove("list_faction");
	
	faction_default = 0;
}

Faction::Faction() : 
	core::Info(faction_infotype),
	faction_color(),
	faction_color_second() 
{	

}

Faction::~Faction()
{
}

void Faction::apply(core::Entity *entity) const
{
	assert(entity);
	
	// set primary color
	entity->set_color(color());
	
	// set secondary color
	entity->set_color_second(color_second());
	
	// set entity faction
	entity->set_faction(this);
}


void Faction::apply_kill(core::Player *player) const
{
	assert(player);
	
	// a kill will cost the player 5 reputation points with this faction
	const float points = 5.0f;
	
	// a kill will cost the player 1 reputation point with this faction's friends and allies
	const float points_ally = 1.0f;
	
	// adjust current player reputation for this faction
	player->reputation().set_reputation(this, player->reputation(this) - points);
	
	// adjust player reputation for this factions's allies and enemies
	for (core::Reputation::FactionReps::const_iterator rip = faction_reputation.factionreps_begin(); rip != faction_reputation.factionreps_end(); ++rip) {
		assert((*rip)->faction());
		
		if ((*rip)->faction() != faction_default) {
			const float other_faction_reputation = faction_reputation.reputation((*rip)->faction());
			if (other_faction_reputation >= core::range::reputation_friendly) {
				// allied faction
				player->reputation().set_reputation((*rip)->faction(), player->reputation((*rip)->faction()) - points_ally);
			} else if (other_faction_reputation <= core::range::reputation_hostile) {
				//enemy faction
				player->reputation().set_reputation((*rip)->faction(), player->reputation((*rip)->faction()) + points_ally);
			}
		}
	}
	
	player->reputation().set_dirty();
}

void Faction::apply_sale(core::Player *player, const float amount) const
{
	assert(player);
}


void Faction::apply_default(core::Reputation & reputation)
{
	// apply default reputation	
	if (faction_default) {
		for (core::Reputation::FactionReps::const_iterator rip = faction_default->reputation().factionreps().begin(); rip != faction_default->reputation().factionreps().end(); ++rip) {
			// only add defaults the reputation doesn't have yet
			if (!reputation.find((*rip)->faction())) {
				reputation.set_reputation((*rip)->faction(), (*rip)->reputation());
			}
		}
	}
	reputation.set_dirty();
}

} // namespace game