/***************************************************************************
 *  file: interp.c , Command interpreter module.      Part of DIKUMUD      *
 *  Usage: Procedures interpreting user command                            *
 *  Copyright (C) 1990, 1991 - see 'license.doc' for complete information. *
 *                                                                         *
 *  Copyright (C) 1992, 1993 Michael Chastain, Michael Quan, Mitchell Tse  *
 *  Performance optimization and bug fixes by MERC Industries.             *
 *  You can use our stuff in any way you like whatsoever so long as this   *
 *  copyright notice remains intact.  If you like it please drop a line    *
 *  to mec@garnet.berkeley.edu.                                            *
 *                                                                         *
 *  This is free software and you are benefitting.  We hope that you       *
 *  share your changes too.  What goes around, comes around.               *
 ***************************************************************************/

#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdio.h>
#include <memory.h>

#include "structs.h"
#include "mob.h"
#include "obj.h"
#include "interp.h"
#include "db.h"
#include "utils.h"
#include "limits.h"

extern bool check_social( struct char_data *ch, char *pcomm,
    int length, char *arg );

extern struct index_data *mob_index;
extern struct index_data *obj_index;
extern struct room_data *world;

struct command_info
{
    char *command_name;             /* Name of this command             */
    DO_FUN *command_pointer;        /* Function that does it            */
    byte minimum_position;          /* Position commander must be in    */
    byte minimum_level;             /* Minimum level needed             */
    ubyte command_number;           /* Passed to function as argument   */
};

/*
 * Note that command number is always non-zero for user commands.
 * Various spec_proc.c functions impose ICKY requirements.
 * Someday I'll NUKE the arg and pass the command name instead.
 *
 * Spec_procs that need fixing:
 *	shop_keeper
 */
struct command_info cmd_info[] =
{
    /*
     * Common movement commands.
     */
    { "north",      do_move,        POSITION_STANDING,  0,  1 },
    { "east",       do_move,        POSITION_STANDING,  0,  2 },
    { "south",      do_move,        POSITION_STANDING,  0,  3 },
    { "west",       do_move,        POSITION_STANDING,  0,  4 },
    { "up",         do_move,        POSITION_STANDING,  0,  5 },
    { "down",       do_move,        POSITION_STANDING,  0,  6 },

    /*
     * Common other commands.
     * Placed here so one and two letter abbreviations work.
     */
    { "cast",       do_cast,        POSITION_SITTING,   0,  9 },
    { "exits",      do_exits,       POSITION_RESTING,   0,  9 },
    { "get",        do_get,         POSITION_RESTING,   0,  9 },
    { "inventory",  do_inventory,   POSITION_DEAD,      0,  9 },
    { "kill",       do_kill,        POSITION_FIGHTING,  0,  9 },
    { "look",       do_look,        POSITION_RESTING,   0,  9 },
    { "order",      do_order,       POSITION_RESTING,   0,  9 },
    { "rest",       do_rest,        POSITION_RESTING,   0,  9 },
    { "stand",      do_stand,       POSITION_RESTING,   0,  9 },
    { "tell",       do_tell,        POSITION_RESTING,   0,  9 },
    { "wield",      do_wield,       POSITION_RESTING,   0,  9 },
    { "wizhelp",    do_wizhelp,     POSITION_DEAD,      31, 9 },

    /*
     * Informational commands.
     */
    { "brief",      do_brief,       POSITION_DEAD,      0,  9 },
    { "bug",        do_bug,         POSITION_DEAD,      0,  9 },
    { "compact",    do_compact,     POSITION_DEAD,      0,  9 },
    { "credits",    do_credits,     POSITION_DEAD,      0,  9 },
    { "equipment",  do_equipment,   POSITION_DEAD,      0,  9 },
    { "help",       do_help,        POSITION_DEAD,      0,  9 },
    { "idea",       do_idea,        POSITION_DEAD,      0,  9 },
    { "info",       do_info,        POSITION_DEAD,      0,  9 },
/*  { "inventory",  do_inventory,   POSITION_DEAD,      0,  9 },    */
    { "levels",     do_levels,      POSITION_DEAD,      0,  9 },
    { "news",       do_news,        POSITION_DEAD,      0,  9 },
    { "score",      do_score,       POSITION_DEAD,      0,  9 },
    { "story",      do_story,       POSITION_DEAD,      0,  9 },
    { "tick",       do_tick,        POSITION_DEAD,      0,  9 },
    { "time",       do_time,        POSITION_DEAD,      0,  9 },
    { "title",      do_title,       POSITION_DEAD,      0,  9 },
    { "typo",       do_typo,        POSITION_DEAD,      0,  9 },
    { "weather",    do_weather,     POSITION_DEAD,      0,  9 },
    { "who",        do_who,         POSITION_DEAD,      0,  9 },
    { "wizlist",    do_wizlist,     POSITION_DEAD,      0,  9 },

    /*
     * Communication commands.
     */
    { "ask",        do_ask,         POSITION_RESTING,   0,  9 },
    { "emote",      do_emote,       POSITION_RESTING,   0,  9 },
    { ",",          do_emote,       POSITION_RESTING,   0,  9 },    
    { "gtell",      do_grouptell,   POSITION_DEAD,      0,  9 },
    { ";",          do_grouptell,   POSITION_DEAD,      0,  9 },
    { "insult",     do_insult,      POSITION_RESTING,   0,  9 },
/*  { "order",      do_order,       POSITION_RESTING,   0,  9 },    */
    { "pose",       do_pose,        POSITION_RESTING,   0,  9 },
    { "report",     do_report,      POSITION_DEAD,      0,  9 },
    { "say",        do_say,         POSITION_RESTING,   0,  9 },
    { "'",          do_say,         POSITION_RESTING,   0,  9 },
    { "shout",      do_shout,       POSITION_RESTING,   0,  9 },
/*  { "tell",       do_tell,        POSITION_RESTING,   0,  9 },    */
    { "whisper",    do_whisper,     POSITION_RESTING,   0,  9 },

    /*
     * Object manipulation commands.
     */
    { "close",      do_close,       POSITION_RESTING,   0,  9 },
    { "drink",      do_drink,       POSITION_RESTING,   0,  9 },
    { "drop",       do_drop,        POSITION_RESTING,   0,  9 },
    { "eat",        do_eat,         POSITION_RESTING,   0,  9 },
    { "fill",       do_fill,        POSITION_RESTING,   0,  9 },
/*  { "get",        do_get,         POSITION_RESTING,   0,  9 },    */
    { "give",       do_give,        POSITION_RESTING,   0,  9 },
    { "grab",       do_grab,        POSITION_RESTING,   0,  9 },
    { "hold",       do_grab,        POSITION_RESTING,   0,  9 },
    { "lock",       do_lock,        POSITION_RESTING,   0,  9 },
    { "open",       do_open,        POSITION_RESTING,   0,  9 },
    { "pour",       do_pour,        POSITION_RESTING,   0,  9 },
    { "put",        do_put,         POSITION_RESTING,   0,  9 },
    { "quaff",      do_quaff,       POSITION_RESTING,   0,  9 },
    { "read",       do_read,        POSITION_RESTING,   0,  9 },
    { "recite",     do_recite,      POSITION_RESTING,   0,  9 },
    { "remove",     do_remove,      POSITION_RESTING,   0,  9 },
    { "sip",        do_sip,         POSITION_RESTING,   0,  9 },
    { "take",       do_get,         POSITION_RESTING,   0,  9 },
    { "junk",       do_tap,         POSITION_RESTING,   0,  9 },
    { "sacrifice",  do_tap,         POSITION_RESTING,   0,  9 },
    { "tap",        do_tap,         POSITION_RESTING,   0,  9 },
    { "taste",      do_taste,       POSITION_RESTING,   0,  9 },
    { "unlock",     do_unlock,      POSITION_RESTING,   0,  9 },
    { "use",        do_use,         POSITION_RESTING,   0,  9 },
    { "wear",       do_wear,        POSITION_RESTING,   0,  9 },
/*  { "wield",      do_wield,       POSITION_RESTING,   0,  9 },    */

    /*
     * Combat commands.
     */
    { "bash",       do_bash,        POSITION_FIGHTING,  0,  9 },
    { "disarm",     do_disarm,      POSITION_FIGHTING,  0,  9 },
    { "flee",       do_flee,        POSITION_FIGHTING,  0,  9 },
    { "hit",        do_hit,         POSITION_FIGHTING,  0,  9 },
    { "kick",       do_kick,        POSITION_FIGHTING,  0,  9 },
/*  { "kill",       do_kill,        POSITION_FIGHTING,  0,  9 },    */
    { "murder",     do_murder,      POSITION_FIGHTING,  5,  9 },
    { "rescue",     do_rescue,      POSITION_FIGHTING,  0,  9 },
    { "trip",       do_trip,        POSITION_FIGHTING,  0,  9 },

    /*
     * Position commands.
     */
/*  { "rest",       do_rest,        POSITION_RESTING,   0,  9 },    */
    { "sit",        do_sit,         POSITION_RESTING,   0,  9 },
    { "sleep",      do_sleep,       POSITION_SLEEPING,  0,  9 },
/*  { "stand",      do_stand,       POSITION_RESTING,   0,  9 },    */
    { "wake",       do_wake,        POSITION_SLEEPING,  0,  9 },

    /*
     * Miscellaneous commands.
     */
    { "backstab",   do_backstab,    POSITION_STANDING,  0,  9 },
    { "bs",         do_backstab,    POSITION_STANDING,  0,  9 },
/*  { "cast",       do_cast,        POSITION_SITTING,   0,  9 },    */
    { "consider",   do_consider,    POSITION_RESTING,   0,  9 },
    { "enter",      do_enter,       POSITION_STANDING,  0,  9 },
    { "examine",    do_examine,     POSITION_RESTING,   0,  9 },
/*  { "exits",      do_exits,       POSITION_RESTING,   0,  9 },    */
    { "follow",     do_follow,      POSITION_RESTING,   0,  9 },
    { "group",      do_group,       POSITION_RESTING,   0,  9 },
    { "hide",       do_hide,        POSITION_RESTING,   0,  9 },
    { "leave",      do_leave,       POSITION_STANDING,  0,  9 },
/*  { "look",       do_look,        POSITION_RESTING,   0,  9 },    */
    { "pick",       do_pick,        POSITION_STANDING,  0,  9 },
    { "qui",        do_qui,         POSITION_DEAD,      0,  9 },
    { "quit",       do_quit,        POSITION_DEAD,      0,  9 },
    { "recall",     do_recall,      POSITION_FIGHTING,  0,  9 },
    { "/",          do_recall,      POSITION_FIGHTING,  0,  9 },
    { "return",     do_return,      POSITION_DEAD,      0,  9 },
    { "save",       do_save,        POSITION_DEAD,      0,  9 },
    { "sneak",      do_sneak,       POSITION_STANDING,  1,  9 },
    { "split",      do_split,       POSITION_RESTING,   0,  9 },
    { "steal",      do_steal,       POSITION_STANDING,  1,  9 },
    { "where",      do_where,       POSITION_RESTING,   0,  9 },
    { "wimpy",      do_wimpy,       POSITION_DEAD,      0,  9 },
    { "write",      do_write,       POSITION_STANDING,  0,  9 },

    /*
     * Special procedure commands.
     */
    { "buy",        do_not_here,    POSITION_STANDING,  0,  56 },
    { "sell",       do_not_here,    POSITION_STANDING,  0,  57 },
    { "value",      do_not_here,    POSITION_STANDING,  0,  58 },
    { "list",       do_not_here,    POSITION_STANDING,  0,  59 },
    { "practice",   do_practice,    POSITION_RESTING,   1,  164 },
    { "practise",   do_practice,    POSITION_RESTING,   1,  164 },
    { "train",      do_not_here,    POSITION_RESTING,   1,  165 },

    /*
     * Immortal commands.
     */
    { "advance",    do_advance,     POSITION_DEAD,      35, 9 },

    { "allow",      do_allow,       POSITION_DEAD,      34, 9 },
    { "ban",        do_ban,         POSITION_DEAD,      34, 9 },
    { "disconnect", do_disconnect,  POSITION_DEAD,      34, 9 },
    { "freeze",     do_freeze,      POSITION_DEAD,      34, 9 },
    { "log",        do_log,         POSITION_DEAD,      34, 9 },
    { "purge",      do_purge,       POSITION_DEAD,      34, 9 },
    { "reroll",     do_reroll,      POSITION_DEAD,      34, 9 },
    { "reset",      do_reroll,      POSITION_DEAD,      34, 9 },
    { "set",        do_set,         POSITION_DEAD,      34, 9 },
    { "shutdow",    do_shutdow,     POSITION_DEAD,      34, 9 },
    { "shutdown",   do_shutdown,    POSITION_DEAD,      34, 9 },
    { "sockets",    do_sockets,     POSITION_DEAD,      34, 9 },
    { "string",     do_string,      POSITION_DEAD,      34, 9 },
    { "wizlock",    do_wizlock,     POSITION_DEAD,      34, 9 },

    { "force",      do_force,       POSITION_DEAD,      33, 9 },
    { "load",       do_load,        POSITION_DEAD,      33, 9 },
    { "noemote",    do_noemote,     POSITION_DEAD,      33, 9 },
    { "nosocial",   do_noemote,     POSITION_DEAD,      33, 9 },
    { "noshout",    do_noshout,     POSITION_DEAD,      33, 9 },
    { "notell",     do_notell,      POSITION_DEAD,      33, 9 },
    { "pardon",     do_pardon,      POSITION_DEAD,      33, 9 },
    { "restore",    do_restore,     POSITION_DEAD,      33, 9 },
    { "teleport",   do_teleport,    POSITION_DEAD,      33, 9 },
    { "trans",      do_trans,       POSITION_DEAD,      33, 9 },

    { "at",         do_at,          POSITION_DEAD,      32, 9 },
    { "echo",       do_echo,        POSITION_DEAD,      32, 9 },
    { "goto",       do_goto,        POSITION_DEAD,      32, 9 },
    { "snoop",      do_snoop,       POSITION_DEAD,      32, 9 },
    { "stat",       do_stat,        POSITION_DEAD,      32, 9 },
    { "switch",     do_switch,      POSITION_DEAD,      32, 9 },
    { "invis",      do_wizinvis,    POSITION_DEAD,      32, 9 },
    { "wizinvis",   do_wizinvis,    POSITION_DEAD,      32, 9 },

    { "holylite",   do_holylite,    POSITION_DEAD,      31, 9 },
    { "immortal",   do_wiz,         POSITION_DEAD,      31, 9 },
    { ":",          do_wiz,         POSITION_DEAD,      31, 9 },
/*  { "wizhelp",    do_wizhelp,     POSITION_DEAD,      31, 9 },    */

    /*
     * End of list.
     */
    { "",           do_not_here,    POSITION_DEAD,      0,  9 }
};



char *fill[]=
{
    "in", "from", "with", "the", "on", "at", "to", "\n"
};



void command_interpreter( struct char_data *ch, char *pcomm )
{
    int look_at;
    int cmd;

    /*
     * No hiding.
     */
    REMOVE_BIT( ch->specials.affected_by, AFF_HIDE );

    /*
     * Log players.
     */
    if ( !IS_NPC(ch) && IS_SET(ch->specials.act, PLR_LOG) )
    {
	sprintf( log_buf, "Log %s: %s", GET_NAME(ch), pcomm );
	log( log_buf );
    }

    /*
     * Implement freeze command.
     */
    if ( !IS_NPC(ch) && IS_SET(ch->specials.act, PLR_FREEZE) )
    {
	send_to_char( "You're totally frozen!\n\r", ch );
	return;
    }

    /*
     * Strip initial spaces and parse command word.
     * Translate to lower case.
     */
    while ( *pcomm == ' ' )
	pcomm++;
    
    for ( look_at = 0; pcomm[look_at] > ' '; look_at++ )
	pcomm[look_at]  = LOWER(pcomm[look_at]);

    if ( look_at == 0 )
	return;
    
    /*
     * Look for command in command table.
     */
    for ( cmd = 0; cmd < sizeof(cmd_info)/sizeof(cmd_info[0]); cmd++ )
    {
	if ( GET_LEVEL(ch) < cmd_info[cmd].minimum_level )
	    continue;
	if ( cmd_info[cmd].command_pointer == NULL )
	    continue;
	if ( memcmp( pcomm, cmd_info[cmd].command_name, look_at ) == 0 )
	    goto LCmdFound;
    }

    /*
     * Look for command in socials table.
     */
    if ( check_social( ch, pcomm, look_at, &pcomm[look_at] ) )
	return;

    /*
     * Unknown command (or char too low level).
     */
    send_to_char( "Huh?\n\r", ch );
    return;

 LCmdFound:
    /*
     * Character not in position for command?
     */
    if ( GET_POS(ch) < cmd_info[cmd].minimum_position )
    {
	switch( GET_POS(ch) )
	{
	case POSITION_DEAD:
	    send_to_char( "Lie still; you are DEAD.\n\r", ch );
	    break;
	case POSITION_INCAP:
	case POSITION_MORTALLYW:
	    send_to_char( "You are hurt far too bad for that.\n\r", ch );
	    break;
	case POSITION_STUNNED:
	    send_to_char( "You are too stunned to do that.\n\r", ch );
	    break;
	case POSITION_SLEEPING:
	    send_to_char( "In your dreams, or what?\n\r", ch );
	    break;
	case POSITION_RESTING:
	    send_to_char( "Nah... You feel too relaxed...\n\r", ch);
	    break;
	case POSITION_SITTING:
	    send_to_char( "Maybe you should stand up first?\n\r", ch);
	    break;
	case POSITION_FIGHTING:
	    send_to_char( "No way!  You are still fighting!\n\r", ch);
	    break;
	}
	return;
    }

    /*
     * We're gonna execute it.
     * First look for usable special procedure.
     */
    if ( special( ch, cmd_info[cmd].command_number, &pcomm[look_at] ) )
	return;
      
    /*
     * Normal dispatch.
     */
    (*cmd_info[cmd].command_pointer)
	(ch, &pcomm[look_at], cmd_info[cmd].command_number);

    /*
     * This call is here to prevent gcc from tail-chaining the
     * previous call, which screws up the debugger call stack.
     * -- Furey
     */
    number( 0, 0 );
    return;
}



int search_block(char *arg, char **list, bool exact)
{
    register int i,l;

    /* Make into lower case, and get length of string */
    for(l=0; *(arg+l); l++)
	*(arg+l)=LOWER(*(arg+l));

    if (exact) {
	for(i=0; **(list+i) != '\n'; i++)
	    if (!strcmp(arg, *(list+i)))
		return(i);
    } else {
	if (!l)
	    l=1; /* Avoid "" to match the first available string */
	for(i=0; **(list+i) != '\n'; i++)
	    if (!strncmp(arg, *(list+i), l))
		return(i);
    }

    return(-1);
}


int old_search_block(char *argument,int begin,int length,char **list,int mode)
{
    int guess, found, search;
	
    /* If the word contain 0 letters, then a match is already found */
    found = (length < 1);

    guess = 0;

    /* Search for a match */

    if(mode)
    while ( !found && *(list[guess]) != '\n' )
    {
	found = (length==strlen(list[guess]));
	for ( search = 0; search < length && found; search++ )
	    found=(*(argument+begin+search)== *(list[guess]+search));
	guess++;
    } else {
	while ( !found && *(list[guess]) != '\n' ) {
	    found=1;
	    for(search=0;( search < length && found );search++)
		found=(*(argument+begin+search)== *(list[guess]+search));
	    guess++;
	}
    }

    return ( found ? guess : -1 ); 
}



void argument_interpreter(char *argument,char *first_arg,char *second_arg )
{
    int look_at, found, begin;

    found = begin = 0;

    do
    {
	/* Find first non blank */
	for ( ;*(argument + begin ) == ' ' ; begin++);

	/* Find length of first word */
	for ( look_at=0; *(argument+begin+look_at)> ' ' ; look_at++)

		/* Make all letters lower case,
		   and copy them to first_arg */
		*(first_arg + look_at) =
		LOWER(*(argument + begin + look_at));

	*(first_arg + look_at)='\0';
	begin += look_at;

    }
    while( fill_word(first_arg));

    do
    {
	/* Find first non blank */
	for ( ;*(argument + begin ) == ' ' ; begin++);

	/* Find length of first word */
	for ( look_at=0; *(argument+begin+look_at)> ' ' ; look_at++)

		/* Make all letters lower case,
		   and copy them to second_arg */
		*(second_arg + look_at) =
		LOWER(*(argument + begin + look_at));

	*(second_arg + look_at)='\0';
	begin += look_at;
    }
    while( fill_word(second_arg));
}



int is_number(char *str)
{
    int look_at;

    if(*str=='\0')
	return(0);

    for(look_at=0;*(str+look_at) != '\0';look_at++)
	if((*(str+look_at)<'0')||(*(str+look_at)>'9'))
	    return(0);
    return(1);
}


/* find the first sub-argument of a string, return pointer to first char in
   primary argument, following the sub-arg                      */
char *one_argument(char *argument, char *first_arg )
{
    int found, begin, look_at;

	found = begin = 0;

	do
	{
		/* Find first non blank */
		for ( ;isspace(*(argument + begin)); begin++);

		/* Find length of first word */
		for (look_at=0; *(argument+begin+look_at) > ' ' ; look_at++)

			/* Make all letters lower case,
			   and copy them to first_arg */
			*(first_arg + look_at) =
			LOWER(*(argument + begin + look_at));

		*(first_arg + look_at)='\0';
	begin += look_at;
    }
	while (fill_word(first_arg));

    return(argument+begin);
}
    


int fill_word(char *argument)
{
    return ( search_block(argument,fill,TRUE) >= 0);
}



/* determine if a given string is an abbreviation of another */
int is_abbrev(char *arg1, char *arg2)
{
    if (!*arg1)
       return(0);

    for (; *arg1; arg1++, arg2++)
       if (LOWER(*arg1) != LOWER(*arg2))
	  return(0);

    return(1);
}




/* return first 'word' plus trailing substring of input string */
void half_chop(char *string, char *arg1, char *arg2)
{
    for (; isspace(*string); string++);

    for (; !isspace(*arg1 = *string) && *string; string++, arg1++);

    *arg1 = '\0';

    for (; isspace(*string); string++);

    for (; ( *arg2 = *string ) != '\0'; string++, arg2++)
	;
}



int special(struct char_data *ch, int cmd, char *arg)
{
    register struct obj_data *i;
    register struct char_data *k;
    int j;

    /* special in room? */
    if (world[ch->in_room].funct)
       if ((*world[ch->in_room].funct)(ch, cmd, arg))
	  return(1);

    /* special in equipment list? */
    for (j = 0; j <= (MAX_WEAR - 1); j++)
       if (ch->equipment[j] && ch->equipment[j]->item_number>=0)
	  if (obj_index[ch->equipment[j]->item_number].func)
	     if ((*obj_index[ch->equipment[j]->item_number].func)
		(ch, cmd, arg))
		return(1);

    /* special in inventory? */
    for (i = ch->carrying; i; i = i->next_content)
	if (i->item_number>=0)
	    if (obj_index[i->item_number].func)
	   if ((*obj_index[i->item_number].func)(ch, cmd, arg))
	      return(1);

    /* special in mobile present? */
    for (k = world[ch->in_room].people; k; k = k->next_in_room)
       if ( IS_MOB(k) )
	  if (mob_index[k->nr].func)
	     if ((*mob_index[k->nr].func)(ch, cmd, arg))
		return(1);

    /* special in object present? */
    for (i = world[ch->in_room].contents; i; i = i->next_content)
       if (i->item_number>=0)
	  if (obj_index[i->item_number].func)
	     if ((*obj_index[i->item_number].func)(ch, cmd, arg))
		return(1);


    return(0);
}



void do_wizhelp(struct char_data *ch, char *argument, int cmd_arg)
{
    char buf[MAX_STRING_LENGTH];
    int cmd;
    int no;

    if (IS_NPC(ch))
	return;

    send_to_char(
	"The following privileged comands are available:\n\r\n\r", ch);

    buf[0] = '\0';
    for ( cmd = 0, no = 0; cmd_info[cmd].command_name[0] != '\0'; cmd++ )
    {
	if ( cmd_info[cmd].minimum_level <= 30 )
	    continue;
	if ( cmd_info[cmd].minimum_level > GET_LEVEL(ch) )
	    continue;

	sprintf( buf + strlen(buf), "%-10s", cmd_info[cmd].command_name );
	if ( no % 7 == 0 )
	    strcat(buf, "\n\r");
	no++;
    }

    strcat(buf, "\n\r");
    page_string(ch->desc, buf, 1);
}