/* eval.c */
/* Expression parsing module created by Lawrence Foard */

#include "copyright.h"
#include <ctype.h>

#include "db.h"
#include "config.h"
#include "externs.h"
#include "interface.h"
#include "match.h"
#include "strings.h"

/* functions for the string expressions */

dbref match_thing(dbref player, const char *name)
{
  struct match_data md;
  init_match(player, name, NOTYPE, &md);
  match_everything(&md);
  
  return(noisy_match_result(&md));
}

void fun_rand(char *buff, const char *args[10])
{                                    
  int mod = atoi(args[0]);
  if (mod < 1)
    mod = 1;
  sprintf(buff,"%d",(random() & 65535) % mod);
}                         

void fun_time(char *buff, const char *args[10])
{
  long tt;
  time(&tt);
  strcpy(buff, (char *) ctime(&tt));
  buff[strlen(buff)-1] = 0;
}

void fun_get(char *buff, const char *args[10], dbref player)
{
  dbref thing;
  char x[1024];
  char y[1024];

  if(!index(args[0], '/')) {
    strcpy(buff, "NO MATCH");
    return;
  }

  strcpy(x, args[0]);
  *index(x, '/') = '\0';
  strcpy(y, 1 + index(args[0], '/'));
  if((thing = match_thing(player, x)) == NOTHING) {
    sprintf(buff, "NO MATCH");
    return;
  }
  if (!controls(player, thing))
    {
      strcpy(buff,"PERMISSION DENIED");
      return;
    }
  if(!get_property_class(thing, y)) {
    strcpy(buff,"NO MATCH");
    return;
  }
  strcpy(buff, get_property_class(thing,y));
}

void fun_mid(char *buff, const char *args[10])
{
  int l = atoi(args[1]);
  int len = atoi(args[2]);
  if ((l < 0) || (len < 0) || ((len + l) > 1000))
    {
      strcpy(buff,"OUT OF RANGE");
      return;
    }
  if (l < strlen(args[0]))
    strcpy(buff, args[0] + l);
  else
    *buff = 0;
  buff[len] = 0;
}


void fun_add(char *buff, const char *args[10])
{
  sprintf(buff, "%d", atoi(args[0]) + atoi(args[1]));
}

void fun_sub(char *buff, const char *args[10])
{
  sprintf(buff, "%d", atoi(args[0]) - atoi(args[1]));
}

void fun_mul(char *buff, const char *args[10])
{
  sprintf(buff, "%d", atoi(args[0]) * atoi(args[1]));
}


void fun_div(char *buff, const char *args[10])
{          
  int bot = atoi(args[1]);
  if (bot == 0)
    bot = 1;
  sprintf(buff, "%d", atoi(args[0]) / bot);
}

void fun_mod(char *buff, const char *args[10])
{          
  int bot = atoi(args[1]);
  if (bot == 0)
    bot = 1;
  sprintf(buff, "%d", atoi(args[0]) % bot);
}

/* read first word from a string */         
void fun_first(char *buff, char *args[10])
{
  char *s = args[0];              
  char *b;
  /* get rid of leading space */
  while(*s && (*s == ' ')) s++;
  b = s;
  while(*s && (*s != ' ')) s++;
  *s++ = 0;
  strcpy(buff, b);
}

void fun_rest(char *buff, const char *args[10])
{
  char *s = args[0];
  /* skip leading space */
  while(*s && (*s == ' ')) s++;
  /* skip firsts word */
  while(*s && (*s != ' ')) s++;
  /* skip leading space */
  while(*s && (*s == ' ')) s++;
  strcpy(buff, s);
}

void fun_strlen(char *buff, const char *args[10])
{
  sprintf(buff, "%d", strlen(args[0]));
}

/* handle 0-9,va-vz,n,# */  
void fun_v(char *buff, const char *args[10], dbref privs, dbref doer)
{           
  int c = args[0][0];
  switch(c)  
    {
    case '0':
    case '1':
    case '2':
    case '3':
    case '4':
    case '5':
    case '6':
    case '7':
    case '8':
    case '9':
      if (!awptr[c - '0'])
	{
	  *buff = 0;
	  return;
	}
      strcpy(buff, awptr[c - '0']);
      break;
    case 'v':
    case 'V':
      { 
	int a;
	a = toupper(args[0][1]);
	if ((a < 'A') || (a > 'Z'))
	  {
	    *buff = 0;
	    return;
	  }
	{
	  char x[5];
	  *x = 'v';
	  *(x + 2) = '\0';
	  *(x + 1) = a;
	  if(get_attr(privs, x))
	    strcpy(buff, get_attr(privs, x));
	  else
	    *buff = '\0';
	}
      }
      break;
    case 'n':
    case 'N':
      strcpy(buff, DBFETCH(doer)->name);
      break;                            
    case '#':
      sprintf(buff, "#%d", doer);
      break;
      /* info about location and stuff */
      /* objects # */
    case '!':     
      sprintf(buff, "#%d", privs);
      break;                    
    default:
      *buff = 0;
    }
}

void fun_s(char *buff, char *args[10], dbref privs, dbref doer)
{        
  char buff1[BUFFER_LEN];
  strcpy(buff1, pronoun_substitute(doer, args[0], privs));
  strcpy(buff, buff1 + strlen(NAME(doer)) + 1);
}

void fun_num(char *buff, const char *args[10], dbref privs)
{
  sprintf(buff, "#%d", match_thing(privs, args[0]));
}                            

void fun_con(char *buff, const char *args[10], dbref privs, dbref doer)
{
  dbref it = match_thing(privs, args[0]);
  if ((it != NOTHING) && (controls(privs, it) ||
			  (DBFETCH(privs)->location == it) || (it == doer))) {
    sprintf(buff, "#%d", DBFETCH(it)->contents);
  } else {
    strcpy(buff, "#-1");
  }
  return;
}

/* return next exit that is ok to see */
dbref next_exit(dbref player, dbref this)
{
  while((this != NOTHING) && (FLAGS(this) & DARK) && !controls(player, this))
    this = DBFETCH(this)->next;
  return(this);
}

void fun_exit(char *buff, const char *args[10], dbref privs, dbref doer)
{
  dbref it = match_thing(privs, args[0]);
  if ((it != NOTHING) && (controls(privs, it)
			  || (DBFETCH(privs)->location == it) || (it == doer)))
    {
      sprintf(buff, "#%d", next_exit(privs, DBFETCH(it)->exits));
    } else {
      strcpy(buff, "#-1");
    }
  return;
}

void fun_next(char *buff, const char *args[10], dbref privs, dbref doer)
{
  dbref it = match_thing(privs, args[0]);
  if (it != NOTHING)         
    {
      if (Typeof(it) != TYPE_EXIT)
	{
	  if (controls(privs, DBFETCH(it)->location) ||
	      (DBFETCH(it)->location == doer) ||
	      (DBFETCH(it)->location == DBFETCH(privs)->location)) {
	    sprintf(buff, "#%d", DBFETCH(it)->next);
	    return;
	  }
	}
      else
	{
	  sprintf(buff, "#%d", next_exit(privs, DBFETCH(it)->next));
	  return;
	}
    }
  strcpy(buff, "#-1");
  return; 
} 

void fun_loc(char *buff, const char *args[10], dbref privs, dbref doer)
{
  dbref it = match_thing(privs, args[0]);
  if ((it != NOTHING) && 
      (controls(privs, it) || controls(privs, DBFETCH(it)->location) ||
       (it == doer)))
    {
      sprintf(buff,"#%d",DBFETCH(it)->location);
    } else {
      strcpy(buff,"#-1");
    }
  return;
}

void fun_owner(char *buff, const char *args[10], dbref privs)
{
  dbref it = match_thing(privs, args[0]);
  if (it != NOTHING)
    it = DBFETCH(it)->owner;
  sprintf(buff,"#%d", it);
}

void fun_name(char *buff, const char *args[10], dbref privs)
{
  dbref it = match_thing(privs, args[0]);
  if (it == NOTHING)
    strcpy(buff, "");
  else
    if (Typeof(it) == TYPE_EXIT)
      {
	char *s;
	strcpy(buff, DBFETCH(it)->name);
	for(s = buff; *s && (*s != ';'); s++)
	  ;
	*s = 0;
      }
    else
      strcpy(buff, DBFETCH(it)->name);
}

void fun_match(char *buff, char *args[10])
{
  int wcount = 1;
  char *s = args[0];
  char *ptrsrv[10];
  int a;
  for(a = 0; a < 10; a++)
    ptrsrv[a] = awptr[a];
  do
    {
      char *r;
      while(*s && (*s == ' '))
	s++;
      r = s;
      while(*s && (*s != ' '))
	s++;
      if (*s)
	*s++=0;
      if (wild_match(args[1], r))
	{
	  sprintf(buff, "%d", wcount);
	  for(a = 0; a < 10; a++)
	    awptr[a] = ptrsrv[a];
	  return;
	}       
      wcount++;
    }
  while(*s); 
  strcpy(buff, "0");
  for(a = 0; a < 10; a++)
    awptr[a]=ptrsrv[a];
} 

void fun_extract(char *buff, char *args[10])
{
  int start = atoi(args[1]);
  int len = atoi(args[2]);
  char *s = args[0];
  char *r;

  if ((start < 1) || (len < 1))
    {
      *buff = 0;
      return;
    }
  start--;
  while(start && *s)
    {
      while(*s && (*s == ' '))
	s++;
      while(*s && (*s != ' '))
	s++;
      start--;
    }
  while(*s && (*s == ' '))
    s++;
  r = s;
  while(len && *s)
    {
      while(*s && (*s == ' '))
	s++;
      while(*s && (*s != ' '))
	s++;
      len--;
    } 
  *s = 0;
  strcpy(buff, r);
} 

typedef struct fun FUN;

struct fun
{ 
  char *name;
  void (*fun) ();
  int nargs;
};

FUN flist[] = {
  {"RAND",   fun_rand,   1},
  {"TIME",   fun_time,   0},
  {"GET",    fun_get,    1},
  {"MID",    fun_mid,    3},
  {"ADD",    fun_add,    2},
  {"SUB",    fun_sub,    2},
  {"MUL",    fun_mul,    2},
  {"DIV",    fun_div,    2},
  {"MOD",    fun_mod,    2},
  {"FIRST",  fun_first,  1},
  {"REST",   fun_rest,   1},
  {"STRLEN", fun_strlen, 1},
  {"V",      fun_v,      1},
  {"S",      fun_s,      1},
  {"MATCH",  fun_match,  2},
  {"EXTRACT",fun_extract,3},
  {"NUM",    fun_num,    1},
  {"CON",    fun_con,    1},
  {"NEXT",   fun_next,   1},
  {"OWNER",  fun_owner,  1},
  {"LOC",    fun_loc,    1},
  {"EXIT",   fun_exit,   1},
  {"NAME",   fun_name,   1},
  {NULL,     NULL,       0}
};

void do_fun(char **str, char *buff, dbref privs, dbref doer)
{
  char *s;
  FUN *fp;
  char *args[10];
  char obuff[1024];
  int a;

  /* look for buff in flist */
  strcpy(obuff, buff);
  for(s = buff; *s; s++)
    *s = islower(*s) ? toupper(*s) : *s;
  for(fp = flist; fp->name && strcmp(fp->name, buff); fp++);
  /* if not found return all stuff up to next matching ) */
  if (!fp->name)
    {
      int deep = 2;
      char *t = buff + strlen(obuff);
      strcpy(buff, obuff);
      *t++ = '(';
      while(**str && deep)
	switch(*t++ = *(*str)++)
	  {
	  case '(':
	    deep++;
	    break;
	  case ')':
	    deep--;
	    break;
	  }
      if (**str)
	{
	  (*str)--;
	  t--;
	}
      *t = 0;
      return;
    }
  /* now get the arguments to the function */
  for(a = 0; (a < 10) && **str &&  (**str != ')'); a++)          
    {
      if (**str == ',')
	(*str)++;
      exec(str, obuff, privs, doer, 1);
      args[a] = alloc_ec_string(obuff);
    }
  if (**str)
    (*str)++;
  if ((fp->nargs != -1) && (fp->nargs != a))
    sprintf(buff, "Function (%s) only expects %d arguments",
	    fp->name, fp->nargs);
  else
    fp->fun(buff, args, privs, doer);
}

void exec(char **str, char *buff, dbref privs, dbref doer, int coma)
{
  char *s, *e = buff;
  *buff = 0;

  /* eat preceding space */
  for (s = *str; *s && isspace(*s); s++)
    ;

  /* parse until (, ],) or , */
  for (; *s; s++)
    switch (*s) {
      case ',':         /* comma in list of function arguments */
      case ')':         /* end of arguments */
        if (!coma)
          goto cont;
      case ']':         /* end of expression */
        /* eat trailing space */
        while ((--e >= buff) && isspace(*e)) ;
        e[1] = 0;
        *str = s;
        return;
      case '(':         /* this is a function */
        while ((--e >= buff) && isspace(*e))
          ;
        e[1] = 0;
        *str = s + 1;
        /* if just ()'s by them self then it is quoted */
        if (*buff)
          do_fun(str, buff, privs, doer);
        return;
      case '{':
        if (e == buff) {
          int deep = 1;
          e = buff;
          s++;
          while (deep && *s)
            switch (*e++ = *s++) {
              case '{':
                deep++;
                break;
              case '}':
                deep--;
                break;
            }
          if ((e > buff) && (e[-1] == '}'))
            e--;
          while ((--e >= buff) && isspace(*e))
            ;
          e[1] = 0;
          *str = s;
          return;
        } else
          /* otherwise is a quote in middle, search for other end */
        {
          int deep = 1;
          *e++ = *s++;
          while (deep && *s)
            switch (*e++ = *s++) {
              case '{':
                deep++;
                break;
              case '}':
                deep--;
                break;
            }
          s--;
        }
        break;
      default:
      cont:
        *e++ = *s;
        break;
    }
  while ((--e >= buff) && isspace(*e))
    ;
  e[1] = 0;
  *str = s;
  return;
}

char * check_arg(char *what, dbref player, dbref cause)
{
  char buf[BUFFER_LEN];

  if(!what) return alloc_ec_string("");
  printf("what: %s\n", what);
  exec(&what, buf, player, cause, 0);
  printf("buf: %s\n", buf);
  return alloc_ec_string(buf);
}