/*
 * Copyright (C) 2002-2004, 2006-2008 by the Widelands Development Team
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

#include "warehouse.h"
#include "warehousesupply.h"

#include <algorithm>

#include "battle.h"
#include "carrier.h"
#include "editor_game_base.h"
#include "game.h"
#include "player.h"
#include "profile.h"
#include "request.h"
#include "requirements.h"
#include "soldier.h"
#include "transport.h"
#include "wexception.h"
#include "worker.h"
#include "tribe.h"

#include "log.h"

#include "upcast.h"

namespace Widelands {

static const int32_t CARRIER_SPAWN_INTERVAL = 2500;

WarehouseSupply::~WarehouseSupply()
{
	if (m_economy)
	{
		log
			("WarehouseSupply::~WarehouseSupply: Warehouse %u still belongs to "
			 "an economy",
			 m_warehouse->get_serial());
		set_economy(0);
	}

	// We're removed from the Economy. Therefore, the wares can simply
	// be cleared out. The global inventory will be okay.
	m_wares  .clear();
	m_workers.clear();
}

/*
 * Inform this supply, how much wares
 * are to be handled
 */
void WarehouseSupply::set_nrwares(Ware_Index const i) {
	assert(Ware_Index::First() == m_wares.get_nrwareids());

	m_wares.set_nrwares(i);
}
void WarehouseSupply::set_nrworkers(Ware_Index const i) {
	assert(Ware_Index::First() == m_workers.get_nrwareids());

	m_workers.set_nrwares(i);
}



/*
===============
WarehouseSupply::set_economy

Add and remove our wares and the Supply to the economies as necessary.
===============
*/
void WarehouseSupply::set_economy(Economy* e)
{
	if (e == m_economy)
		return;

	if (m_economy) {
		m_economy->remove_supply(this);
		for
			(Ware_Index i = Ware_Index::First(); i < m_wares.get_nrwareids(); ++i)
			if (m_wares.stock(i))
				m_economy->remove_wares(i, m_wares.stock(i));
		for
			(Ware_Index i = Ware_Index::First();
			 i < m_workers.get_nrwareids();
			 ++i)
			if (m_workers.stock(i))
				m_economy->remove_workers(i, m_workers.stock(i));
	}

	m_economy = e;

	if (m_economy) {
		for
			(Ware_Index i = Ware_Index::First(); i < m_wares.get_nrwareids(); ++i)
			if (m_wares.stock(i))
				m_economy->add_wares(i, m_wares.stock(i));
		for
			(Ware_Index i = Ware_Index::First();
			 i < m_workers.get_nrwareids();
			 ++i)
			if (m_workers.stock(i))
				m_economy->add_workers(i, m_workers.stock(i));
		m_economy->add_supply(this);
	}
}


/*
===============
WarehouseSupply::add_wares

Add wares and update the economy.
===============
*/
void WarehouseSupply::add_wares     (Ware_Index const id, uint32_t const count)
{
	if (!count)
		return;

	m_economy->add_wares(id, count);
	m_wares.add(id, count);
}


/*
===============
WarehouseSupply::remove_wares

Remove wares and update the economy.
===============
*/
void WarehouseSupply::remove_wares  (Ware_Index const id, uint32_t const count)
{
	if (!count)
		return;

	m_wares.remove(id, count);
	m_economy->remove_wares(id, count);
}

/*
===============
WarehouseSupply::add_workers

Add workers and update the economy.

===============
*/
void WarehouseSupply::add_workers   (Ware_Index const id, uint32_t const count)
{
	if (!count)
		return;

	m_economy->add_workers(id, count);
	m_workers.add(id, count);
}


/*
===============
WarehouseSupply::remove_workers

Remove workers and update the economy.

Comments see add_workers
===============
*/
void WarehouseSupply::remove_workers(Ware_Index const id, uint32_t const count)
{
	if (!count)
		return;

	m_workers.remove(id, count);
	m_economy->remove_workers(id, count);
}

/*
===============
WarehouseSupply::get_position

Return the position of the Supply, i.e. the owning Warehouse.
===============
*/
PlayerImmovable* WarehouseSupply::get_position(Game *) {return m_warehouse;}


/*
===============
WarehouseSupply::is_active

Warehouse supplies are never active.
===============
*/
bool WarehouseSupply::is_active() const throw () {return false;}


uint32_t WarehouseSupply::nr_supplies
	(Game * const game, Request const * req) const
{
	if (req->get_type() == Request::WARE) {
		return m_wares.stock(req->get_index());
	} else {
		return m_warehouse->count_workers(game, req->get_index(), req->get_requirements());
	}
}


/*
===============
Launch a ware as item.
===============
*/
WareInstance & WarehouseSupply::launch_item(Game * game, const Request* req) {
	if (req->get_type() != Request::WARE)
		throw wexception("WarehouseSupply::launch_item: called for non-ware request");
	if (!m_wares.stock(req->get_index()))
		throw wexception("WarehouseSupply::launch_item: called for non-existing ware");

	return m_warehouse->launch_item(game, req->get_index());
}


/*
===============
WarehouseSupply::launch_worker

Launch a ware as worker.
===============
*/
Worker* WarehouseSupply::launch_worker(Game* g, const Request* req)
{
	return m_warehouse->launch_worker(g, req->get_index(), req->get_requirements());
}


/*
==============================================================================

Warehouse building

==============================================================================
*/

/*
===============
Warehouse_Descr::Warehouse_Descr

Initialize with sane defaults
===============
*/
Warehouse_Descr::Warehouse_Descr
	(Tribe_Descr const & tribe_descr, std::string const & warehouse_name)
:
Building_Descr(tribe_descr, warehouse_name),
m_subtype     (Subtype_Normal),
m_conquers    (0)
{}

/*
===============
Warehouse_Descr::parse

Parse the additional warehouse settings from the given profile and directory
===============
*/
void Warehouse_Descr::parse
	(char         const * const directory,
	 Profile            * const prof,
	 enhancements_map_t &       enhancements_map,
	 EncodeData const * const encdata)
{
	Building_Descr::parse(directory, prof, enhancements_map, encdata);

	Section & global = prof->get_safe_section("global");
	char const * const string = global.get_safe_string("subtype");
	if (!strcasecmp(string, "HQ")) {
		m_subtype = Subtype_HQ;
	} else if (!strcasecmp(string, "port")) {
		m_subtype = Subtype_Port;
	} else if (!strcasecmp(string, "none")) {
		//
	} else
		throw wexception
			("Unsupported warehouse subtype '%s'. Possible values: none, HQ, "
			 "port",
			 string);

	if (m_subtype == Subtype_HQ) {
		m_conquers = global.get_int("conquers");
		if (0 < m_conquers)
			m_workarea_info[m_conquers].insert(descname() + _(" conquer"));
	}
}


/*
==============================

IMPLEMENTATION

==============================
*/

/*
===============
Warehouse::Warehouse

Initialize a warehouse (zero contents, etc...)
===============
*/
Warehouse::Warehouse(const Warehouse_Descr & warehouse_descr) :
Building            (warehouse_descr),
m_supply            (new WarehouseSupply(this)),
m_next_carrier_spawn(0)
{}


/*
===============
Warehouse::Warehouse

Cleanup
===============
*/
Warehouse::~Warehouse()
{
	delete m_supply;
}

/*
Warehouse::get_priority
warehouses determine how badly they want a certain ware
*/
int32_t Warehouse::get_priority(int32_t type, Ware_Index ware_index, bool adjust) const {
	int MAX_IDLE_PRIORITY = 100;
	if (type == Request::WARE){
		if (m_target_supply[ware_index] > 0){
			MAX_IDLE_PRIORITY = 500000;
		}
	}
	return MAX_IDLE_PRIORITY;
}

void Warehouse::set_needed(Ware_Index ware_index, int value){
	//assert (value >= m_supply->stock_wares(ware_index));
	if (value > m_supply->stock_wares(ware_index)){
		m_target_supply[ware_index] = value - m_supply->stock_wares(ware_index);
	}
}

/*
===============
Warehouse::init

Conquer the land around the HQ on init.
===============
*/
void Warehouse::init(Editor_Game_Base* gg)
{
	Building::init(gg);

	Tribe_Descr const & tribe = owner().tribe();
	Ware_Index const nr_wares   = tribe.get_nrwares  ();
	Ware_Index const nr_workers = tribe.get_nrworkers();
	m_supply->set_nrwares  (nr_wares);
	m_supply->set_nrworkers(nr_workers);

	if (upcast(Game, game, gg)) {
		for (Ware_Index i = Ware_Index::First(); i < nr_wares;   ++i) {
			Request & req =
				*new Request
					(this, i, &Warehouse::idle_request_cb, this, Request::WARE);

			req.set_idle(true);

			m_requests.push_back(&req);
		}
		for (Ware_Index i = Ware_Index::First(); i < nr_workers; ++i) {
			Request & req =
				*new Request
					(this, i, &Warehouse::idle_request_cb, this, Request::WORKER);

			req.set_idle(true);

			m_requests.push_back(&req);
		}
		m_next_carrier_spawn = schedule_act(game, CARRIER_SPAWN_INTERVAL);
		m_next_military_act  = schedule_act(game, 1000);
	}
	m_target_supply.resize(m_requests.size());

	// Even though technically, a warehouse might be completely empty,
	// we let warehouse see always for simplicity's sake (since there's
	// almost always going to be a carrier inside, that shouldn't hurt).
	Player & player = owner();
	Map & map = gg->map();
	player.see_area
		(Area<FCoords>(map.get_fcoords(get_position()), vision_range()));
}


/*
===============
Warehouse::cleanup

Destroy the warehouse.
===============
*/
void Warehouse::cleanup(Editor_Game_Base* gg)
{
	if (upcast(Game, game, gg)) {

		while (m_requests.size()) {
			delete m_requests.back();
			m_requests.pop_back();
		}

		//  all cached workers are unbound and freed
		while (m_incorporated_workers.size()) {
			//  If the game ends and this worker has been created before this
			//  warehouse, it might already be deleted. So do not try and free him
			if (upcast(Worker, worker, m_incorporated_workers.begin()->get(gg)))
				worker->reset_tasks(game);
			m_incorporated_workers.erase(m_incorporated_workers.begin());
		}
	}
	if (const uint32_t conquer_radius = get_conquers())
		gg->unconquer_area
			(Player_Area<Area<FCoords> >
			 	(owner().get_player_number(),
			 	 Area<FCoords>
			 	 	(gg->map().get_fcoords(get_position()), conquer_radius)),
			 m_defeating_player);

	// Unsee the area that we started seeing in init()
	Player & player = owner();
	Map & map = gg->map();
	player.unsee_area
			(Area<FCoords>(map.get_fcoords(get_position()), vision_range()));

	Building::cleanup(gg);
}


/// Act regularly to create carriers. According to intelligence, this is some
/// highly advanced technology. Not only do the settlers have no problems with
/// birth control, they don't even need anybody to procreate. They must have
/// built-in DNA samples in those warehouses. And what the hell are they doing,
/// killing useless tribesmen! The Borg? Or just like Soylent Green?
/// Or maybe I should just stop writing comments that late at night ;-)
void Warehouse::act(Game* g, uint32_t data)
{
	const Tribe_Descr & tribe = owner().tribe();
	if (g->get_gametime() - m_next_carrier_spawn >= 0) {
		Ware_Index const id = tribe.safe_worker_index("carrier");
		int32_t stock = m_supply->stock_workers(id);
		int32_t tdelta = CARRIER_SPAWN_INTERVAL;

		if (stock < 100) {
			tdelta -= 4*(100 - stock);
			insert_workers(id, 1);
		} else if (stock > 100) {
			tdelta -= 4*(stock - 100);
			if (tdelta < 10)
				tdelta = 10;
			remove_workers(id, 1);
		}

		m_next_carrier_spawn = schedule_act(g, tdelta);
	}

	//  Military stuff: Kill the soldiers that are dead.
	if (g->get_gametime() - m_next_military_act >= 0) {
		Ware_Index const ware = tribe.safe_worker_index("soldier");
		const Worker_Descr & workerdescr = *tribe.get_worker_descr(ware);
		const std::string & workername = workerdescr.name();
		//  Look if we got one in stock of those.
		for
			(std::vector<Object_Ptr>::iterator it = m_incorporated_workers.begin();
			 it != m_incorporated_workers.end();
			 ++it)
		{
			Worker const & worker = dynamic_cast<Worker const &>(*it->get(g));
			if (worker.name() == workername) {
				upcast(Soldier const, soldier, &worker);

				//  Soldier dead ...
				if (not soldier or soldier->get_current_hitpoints() == 0) {
					m_incorporated_workers.erase(it);
					m_supply->remove_workers(ware, 1);
					continue;
				}
				//  If warehouse can heal, this is the place to put it.
			}
		}
		m_next_military_act = schedule_act (g, 1000);
	}
	Building::act(g, data);
}


/*
===============
Warehouse::set_economy

Transfer our registration to the new economy.
===============
*/
void Warehouse::set_economy(Economy* e)
{
	Economy* old = get_economy();

	if (old == e)
		return;

	if (old)
		old->remove_warehouse(this);

	m_supply->set_economy(e);
	Building::set_economy(e);

	for (uint32_t i = 0; i < m_requests.size(); ++i)
		m_requests[i]->set_economy(e);

	if (e)
		e->add_warehouse(this);
}


/*
===============
Warehouse::get_wares
===============
*/
const WareList& Warehouse::get_wares() const
{
	return m_supply->get_wares();
}

/*
===============
Warehouse::get_workers
===============
*/
const WareList& Warehouse::get_workers() const
{
	return m_supply->get_workers();
}



/*
===============
Warehouse::create_wares

Magically create wares in this warehouse. Updates the economy accordingly.
===============
*/
void Warehouse::insert_wares(Ware_Index const id, uint32_t const count)
{
	assert(get_economy());
	if (m_target_supply[id] > count){
		m_target_supply[id] -= count;
	} else {
		m_target_supply[id] = 0;
	}
	m_supply->add_wares(id, count);
}


/*
===============
Warehouse::destroy_wares

Magically destroy wares.
===============
*/
void Warehouse::remove_wares(Ware_Index const id, uint32_t const count)
{
	assert(get_economy());
	if (m_target_supply[id] > 0){
		m_target_supply[id] += 1;
	}
	m_supply->remove_wares(id, count);
}

/*
===============
Warehouse::create_workers

Magically create workers in this warehouse. Updates the economy accordingly.
===============
*/
void Warehouse::insert_workers(Ware_Index const id, uint32_t const count)
{
	assert(get_economy());

	m_supply->add_workers(id, count);
}


/*
===============
Warehouse::destroy_workers

Magically destroy workers.
===============
*/
void Warehouse::remove_workers(Ware_Index const id, uint32_t const count)
{
	assert(get_economy());

	m_supply->remove_workers(id, count);
}


/*
===============
Warehouse::fetch_from_flag

Launch a carrier to fetch an item from our flag.
===============
*/
bool Warehouse::fetch_from_flag(Game* g)
{
	Ware_Index const carrierid = owner().tribe().safe_worker_index("carrier");

	if (!m_supply->stock_workers(carrierid)) // XXX yep, let's cheat
		insert_workers(carrierid, 1);

	launch_worker(g, carrierid, Requirements())->start_task_fetchfromflag(g);

	return true;
}


/**
 * \return the number of workers that we can launch satisfying the given
 * requirements.
 */
uint32_t Warehouse::count_workers(Game* g, Ware_Index ware, const Requirements& req) {
	Tribe_Descr const & tribe = owner().tribe();
	std::vector<Map_Object_Descr*> subs;
	uint32_t sum = 0;

	do {
		subs.push_back(tribe.get_worker_descr(ware));
		sum += m_supply->stock_workers(ware);
		ware = tribe.get_worker_descr(ware)->becomes();
	} while (ware != Ware_Index::Null());

	// NOTE: This code lies about the tAttributes of non-instantiated workers.

	std::vector<Object_Ptr>::const_iterator const incorporated_workers_end =
		m_incorporated_workers.end();
	for
		(std::vector<Object_Ptr>::iterator it = m_incorporated_workers.begin();
		 it != incorporated_workers_end;
		 ++it)
	{
		Map_Object* w = it->get(g);
		if (w && std::find(subs.begin(), subs.end(), &w->descr()) != subs.end()) {
			// This is one of the workers in our sum
			if (!req.check(w))
				--sum;
		}
	}

	return sum;
}


/*
===============
Warehouse::launch_worker

Start a worker of a given type. The worker will be assigned a job by the caller.
===============
*/
Worker * Warehouse::launch_worker(Game * game, Ware_Index ware, const Requirements& req) {
	Tribe_Descr const & tribe = owner().tribe();

	do {
		if (m_supply->stock_workers(ware)) {
			uint32_t unincorporated = m_supply->stock_workers(ware);
			const Worker_Descr & workerdescr = *owner().tribe().get_worker_descr(ware);

			//  look if we got one of those in stock
			const std::string & workername = workerdescr.name();
			std::vector<Object_Ptr>::const_iterator const incorporated_workers_end =
				m_incorporated_workers.end();
			for
				(std::vector<Object_Ptr>::iterator it = m_incorporated_workers.begin();
				 it != incorporated_workers_end;
				 ++it)
			{
				if (upcast(Worker, worker, it->get(game))) {
					if (worker->name() == workername) {
						--unincorporated;

						if (req.check(worker)) {
							worker->reset_tasks(game);  //  forget everything you did
							worker->set_location(this); //  back in a economy
							m_incorporated_workers.erase(it);

							m_supply->remove_workers(ware, 1);
							return worker;
						}
					}
				}
			}

			assert(unincorporated <= m_supply->stock_workers(ware));

			if (unincorporated) {
				// Create a new one
				// NOTE: This code lies about the tAttributes of the new worker
				m_supply->remove_workers(ware, 1);
				return &workerdescr.create(*game, owner(), *this, m_position);
			}
		}

		if (can_create_worker(game, ware)) {
			// don't want to use an upgraded worker, so create new one.
			create_worker(game, ware);
		} else {
			ware = tribe.get_worker_descr(ware)->becomes();
		}
	} while (ware != Ware_Index::Null());

	throw wexception("Warehouse::launch_worker: Worker doesn't actually exist");
}


/*
===============
This is the opposite of launch_worker: destroy the worker and add the
appropriate ware to our warelist
===============
*/
void Warehouse::incorporate_worker(Game* g, Worker* w)
{
	assert(w->get_owner() == &owner());

	Ware_Index const index = owner().tribe().worker_index(w->name().c_str());
	WareInstance * const item = w->fetch_carried_item(g); // rescue an item

	//  We remove carriers, but we keep other workers around.
	if (dynamic_cast<Carrier const *>(w)) {
		w->remove(g);
		w = 0;
	} else {
		sort_worker_in(g, w->name(), w);
		w->set_location(0); // No more in a economy
		w->reset_tasks(g);
		w->start_task_idle(g, 0, -1); // bind the worker into this house, hide him on the map
	}

	m_supply->add_workers(index, 1);

	if (item)
		incorporate_item(g, item);
}


/*
 * Sort the worker into the right position in m_incorporated_workers
 */
void Warehouse::sort_worker_in(Editor_Game_Base* g, std::string workername, Worker* w) {
	//  We insert this worker, but to keep some consistency in ordering, we tell
	//  him where to insert.

	std::vector<Object_Ptr>::iterator i = m_incorporated_workers.begin();

	while
		(i!= m_incorporated_workers.end()
		 &&
		 workername <= dynamic_cast<Worker &>(*i->get(g)).name())
		++i;
	if (i == m_incorporated_workers.end()) {
		m_incorporated_workers.insert(i, w);
		return;
	}

	while
		(i != m_incorporated_workers.end()
		 &&
		 w->get_serial() <= dynamic_cast<Worker &>(*i->get(g)).get_serial())
		++i;

	m_incorporated_workers.insert(i, w);
}

/*
===============
Create an instance of a ware and make sure it gets carried out of the warehouse.
===============
*/
WareInstance & Warehouse::launch_item(Game * game, Ware_Index const ware) {
	// Create the item
	WareInstance & item =
		*new WareInstance(ware, owner().tribe().get_ware_descr(ware));
	item.init(game);

	m_supply->remove_wares(ware, 1);

	do_launch_item(game, item);

	return item;
}


/*
===============
Get a carrier to actually move this item out of the warehouse.
===============
*/
void Warehouse::do_launch_item(Game * game, WareInstance & item)
{
	// Create a carrier
	const Tribe_Descr & tribe = owner().tribe();
	Ware_Index const carrierid = tribe.worker_index("carrier");
	const Worker_Descr & workerdescr = *tribe.get_worker_descr(carrierid);

	Worker & worker = workerdescr.create(*game, owner(), *this, m_position);

	// Yup, this is cheating.
	if (m_supply->stock_workers(carrierid))
		m_supply->remove_workers(carrierid, 1);

	// Setup the carrier
	worker.start_task_dropoff(*game, item);
}


/*
===============
Warehouse::incorporate_item

Swallow the item, adding it to out inventory.
===============
*/
void Warehouse::incorporate_item(Game* g, WareInstance* item)
{
	m_supply->add_wares(item->descr_index(), 1);
	return item->destroy(g);
}


/*
===============
Warehouse::idle_request_cb [static]

Called when a transfer for one of the idle Requests completes.
===============
*/
void Warehouse::idle_request_cb
	(Game     *       game,
	 Request  *,
	 Ware_Index const ware,
	 Worker   * const w,
	 void     * const data)
{
	if (w)
		w->schedule_incorporate(game);
	else
		static_cast<Warehouse *>(data)->m_supply->add_wares(ware, 1);
}


/*
===============
Warehouse_Descr::create_object
===============
*/
Building * Warehouse_Descr::create_object() const
{return new Warehouse(*this);}


/*
===============
Warehouse::can_create_worker
===============
*/
bool Warehouse::can_create_worker(Game *, Ware_Index const worker) const {
	if (not (worker < m_supply->get_workers().get_nrwareids()))
		throw wexception
			("Worker type %d doesn't exists! (max is %d)",
			 worker.value(), m_supply->get_workers().get_nrwareids().value());

	const Tribe_Descr & tribe = owner().tribe();
	if (Worker_Descr const * const w_desc = tribe.get_worker_descr(worker)) {
		// First watch if we can build it
		if (!w_desc->get_buildable())
			return false;

		// Now see if we have the resources
		const Worker_Descr::BuildCost & buildcost = w_desc->get_buildcost();
		const Worker_Descr::BuildCost::const_iterator buildcost_end =
			buildcost.end();
		for
			(Worker_Descr::BuildCost::const_iterator it = buildcost.begin();
			 it != buildcost.end();
			 ++it)
		{
			const char * input_name = it->name.c_str();
			if (Ware_Index id_w = tribe.ware_index(input_name)) {
				if (m_supply->stock_wares(id_w) < it->amount)
					return false;
			} else if ((id_w = tribe.worker_index(input_name))) {
				if (m_supply->stock_workers(id_w) < it->amount)
					return false;
			} else
				throw wexception
					("worker type %s needs \"%s\" to be built but that is neither "
					 "a ware type nor a worker type defined in the tribe %s",
					 w_desc->descname().c_str(), input_name, tribe.name().c_str());
		}
		return true;
	}
	else
		throw wexception
			("Can not create worker of desired type : %d", worker.value());
}

/*
=============
Warehouse::create_worker
+=============
*/
void Warehouse::create_worker(Game * game, Ware_Index const worker) {
	if (!can_create_worker (game, worker))
		throw wexception
			("Warehouse::create_worker WE CANN'T CREATE A %u WORKER",
			 worker.value());

	const Tribe_Descr & tribe = owner().tribe();
	if (const Worker_Descr * const w_desc = tribe.get_worker_descr(worker)) {
		const Worker_Descr::BuildCost & buildcost = w_desc->get_buildcost();
		const Worker_Descr::BuildCost::const_iterator buildcost_end =
			buildcost.end();
		for
			(Worker_Descr::BuildCost::const_iterator it = buildcost.begin();
			 it != buildcost.end();
			 ++it)
		{
			const char & material_name = *it->name.c_str();
			if (Ware_Index const id_ware   = tribe.ware_index  (&material_name))
				remove_wares  (id_ware,   it->amount);
			else if
				(Ware_Index const id_worker = tribe.worker_index(&material_name))
				remove_workers(id_worker, it->amount);
			else
				throw wexception
					("tribe %s does not define worker or ware type \"%s\"",
					 tribe.name().c_str(), &material_name);
		}

		incorporate_worker
			(game, &w_desc->create(*game, owner(), *this, m_position));

	} else
		throw wexception
			("Can not create worker of desired type : %d", worker.value());
}


bool Warehouse::canAttack()
{
	return get_conquers() > 0;
}

void Warehouse::aggressor(Soldier & enemy)
{
	if (!get_conquers())
		return;

	Game & game = dynamic_cast<Game &>(owner().egbase());
	Map  & map  = game.map();
	if
		(enemy.get_owner() == &owner() ||
		 enemy.getBattle() ||
		 get_conquers()
		 <=
		 map.calc_distance(enemy.get_position(), get_position()))
		return;

	if
		(game.map().find_bobs
		 	(Area<FCoords>(map.get_fcoords(get_base_flag()->get_position()), 2),
		 	 0,
		 	 FindBobEnemySoldier(&owner())))
		return;

	Ware_Index const soldier_index = owner().tribe().worker_index("soldier");
	Requirements noreq;

	if (!count_workers(&game, soldier_index, noreq))
		return;

	Soldier & defender =
		dynamic_cast<Soldier &>(*launch_worker(&game, soldier_index, noreq));
	defender.startTaskDefense(&game, false);
	new Battle(game, defender, enemy);
}

bool Warehouse::attack(Soldier & enemy)
{
	Game & game = dynamic_cast<Game &>(owner().egbase());
	Ware_Index soldier_index = owner().tribe().worker_index("soldier");
	Requirements noreq;

	if (count_workers(&game, soldier_index, noreq)) {
		Soldier & defender =
			dynamic_cast<Soldier &>(*launch_worker(&game, soldier_index, noreq));
		defender.startTaskDefense(&game, true);
		new Battle(game, defender, enemy);
		return true;
	}

	set_defeating_player(enemy.get_owner()->get_player_number());
	schedule_destroy(&game);
	return false;
}

};
