/*
....[@@@..[@@@..............[@.................. 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
------------------------------------------------------------------------------
pc_info.cc
*/

#include "string.h"
#include "llist.h"
#include "indexable.h"
#include "room.h"
#include "server.h"
#include "area.h"
#include "help.h"
#include "screen.h"
#include "env.h"
#include "spell.h"
#include "pc.h"

#include "global.h"

void PC::do_list( String & )
{
	Char * ch;
	Object * obj;
	String str;

	// Should be able to have player shopkeepers automated too.

	in_room->chars.reset();
	while( ( ch = in_room->chars.peek() ) )
	{
		in_room->chars.next();

		if( ch == this )
			continue;

		if( ch->isShopKeeper() )
			break;
	}

	if( ! ch )
	{
		out( "No merchant is here.\n\r" );
		return;
	}

	str << "Goods for sale:\n";

	ch->inv.reset();
	while( ( obj = ch->inv.peek() ) )
	{
		ch->inv.next();

		if( obj->worn() )
			continue;

		str << "[ " << obj->getCost() << " ] " << obj->getShort() << endl;
	}

	out( str );
}


void PC::do_commands( String & )
{
	String str( BUF * 2 );
	int count=1;
	int i;

	str << "MUD++ player commands.\n\n\r";
	for( int ihash = 0; ihash < 27; ihash++ )
	{
		i = 0;
		while( cmdlist[ihash][i].commd )
		{
			str.sprintfAdd( "%11s", cmdlist[ihash][i].commd );

			if( !( count++ % 5 ) )
				str += "\n\r";

			i++;
		}
	}

	out( str );  
}


void PC::do_wizhelp( String & )
{
	String str( BUF * 2 );
	int count=1;
	int i;

	str << "MUD++ Wizard Commands.\n\n\r";
	for( int ihash = 0; ihash < 27; ihash++ )
	{
		i = 0;
		while( immcmdlist[ihash][i].commd )
		{
			if( authorized( immcmdlist[ihash][i].bit ) )
			{
				str.sprintfAdd( "%11s", immcmdlist[ihash][i].commd );
				if( !( count++ % 5 ) )
					str += "\n\r";
			}

			i++;
		}
	}

	out( str );  
}



void PC::do_help( String & arg )
{
	if( !arg )
	{
		// Simple help
		view( STANDARD_HELP );   
	}
	else
	{
		// Look through the help list for the keyword, if exists call
		// View( 'HELP_DIR+filename' )
		// View is very efficient at reading in files and helps can be
		// modified while the game is running.
		// Memory usage is also decreased.

		String fname;
		for( int i=0;help_list[i];i++ )
		{
			if( !help_list[i]->isName( arg ) )
				continue;

			if( help_list[i]->getLevel() > level )
				break; 

			fname << HELP_DIR << '/' << help_list[i]->fileName();      
			view( fname.chars() );
			return;
		}

		out("\n\rNo help on that.\n\r"); 
	}
}


void PC::do_levels( String & )
{
	String str( BUF * 2 );
	str << "Exp is per level, not cumalative.\n\r-===================-\n\r"; 
	for( int i = 1; i<=MAX_PC_LEVEL; i++ )
	{
		str.sprintf( " %2d %11d\n\r", i, exp_table[ i ] );
	}

	str += "\n\r";
	out( str );
}


void PC::do_prompt( String & arg )
{
    int len = arg.len() + 1;

	if( len > 80 )
	{
		out( "Prompt string too long. Must be < 80 characters.\n\r" );
		return;
	}

	if( prompt )
		delete [] prompt;
    
	prompt = arg.dup();

	out( "Prompt string set.\n\r" );
	putPrompt();
}


void PC::do_clear( String & )
{
	out( "\x1B[2J" );
}


void PC::do_areas( String & )
{
	String str( BUF * 2 );
	str += (const char*)"\n\rAreas\n\r----------------------\n\r";
	Area *pArea;
	areas.reset( );
	while( ( pArea = areas.peek( ) ) )
	{
		areas.next();
		str << "[" << pArea->getKey() << "] " << pArea->getName() << "\n\r";
	}

	out( str );
}


void PC::do_look( String & arg )
{
	Object *obj;
	Object *obj2;
	Char   *ch;
	String  str( BUF * 2 );
	String arg1;
	String arg2;

	arg.startArgs();
	arg1 = arg.getArg();
	arg2 = arg.getArg();

	if( !arg1 )
	{
		// No arg so do standard look
		str << "\n\r"
			<< BCYAN << in_room->getName() << NORMAL << "\n\r"
			<< CYAN << in_room->getDesc() << NORMAL << "\n\r";

		// show the items
		in_room->inv.reset();
		while( ( obj = in_room->inv.peek() ) )
		{
			in_room->inv.next();
			str << obj->getLong() << "\n\r";
		}

		in_room->chars.reset();
		while( ( ch = in_room->chars.peek() ) )
		{
			in_room->chars.next();
			if( ch == this )
				continue;

			str << ch->getLong() << "\n\r";
		}

		out( str );
		do_exits( "" );
		return;
	}
	else if( arg1 == "in" || arg1 == "inside" )
	{
		// look at contents of a container (arg2)
		if( !arg2 )
		{
			out( "Look inside what?\n\r" );
			return;
		}

		// Look for object in chars inventory first
		obj = getObjInv( arg2 );

		// If not then look in room.
		if( ! obj )
			obj = in_room->getObj( arg2 );

		if( !obj )
		{
			out( "Nothing by that name here.\n\r" );
			return;
		}	

		if( !obj->isContainer() )
		{
			out( "That isn't a container.\n\r" );
			return;
		}

		// replace resetInv/getNexObjInv with a const LList &
		// access member function and assign to a temp readonly list
		// for iterating.

		// Reset object inventory list pointer
		obj->resetInv();
	
		out( obj->getShort() );
		out( " contains:\n\r-------------\n\r" );

		if( !( obj2 = obj->getNextObjInv() ) )
		{
			out( "Nothing.\n\r" );
			return;
		}

		out( obj2->getShort() );
		out( "\n\r" );

		// Iterate
		while( ( obj2 = obj->getNextObjInv() ) )
		{
			out( obj2->getShort() );
			out( "\n\r" );
		}

		return;
	}

	// Find object/mob/player to look at
	ch = in_room->getChar( arg1 );
	if( ch )
	{
		look( ch );
		return;
	}

	// Look at an object 
	obj = in_room->getObj( arg1 );
	if( obj )
	{
		look( obj );
		return;
	} 

	// Extra description keywords
	String ed = in_room->getExtraDesc( arg1 );	
	if( ed )
	{
		out( ed );
		out( "\n\r" );
		return;
	}

	// Look in a direction
	int dir;
	
	if(	( dir = getDir( arg1[0] ) ) != DIR_UNDEFINED )
	{
		const Exit *ex = in_room->getExit( dir );
		if( !ex )
		{
			out( "Nothing to see in that direction.\n\r" );
			return;
		}

		if( ex->getDesc() )
			out( ex->getDesc() );
		else
			out( "You see nothing special in that direction." );
		out( "\n\r" );
		return;
	}

	out( "Look at what?\n\r" );
}



void PC::do_save( String & )
{
	String fname;

	fname << PLAYER_DIR << "/" << name[0] << "/" << name.asProper();

	if( writeTo( fname ) )
		out( "\n\rOk. Saved.\n\r" );
	else
		out( "\n\rError, not saved, report to immortal!!!\n\r" ); 
}


void PC::do_password( String & arg )
{
	String orig;
	String newp;

	arg.startArgs();

	orig	= arg.getArg();
	newp	= arg.getArg();

	if( !orig || !newp )	
	{
		out( "Usage:   password <old> <new>\n\r" );
		return;
	}

	orig.crypt( getName() );
	
	if( getPasswd()
		&&
		orig != getPasswd() )
	{
		out( "Old password doesn't match. Password not changed.\n\r" );
		return;
	}

	if( newp.hasChar( '~' ) )
	{
		out( "Illegal character '~'. Password not changed.\n\r" );
		return;
	}

	newp.crypt( getName() );
	password = newp;
	
	out( "Ok. Password changed.\n\r" );
}


void PC::do_score( String & )
{
	String str( 8192 );

	str.sprintf( "You are %s.\n\rLevel: %d\n\r", name.chars(), levels[classnow]);
	str.sprintfAdd( "Security: %d\n\r", getSecurity() ); 
  
	if( classnow != CLASS_WIZARD )
	{
		str.sprintfAdd( "You have %ld experience points.\n\r", exp );
		str.sprintfAdd( "You need %ld exp to level %d.\n\r",
					exp_table[levels[classnow]]-exp,
					levels[classnow]+1 );
	}

	str.sprintfAdd( "Str: %d  Int: %d  Wis: %d  Con: %d  Spd: %d\n\r",
							getStr(), getInt(), getWis(), getCon(), getSpd() );
	str.sprintfAdd( "%d(%d) hp   %d(%d) mana\n\r",
							getHp(), getMaxHp(), getMana(), getMaxMana() );
	str.sprintfAdd( "You are carrying %d items weighing %d stones.\n\r",
							getCarriedCount(), getCarriedWeight() );

	str.sprintfAdd( "You are have %ld gold coins.\n\r", getGold() );
	str.sprintfAdd( "Damroll: %d  Hitroll: %d  Armor: %d\n\r",
							getDamRoll(), getHitRoll(), getArmor() ); 

	str.sprintfAdd( "Flags: " );

	str += "\n\r";

	if( fighting )
	{
		str << "You are fighting " << fighting->getShort() << ".\n\r";
	}

	str << "Affected by:\n\r";

	Affect *paf;
	const Spell *spell;

	affects.reset();
	while( ( paf = affects.peek() ) )
	{
		affects.next();
		if( ( spell = paf->getSpell() ) )
			str.sprintfAdd( "Spell '%s' for %d hours.\n\r",
				spell->getName(), paf->getTimer() );
	}

	out( str );
}


void PC::do_time( String & )
{
	out( envir.getTime() );
}

void PC::do_weather( String & )
{
	out( envir.getWeather() );
}

void PC::do_who( String & )
{
	PC *ch;
	int count = 0;
	String str( BUF * 4 ); 
	LList<PC> tlist = pcs;
	tlist.reset();      // reset the list

	str += "                             [ PCs ]\n\r";
	str += " Level   Class     Name\n\r";
	str += "----------------------------------\n\r";
	while( ( ch = tlist.peek() ) )
	{ 
		count++;
		str.sprintfAdd( " [%3d] [%7s]  %s\n\r", ch->getLevel(),
					ch->className(), ch->getName().chars() );  
		tlist.next(); 
	}

	str += "----------------------------------\n\r";
	str.sprintfAdd( " Total - %d players\n\r", count );
	out( str );
}