/*
....[@@@..[@@@..............[@.................. MUD++ is a written from
....[@..[@..[@..[@..[@..[@@@@@....[@......[@.... scratch multi-user swords and
....[@..[@..[@..[@..[@..[@..[@..[@@@@@..[@@@@@.. sorcery game written in C++.
....[@......[@..[@..[@..[@..[@....[@......[@.... This server is an ongoing
....[@......[@..[@@@@@..[@@@@@.................. development project.  All 
................................................ contributions are welcome. 
....Copyright(C).1995.Melvin.Smith.............. Enjoy. 
------------------------------------------------------------------------------
Melvin Smith (aka Fusion)         msmith@falcon.mercer.peachnet.edu 
MUD++ development mailing list    mudpp-list@spice.com
------------------------------------------------------------------------------
area.cc
*/

#include "io.h"
#include "string.h"
#include "llist.h"
#include "indexable.h"
#include "room.h"
#include "area.h"
#include "pc.h"
#include "shop.h"

extern LList<PC> pcs;
extern LList<NPC> npcs;
extern LList<Area> areas;

extern IndexList<Object> objIndex;
extern IndexList<NPC> npcIndex;
extern IndexList<Room> roomIndex;


void Area::setFile( const String & x )
{
	file = x;
}


const String & Area::getFile()
{
	return file;
}


int Area::reload()
{
	// This leaves invalid pointers in other areas so it is imperative
	// that a global relink be done immediately following an area reload
	roomIndex.clr();
	npcIndex.clr();
	objIndex.clr();

	rmDirtyBit();
	return readFrom();
}


// will change to readProtoFrom
/*
int Area::readFrom( const char * str )
{
	InFile in( str );

	if( !in )
		return -1;

	return readFrom( in );
}
*/

// will change to readProtoFrom

int Area::readFrom( InFile &in )
{  
	char   buf[BUF];
	Index  index;

	// Read area header
	for( ; ; ) 
	{
		in.getword( buf );
		switch( *buf )
		{
			default : continue;
			case '{': // nest++; unused for now
				continue;
			case '}': // nest--;
				break;
			case 'B': if( !str_cmp( buf, "Builder" ) )
				builder = in.getstring( buf );
				continue;
			case 'N': if( !str_cmp( buf, "Name" ) )
				setName( in.getstring( buf ) );
				continue;
			case 'R': if( !str_cmp( buf, "RepopMes" ) )
				setRepopMessg( in.getstring( buf ) );
				else if( !str_cmp( buf, "RepopTime" ) )
				repop_time = in.getnum();
				continue;
			case 'S':
				if( !str_cmp( buf, "Security" ) )
				{
					security = in.getnum();
					continue;
				}
				continue;
			case 'V': //if( !str_cmp( buf, "Version" ) ) 
			//version = in.getnum();
				continue;
		} 
		break;
	}

	while(in)
	{
		in.getword( buf );

		if( !in )
			break;

		if( *buf == 'R' ) // Room
		{
			index = in.getword( buf );
			Room *newRoom = new Room( this, index.getKey() );  // This = Area *

			// will change to readProtoFrom
			newRoom->readFrom( in );
			roomIndex.addIndex( index, newRoom );
		}
		else if( *buf == 'N' ) // NPC
		{
			NPC *newNPC = new NPC;
			index = in.getword( buf );

			// will change to readProtoFrom
			newNPC->readFrom( in );
			npcIndex.addIndex( index, newNPC );
		}
		else if( *buf == 'S' ) // ShopKeeper
		{
			NPC *newSK = new ShopKeeper;
			index =in.getword( buf );

			// will change to readProtoFrom
			newSK->readFrom( in );
			npcIndex.addIndex( index, newSK );
		}
		else if( *buf == 'O' ) // Object
		{
			Object *newObj = new Object;
			index = in.getword( buf );
			newObj->readProtoFrom( in );
			objIndex.addIndex( index, newObj );
		}
		else if( *buf == '#' )
			return 0;
		else
			return -1;
	}
	return 0;
}


// will change to writeProtoTo
/*
int Area::writeTo( const char * str )
{
	OutFile out( str );

	if( !out )
		return -1;

	return writeTo( out );
}
*/

// will change to writeProtoTo

int Area::writeTo( OutFile & out ) const
{
	out << "{\n"
		<< "Name " << name << "~\n"
		<< "Security " << security << "\n"
		<< "RepopMes " << repop_messg << "~\n"
		<< "RepopTime " << repop_time << "\n"
		<< "Builder " << builder << "~\n"
		<< "}\n\n";

	NPC *npc;

	out << "NPCS\n";

	npcIndex.reset();
	while( ( npc = npcIndex.peek() ) )
	{
		npcIndex.next();
		// Will change to writeProtoTo
		if( !npc->isShopKeeper() )
			npc->writeTo( out );
	}

	out << "#\n\n";

	out << "SHOPKEEPERS\n";

	npcIndex.reset();
	while( ( npc = npcIndex.peek() ) )
	{
		npcIndex.next();
		if( !npc->isShopKeeper() )
			continue;

		// Will change to writeProtoTo
		npc->writeTo( out );
	}

	out << "#\n\n";


	out << "ITEMS\n";

	objIndex.reset();
	Object *obj;
	
	while( ( obj = (Object *)objIndex.peek() ) )
	{
		objIndex.next();
		obj->writeProtoTo( out );
	}

	out << "#\n\n";

	out << "ROOMS\n";

	roomIndex.reset();
	Room *room;

	while( ( room = (Room *)roomIndex.peek() ) )
	{
		roomIndex.next();
		// Will change to writeProtoTo
		room->writeTo( out );
	}

	out << "#\n";

	return 1;
}


int Area::addRoom( Room * room )
{
	// Add checks later
	Index x;
	x.setKey( room->getKey() );
	roomIndex.addIndex( x, room );
	return 0;
}


const String & Area::getRepopMessg()
{
	return repop_messg;
}


void Area::setRepopMessg( const String & x )
{
	repop_messg = x;
}


void Area::setRepopTime( int t )
{
	if( t > 0 )
		repop_time = t;
}


int Area::getRepopTime()
{
	return repop_time;
}


void Area::repop()
{
	Room * room;

	roomIndex.reset();

	while( ( room = roomIndex.peek() ) )
	{
		roomIndex.next();

		room->repop();
	}

	outAllChar( repop_messg );
	timer = 0;
}


int Area::getSecurity() const
{
	return security;
}


const String & Area::getBuilder()
{
	return builder;
}


void Area::Builder( const String & strBuilder )
{
	if( builder == strBuilder )
		;
	//else builder = strBuilder;
}


Room *Area::getRoom( const Index & )
{
	return 0;
}


Object *Area::lookupObj( const Index & x )
{
	return objIndex.lookup( x );
}


Object *Area::createObj( const Index & x )
{
	Object *obj;
	obj = objIndex.lookup( x );

	if( !obj )
		return 0;
	// call the Object copy constructor to correctly clone the Object
	return new Object( *obj );
}


NPC *Area::lookupNPC( const Index & x )
{
	return npcIndex.lookup( x );
}


NPC *Area::createNPC( const Index & x )
{
	NPC *npc = npcIndex.lookup( x );

	if( npc )
		// call the NPC copy constructor to correctly clone the Object
		return new NPC( *npc ); 
	
	return 0;
}


// Inline these later
Room *Area::lookupRoom( const String & x )
{
	return roomIndex.lookup( x );
}

Room *Area::lookupRoom( const char * x )
{
	return roomIndex.lookup( x );
}

Room *Area::lookupRoom( const Index & x ) 
{
	return roomIndex.lookup( x.getKey() );
}


void Area::hardLink()
{
	Room *room;
	roomIndex.reset();

	while( ( room = roomIndex.peek() ) )
	{
		roomIndex.next();
		room->hardLink();
	}
}


void Area::outAllChar( const char * str )
{
	PC *pc;
	LList<PC> tList = pcs;

	tList.reset();
	while( ( pc = tList.peek() ) )
	{
		tList.next();

		// Add check for awake after position implemented

		if( !pc->isPlaying() )
			continue;
		else if( pc->inRoom()->getArea() != this )
			continue;
		else
		{
			pc->out( str );
		}
	}
}


void Area::outAllCharExcept( const char * str, PC * skip )
{
	PC *pc;
	LList<PC> tList = pcs;

	tList.reset();
	while( ( pc = tList.peek() ) )
	{
		tList.next();

		// Add check for awake after position implemented
		if( !pc->isPlaying() )
			continue;
		else if( pc->inRoom()->getArea() != this )
			continue;
		else if( pc == skip )
			continue;
		else
		{
			pc->out( str );
		}
	}
}


Area * lookupArea( const String & x )
{
	Area *area;
	LList<Area> tlist = areas;
	
	tlist.reset();
	while( ( area = tlist.peek() ) )
	{
		tlist.next();

		if( area->getKey() == x )
			return area;
	}

	return 0;	
}


Area * lookupArea( const char * x )
{
	return lookupArea( String( x ) );
}