/****************************************************************************
 * [S]imulated [M]edieval [A]dventure multi[U]ser [G]ame      |   \\._.//   *
 * -----------------------------------------------------------|   (0...0)   *
 * SMAUG 1.4 (C) 1994, 1995, 1996, 1998  by Derek Snider      |    ).:.(    *
 * -----------------------------------------------------------|    {o o}    *
 * SMAUG code team: Thoric, Altrag, Blodkai, Narn, Haus,      |   / ' ' \   *
 * Scryn, Rennard, Swordbearer, Gorog, Grishnakh, Nivek,      |~'~.VxvxV.~'~*
 * Tricops, Fireblade, Edmond, Conran                         |             *
 * ------------------------------------------------------------------------ *
 * Merc 2.1 Diku Mud improvments copyright (C) 1992, 1993 by Michael        *
 * Chastain, Michael Quan, and Mitchell Tse.                                *
 * Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer,          *
 * Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe.     *
 * ------------------------------------------------------------------------ *
 * 			Variable Handling Module (Thoric)                         *
 ****************************************************************************/

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "mud.h"

VARIABLE_DATA *make_variable( char type, int vnum, char *tag )
{
   VARIABLE_DATA *var;

   CREATE( var, VARIABLE_DATA, 1 );
   var->next = NULL;
   var->type = type;
   var->flags = 0;
   var->vnum = vnum;
   var->tag = str_dup( tag );
   var->c_time = current_time;
   var->m_time = current_time;
   var->r_time = 0;
   var->timer = 0;
   switch ( type )
   {
      case vtINT:
         var->data = NULL;
         break;
      case vtXBIT:
         CREATE( var->data, EXT_BV, 1 );
         break;
      case vtSTR:
         var->data = NULL;
   }
   return var;
}

void delete_vdata( VARIABLE_DATA * var )
{
   switch ( var->type )
   {
      case vtXBIT:
      case vtSTR:
         if( var->data )
            DISPOSE( var->data );
         break;
   }
}

void delete_variable( VARIABLE_DATA * var )
{
   delete_vdata( var );
   DISPOSE( var->tag );
   DISPOSE( var );
}

/*
 * Return the specified tag from a character
 */
VARIABLE_DATA *get_tag( CHAR_DATA * ch, char *tag, int vnum )
{
   VARIABLE_DATA *vd;

   for( vd = ch->variables; vd; vd = vd->next )
      if( ( !vnum || vnum == vd->vnum ) && !str_cmp( tag, vd->tag ) )
         return vd;
   return NULL;
}

/*
 * Remove the specified tag from a character
 */
bool remove_tag( CHAR_DATA * ch, char *tag, int vnum )
{
   VARIABLE_DATA *vd_next, *vd = ch->variables;

   if( !vd )
      return FALSE;

   if( ( !vnum || vnum == vd->vnum ) && !str_cmp( tag, vd->tag ) )
   {
      ch->variables = vd->next;
      delete_variable( vd );
      return TRUE;
   }

   for( ; vd && vd->next; vd = vd_next )
   {
      vd_next = vd->next;
      if( ( !vnum || vnum == vd_next->vnum ) && !str_cmp( tag, vd_next->tag ) )
      {
         vd->next = vd_next->next;
         delete_variable( vd_next );
         return TRUE;
      }
   }
   return FALSE;
}

/*
 * Tag a variable onto a character  Will replace if specified to do so,
 * otherwise if already exists, fail
 */
int tag_char( CHAR_DATA * ch, VARIABLE_DATA * var, int replace )
{
   VARIABLE_DATA *vd, *pvd;
   bool found = FALSE;

   pvd = vd = ch->variables;
   for( ; vd; vd = vd->next )
   {
      if( vd == var )   /* same variable -- leave it be */
      {
         var->m_time = current_time;
         return 0;
      }
      if( vd->vnum == var->vnum && !str_cmp( vd->tag, var->tag ) )
      {
         if( !replace )
            return -1;
         found = TRUE;
         break;
      }
      pvd = vd;
   }

   if( found )
   {
      var->m_time = current_time;
      var->c_time = vd->c_time;
      var->r_time = vd->r_time;
      var->next = vd->next;
      if( vd == ch->variables )
         ch->variables = var;
      else
         pvd->next = var;
      delete_variable( vd );
      return 0;
   }
   var->next = ch->variables;
   ch->variables = var;

   return 0;
}

bool is_valid_tag( const char *tagname )
{
   if( !isalpha( *tagname ) )
      return FALSE;
   for( ++tagname; *tagname; ++tagname )
      if( !isalnum( *tagname ) && *tagname != '_' )
         return FALSE;
   return TRUE;
}


/*
 *  "tag" is a text identifier to refer to the variable and can
 *  be suffixed with a colon and a mob vnum  ie:  questobj:1101
 *  vnum 0 is used to denote a global tag (local to the victim)
 *  otherwise tags are separated by vnum
 *
 *  mptag	<victim> <tag> [value]
 *  mprmtag	<victim> <tag>
 *  mpflag	<victim> <tag> <flag>
 *  mprmflag    <victim> <tag> <flag>
 *
 *  if istagged($n,tag) [== value]
 *  if isflagged($n,tag[,bit])
 */


/*
 * mptag <victim> <tag> [value]
 */
void do_mptag( CHAR_DATA * ch, char *argument )
{
   CHAR_DATA *victim;
   VARIABLE_DATA *vd;
   char *p;
   char arg1[MAX_INPUT_LENGTH];
   char arg2[MAX_INPUT_LENGTH];
   int vnum = 0, exp = 0;
   bool error = FALSE;

   if( ( !IS_NPC( ch ) && get_trust( ch ) < LEVEL_GREATER ) || IS_AFFECTED( ch, AFF_CHARM ) )
   {
      send_to_char( "Huh?\r\n", ch );
      return;
   }

   argument = one_argument( argument, arg1 );
   if( !str_cmp( arg1, "noexpire" ) )
   {
      exp = 0;
      argument = one_argument( argument, arg1 );
   }
   else if( !str_cmp( arg1, "timer" ) )
   {
      argument = one_argument( argument, arg1 );
      exp = atoi( arg1 );
      argument = one_argument( argument, arg1 );
   }
   else
      exp = ch->level * get_curr_int( ch );
   argument = one_argument( argument, arg2 );

   if( arg1[0] == '\0' || arg2[0] == '\0' )
   {
      send_to_char( "MPtag whom with what?\r\n", ch );
      return;
   }

   if( ( victim = get_char_room( ch, arg1 ) ) == NULL )
   {
      send_to_char( "They aren't here.\r\n", ch );
      return;
   }

   if( ( p = strchr( arg2, ':' ) ) != NULL )
   {
      *p++ = '\0';
      vnum = atoi( p );
   }
   else
   {
      vnum = ch->pIndexData ? ch->pIndexData->vnum : 0;
   }

   if( !is_valid_tag( arg2 ) )
   {
      progbug( "Mptag:  invalid characters in tag", ch );
      return;
   }
   error = FALSE;
   for( p = argument; *p; p++ )
   {
      if( !isdigit( *p ) && !isspace( *p ) )
      {
         error = TRUE;
         break;
      }
   }
   if( error )
   {
      vd = make_variable( vtSTR, vnum, arg2 );
      vd->data = str_dup( argument );
   }
   else
   {
      vd = make_variable( vtINT, vnum, arg2 );
      vd->data = ( void * )( atol( argument ) );
   }
   vd->timer = exp;
   tag_char( victim, vd, 1 );
}

/*
 * mprmtag <victim> <tag>
 */
void do_mprmtag( CHAR_DATA * ch, char *argument )
{
   CHAR_DATA *victim;
   char *p;
   char arg1[MAX_INPUT_LENGTH];
   char arg2[MAX_INPUT_LENGTH];
   int vnum = 0;

   if( ( !IS_NPC( ch ) && get_trust( ch ) < LEVEL_GREATER ) || IS_AFFECTED( ch, AFF_CHARM ) )
   {
      send_to_char( "Huh?\r\n", ch );
      return;
   }

   argument = one_argument( argument, arg1 );
   argument = one_argument( argument, arg2 );

   if( arg1[0] == '\0' || arg2[0] == '\0' )
   {
      send_to_char( "MPtag whom with what?\r\n", ch );
      return;
   }

   if( ( victim = get_char_room( ch, arg1 ) ) == NULL )
   {
      send_to_char( "They aren't here.\r\n", ch );
      return;
   }

   if( ( p = strchr( arg2, ':' ) ) != NULL )
   {
      *p++ = '\0';
      vnum = atoi( p );
   }
   else
   {
      vnum = ch->pIndexData ? ch->pIndexData->vnum : 0;
   }

   if( !is_valid_tag( arg2 ) )
   {
      progbug( "Mptag:  invalid characters in tag", ch );
      return;
   }

   if( !remove_tag( victim, arg2, vnum ) )
   {
      progbug( "Mptag:  could not find tag", ch );
   }
}

/*
 * mpflag <victim> <tag> <flag>
 */
void do_mpflag( CHAR_DATA * ch, char *argument )
{
   CHAR_DATA *victim;
   VARIABLE_DATA *vd;
   char *p;
   char arg1[MAX_INPUT_LENGTH];
   char arg2[MAX_INPUT_LENGTH];
   char arg3[MAX_INPUT_LENGTH];
   int vnum = 0, exp = 0, def = 0, flag = 0;
   bool error = FALSE;

   if( ( !IS_NPC( ch ) && get_trust( ch ) < LEVEL_GREATER ) || IS_AFFECTED( ch, AFF_CHARM ) )
   {
      send_to_char( "Huh?\r\n", ch );
      return;
   }

   argument = one_argument( argument, arg1 );
   if( !str_cmp( arg1, "noexpire" ) )
   {
      exp = 0;
      argument = one_argument( argument, arg1 );
   }
   else if( !str_cmp( arg1, "timer" ) )
   {
      argument = one_argument( argument, arg1 );
      exp = atoi( arg1 );
      argument = one_argument( argument, arg1 );
   }
   else
   {
      exp = ch->level * get_curr_int( ch );
      def = 1;
   }
   argument = one_argument( argument, arg2 );
   argument = one_argument( argument, arg3 );

   if( arg1[0] == '\0' || arg2[0] == '\0' || arg3[0] == '\0' )
   {
      send_to_char( "MPflag whom with what?\r\n", ch );
      return;
   }

   if( ( victim = get_char_room( ch, arg1 ) ) == NULL )
   {
      send_to_char( "They aren't here.\r\n", ch );
      return;
   }

   if( ( p = strchr( arg2, ':' ) ) != NULL )
   {
      *p++ = '\0';
      vnum = atoi( p );
   }
   else
   {
      vnum = ch->pIndexData ? ch->pIndexData->vnum : 0;
   }

   if( !is_valid_tag( arg2 ) )
   {
      progbug( "Mpflag:  invalid characters in tag", ch );
      return;
   }
   error = FALSE;
   for( p = arg3; *p; p++ )
   {
      if( !isdigit( *p ) && !isspace( *p ) )
      {
         error = TRUE;
         break;
      }
   }
   flag = atoi( arg3 );
   if( error || flag < 0 || flag >= MAX_BITS )
   {
      progbug( "Mpflag:  invalid flag value", ch );
      return;
   }
   if( ( vd = get_tag( victim, arg2, vnum ) ) != NULL )
   {
      if( vd->type != vtXBIT )
      {
         progbug( "Mpflag:  type mismatch", ch );
         return;
      }
      if( !def )
         vd->timer = exp;
   }
   else
   {
      vd = make_variable( vtXBIT, vnum, arg2 );
      vd->timer = exp;
   }
   xSET_BIT( *( EXT_BV * ) vd->data, flag );
   tag_char( victim, vd, 1 );
}


/*
 * mprmflag <victim> <tag> <flag>
 */
void do_mprmflag( CHAR_DATA * ch, char *argument )
{
   CHAR_DATA *victim;
   VARIABLE_DATA *vd;
   char *p;
   char arg1[MAX_INPUT_LENGTH];
   char arg2[MAX_INPUT_LENGTH];
   char arg3[MAX_INPUT_LENGTH];
   int vnum = 0;
   bool error = FALSE;

   if( ( !IS_NPC( ch ) && get_trust( ch ) < LEVEL_GREATER ) || IS_AFFECTED( ch, AFF_CHARM ) )
   {
      send_to_char( "Huh?\r\n", ch );
      return;
   }

   argument = one_argument( argument, arg1 );
   argument = one_argument( argument, arg2 );
   argument = one_argument( argument, arg3 );

   if( arg1[0] == '\0' || arg2[0] == '\0' || arg3[0] == '\0' )
   {
      send_to_char( "MPrmflag whom with what?\r\n", ch );
      return;
   }

   if( ( victim = get_char_room( ch, arg1 ) ) == NULL )
   {
      send_to_char( "They aren't here.\r\n", ch );
      return;
   }

   if( ( p = strchr( arg2, ':' ) ) != NULL )
   {
      *p++ = '\0';
      vnum = atoi( p );
   }
   else
   {
      vnum = ch->pIndexData ? ch->pIndexData->vnum : 0;
   }

   if( !is_valid_tag( arg2 ) )
   {
      progbug( "Mprmflag:  invalid characters in tag", ch );
      return;
   }
   error = FALSE;
   for( p = arg3; *p; p++ )
   {
      if( !isdigit( *p ) && !isspace( *p ) )
      {
         error = TRUE;
         break;
      }
   }
   if( error )
   {
      progbug( "Mprmflag:  invalid flag value", ch );
      return;
   }
   /*
    * Only bother doing anything if the tag exists
    */
   if( ( vd = get_tag( victim, arg2, vnum ) ) != NULL )
   {
      if( vd->type != vtXBIT )
      {
         progbug( "Mprmflag:  type mismatch", ch );
         return;
      }
      if( !vd->data )
      {
         progbug( "Mprmflag:  missing data???", ch );
         return;
      }
      xREMOVE_BIT( *( EXT_BV * ) vd->data, atoi( arg3 ) );
      tag_char( victim, vd, 1 );
   }
}

void fwrite_variables( CHAR_DATA * ch, FILE * fp )
{
   VARIABLE_DATA *vd;

   for( vd = ch->variables; vd; vd = vd->next )
   {
      fprintf( fp, "#VARIABLE\n" );
      fprintf( fp, "Type    %d\n", vd->type );
      fprintf( fp, "Flags   %d\n", vd->flags );
      fprintf( fp, "Vnum    %d\n", vd->vnum );
      fprintf( fp, "Ctime   %ld\n", vd->c_time );
      fprintf( fp, "Mtime   %ld\n", vd->m_time );
      fprintf( fp, "Rtime   %ld\n", vd->r_time );
      fprintf( fp, "Timer   %d\n", vd->timer );
      fprintf( fp, "Tag     %s~\n", vd->tag );
      switch ( vd->type )
      {
         case vtSTR:
            fprintf( fp, "Str     %s~\n", ( char * )vd->data );
            break;
         case vtXBIT:
            fprintf( fp, "Xbit    %s\n", print_bitvector( ( EXT_BV * ) vd->data ) );
            break;
         case vtINT:
            fprintf( fp, "Int     %ld\n", ( long )vd->data );
            break;
      }
      fprintf( fp, "End\n\n" );
   }
}

void fread_variable( CHAR_DATA * ch, FILE * fp )
{
   VARIABLE_DATA *pvd;
   const char *word;
   bool fMatch;

   CREATE( pvd, VARIABLE_DATA, 1 );
   for( ;; )
   {
      word = feof( fp ) ? "End" : fread_word( fp );
      fMatch = FALSE;

      switch ( UPPER( word[0] ) )
      {
         case '*':
            fMatch = TRUE;
            fread_to_eol( fp );
            break;

         case 'C':
            KEY( "Ctime", pvd->c_time, fread_number( fp ) );
            break;

         case 'E':
            if( !str_cmp( word, "End" ) )
            {
               switch ( pvd->type )
               {
                  default:
                  {
                     bug( "%s: invalid/incomplete variable: %s", __FUNCTION__, pvd->tag );
                     DISPOSE( pvd->tag );
                     DISPOSE( pvd );
                     break;
                  }
                  case vtSTR:
                  case vtXBIT:
                     if( !pvd->data )
                     {
                        bug( "%s: invalid/incomplete variable: %s", __FUNCTION__, pvd->tag );
                        DISPOSE( pvd->tag );
                        DISPOSE( pvd );
                        break;
                     }
                  case vtINT:
                     tag_char( ch, pvd, 1 );
                     break;
               }
               return;
            }
            break;

         case 'F':
            KEY( "Flags", pvd->flags, fread_number( fp ) );
            break;

         case 'I':
            if( !str_cmp( word, "Int" ) )
            {
               if( pvd->type != vtINT )
                  bug( "%s: Type mismatch -- type(%d) != vtInt", __FUNCTION__, pvd->type );
               else
               {
                  pvd->data = ( void * )( ( long )fread_number( fp ) );
                  fMatch = TRUE;
               }
               break;
            }
            break;

         case 'M':
            KEY( "Mtime", pvd->m_time, fread_number( fp ) );
            break;

         case 'R':
            KEY( "Rtime", pvd->r_time, fread_number( fp ) );
            break;

         case 'S':
            if( !str_cmp( word, "Str" ) )
            {
               if( pvd->type != vtSTR )
                  bug( "%s: Type mismatch -- type(%d) != vtSTR", __FUNCTION__, pvd->type );
               else
               {
                  pvd->data = fread_string_nohash( fp );
                  fMatch = TRUE;
               }
               break;
            }
            break;

         case 'T':
            KEY( "Tag", pvd->tag, fread_string_nohash( fp ) );
            KEY( "Timer", pvd->timer, fread_number( fp ) );
            KEY( "Type", pvd->type, fread_number( fp ) );
            break;

         case 'V':
            KEY( "Vnum", pvd->vnum, fread_number( fp ) );
            break;

         case 'X':
            if( !str_cmp( word, "Xbit" ) )
            {
               if( pvd->type != vtXBIT )
                  bug( "%s: Type mismatch -- type(%d) != vtXBIT", __FUNCTION__, pvd->type );
               else
               {
                  CREATE( pvd->data, EXT_BV, 1 );
                  *( EXT_BV * ) pvd->data = fread_bitvector( fp );
                  fMatch = TRUE;
               }
               break;
            }
            break;
      }

      if( !fMatch )
         bug( "%s: no match: %s", __FUNCTION__, word );
   }
}
