/*  debugcmd.c-- Built in debugging commands              */
/* Copyright (C) 1996-1999,2001  Robert Masenten          */
/* This program may be redistributed under the terms of the
   GNU General Public License, version 2; see agility.h for details. */
/*                                                                */
/* This is part of the source for AGiliTy, the (Mostly) Universal  */
/*       AGT Interpreter                                           */

#include <ctype.h>
#include "agility.h"
#include "interp.h"
#include "exec.h"

static void d_moveobj(int obj,int dest)
/* 1=the player, -1=unknown: ask */
{
  if (obj==-1) {
    writestr("Which object? ");
    obj=read_number();
    if (obj!=1 && !tnoun(obj) && !tcreat(obj)) {
      writeln("Invalid object");
      return;
    }
  }
  if (dest==-1) {
    writestr("To where? ");
    dest=read_number();
    if (dest!=1 && dest!=0 && !tnoun(dest) && !tcreat(dest) && !troom(dest)) {
      writeln("Invalid object");
      return;
    }
  }
  if (obj!=1)
    it_move(obj,dest);
  else  {
    if (!troom(dest)) {
      writeln("Player can only be moved to a room");
      return;
    }
    goto_room(dest-first_room);
  }
}


static int print_objid(int obj)
{
  char buff[10];  
  char *s;
  int n;

  sprintf(buff,"%4d: ",obj);
  writestr(buff);
  s=objname(obj);
  for(n=0;s[n]!=0;n++)
    if (s[n]<=8 || (uchar)s[n]==0xFF) s[n]=' '; /* Strip out format codes */
  writestr(s);
  n=strlen(s);
  rfree(s);
  return n;
}


static void d_listroom(void)
{
  int i;
  
  writeln("       ROOM");
  writeln("      ------");
  writeln("");
  for(i=0;i<=maxroom-first_room;i++) {
    print_objid(i+first_room);
    writeln("");
  }
}



#define SEPLENG 27  /* Width between beginning of object column and
		       location column */
	    

static void d_listnoun(void)
{
  int i;
  int len;

  writestr("      NOUN ");padout(SEPLENG-6);writeln("     LOCATION ");
  writestr("     ------");padout(SEPLENG-6);writeln("    ----------");
  writeln("");
  len=SEPLENG-print_objid(1);
  padout(len);
  writestr("[");
  print_objid(loc);
  writeln("]");  

  nounloop(i) {
    len=print_objid(i+first_noun);
    len=SEPLENG-len;
    if (len>0) padout(len);
    writestr("[");
    print_objid(noun[i].location);
    writeln("]");
  }  
}

static void d_listcreat(void)
{
  int i;
  int len;

  writestr("        CREATURE ");padout(SEPLENG-11);writeln("     LOCATION ");
  writestr("       ----------");padout(SEPLENG-11);writeln("    ----------");
  writeln("");

  creatloop(i) {
    len=print_objid(i+first_creat);
    len=SEPLENG-len;
    if (len>0) padout(len);
    writestr("    [");
    print_objid(creature[i].location);
    writeln("]");    
  }
}


static void writetbl(const char *s,int width)
/* This writes out s and then prints out any additional spaces needed
   to make the output string *width* wide. */
{
  writestr(s);
  width=width-strlen(s);
  if (width>0) padout(width);
}


static void var_edit(int vtype)
/* vtype=0 for variable, 1 for counter, 2 for flag */
{
  long n;
  int i;
  int imax;
  char sbuff[30];

  switch (vtype) 
    {
    case 0: imax=VAR_NUM;break;
    case 1: imax=CNT_NUM;break;
    case 2: imax=FLAG_NUM;break;
    default: writeln("INTERNAL ERROR: Invalid vtype.");
      return;
    }

  for(;;) {
    agt_clrscr();
    writeln("");
    switch (vtype) {
      case 0:writeln("Variables");break;
      case 1:writeln("Counters (-1 means the counter is off)");break;
      case 2:writeln("Flags ( f=false [OFF]  and t=true [ON] )");break;
      }
    writeln("");
    for(i=0;i<=imax;i++) {
      switch (vtype) {
	case 0:sprintf(sbuff,"[Var%3d]=%4ld",i,(long)agt_var[i]);break;
	case 1:sprintf(sbuff,"[Cnt%3d]=%4ld",i,(long)agt_counter[i]);break;
	case 2:sprintf(sbuff,"%3d%c",i,flag[i]?'t':'f');break;
	}
      writetbl(sbuff,(vtype==2)? 5 : 20);
    }
    writeln("");writeln("");
    for(;;) {
      switch (vtype) {
	case 0:writestr("Variable to change");break;
	case 1:writestr("Counter to change");break;
	case 2:writestr("Flag to toggle");break;
	}
      writestr(" (-1 to quit): ");
      i=read_number();
      if (i<0) return;
      if (i<=imax) {
	if (vtype!=2) {
	  if (vtype==0)
	    sprintf(sbuff,"[Var%d]=%ld",i,(long)agt_var[i]);
	  else sprintf(sbuff,"[Cnt%d]=%ld (-1 means it's off)",
		       i,(long)agt_counter[i]);
	  writestr(sbuff);
	  writestr("; new value = ");
	  n=read_number();
	  if (vtype==0) 
	    agt_var[i]=n;
	  else 
	    if (n<-1 || n>(((long)1)<<15)-1 ) 
	      writeln("Invalid value for a counter.");
	    else agt_counter[i]=n;
	} else flag[i]=!flag[i];
	break;       
      } else 
	writeln("Invalid index.");
    }
  }
}

/* Routines to edit user strings */
static void edit_str(void)
{
  int i,j;
  char buff[10];
  char *tmpstr;

  if (MAX_USTR==0 || userstr==NULL) {
    writeln("This game doesn't contain any user strings");
    return;
  }
  for(;;) {
    agt_clrscr();
    writeln("User Definable Strings");
    writeln("");
    for(i=0;i<MAX_USTR;i++) {
      sprintf(buff,"%2d:",i+1);
      writestr(buff);
      writeln(userstr[i]);
    }
    writestr(" (0 to quit): ");
    i=read_number();
    if (i==0) return;
    if (i>0 && i<=MAX_USTR) {
      writeln("Enter new string:");
      tmpstr=agt_readline(3);
      j=strlen(tmpstr)-1;
      if (j>0 && tmpstr[j]=='\n') tmpstr[j]=0;
      strncpy(userstr[i-1],tmpstr,80);
    } else writeln("Invalid string number");
  }
}

static uchar attrcol;  /* Determines which column the attribute is put in */
static uchar attrwidth; /* Number of attribute columns */

static void next_col(void)
{
  if (++attrcol==attrwidth) {
    writeln("");
    attrcol=0;
  }
  else 
    padout(10);
}

static void writeattr(char *attrname,rbool attrval)
{
  writestr(attrname);
  padout(15-strlen(attrname));
  if (attrval) writestr("yes"); else writestr("no ");
  next_col();
}

static void writegender(char *gendername, uchar genderval)
{
  writestr(gendername);
  padout(15-strlen(gendername)-3);
  switch(genderval) 
    {
    case 2: writestr("Male  ");break;
    case 1: writestr("Female");break;
    case 0: writestr("Thing");break;
    }
  next_col();
}

static void writeprop(char *propname,int obj)
{
  writestr(propname);
  writestr(" [");
  print_objid(obj);
  writeln("]");
}

static int writedir(int index,int dir,int obj)
{
  char sbuff[40];

  sprintf(sbuff,"%2d.%-2s %d",index,exitname[dir],obj);
  writestr(sbuff);
  return strlen(sbuff);
}

void writenum(char *propname,int n)
{
  char sbuff[20];

  writestr(propname);
  sprintf(sbuff,"%4d",n);
  writeln(sbuff);
}
   
static void writeflags(char *flagname, int32 flags)
{
  int i;
  char sbuff[5];

  writestr(flagname);
  for(i=0;i<32;i++) {
    if (flags & 1) { 
      sprintf(sbuff,"%2d ",i);
      writestr(sbuff);
    } else
      writestr("   ");
    flags>>=1;
    if (i%12==11) { 
      writeln(""); 
      padout(strlen(flagname)); 
    }
  }
  writeln("");
}


static void readflags(int32 *flags)
{
  long n;

  writestr("Room flag to toggle (0-31)? ");
  n=read_number();
  if (n<=31 && n>=0) 
    *flags^=( ((long)1) << n );
}

static long readval(const char *prompt,int type)
{
  long val;

  for(;;) {
    writestr(prompt);
    writestr(" ");
    val=read_number();
    if (argvalid(type,val)) return val;
    writeln("Invalid value.");
  }
}


static uchar readgender(void)
{
  char c;

  writestr("Gender (M/F/N): ");
  for(;;) {
    c=tolower(agt_getchar());
    switch(c) 
      {
      case 'm': return 2;
      case 'w':  
      case 'f': return 1;
      case 'n': 
      case 't': return 0;
      default: ;/* Do nothing */
      }
  } 
}


static void edit_objattr(int obj)
{
  int i,k, kprop, n;
  long v;

  for(;;) {
    k=1;
    agt_clrscr();
    print_objid(obj);  
    writeln("");
    if (oflag_cnt>0) {
      writeln("ObjFlags:");
      for(i=0;i<oflag_cnt;i++)
	if (have_objattr(0,obj,i)) {
	  v=op_objflag(2,obj,i);
	  rprintf("%2d. ObjProp%2d:%c %-40s\n", k++, i, (v ? '+' : '-'),
		  get_objattr_str(AGT_OBJFLAG,i,v));      
	}
      writeln("");
    }
    kprop=k;
    if (oprop_cnt>0) {
      writeln("ObjProps:");
      for(i=0;i<oprop_cnt;i++)
	if (have_objattr(1,obj,i)) {
	  v=op_objprop(2,obj,i,0);
	  rprintf("%2d. ObjFlag%2d: [%3ld] %-40s\n", k++, i, v,
		  get_objattr_str(AGT_OBJPROP,i,v));      
	}
      writeln("");
    }
    writestr("Field to change (0 to return to main view)? ");
    n=read_number();
    if (n==0) return;
    if (n<1 || n>=k) continue;
    k=0;
    if (n<kprop) { /* Attribute */
      for(i=0;i<oflag_cnt;i++)
	if (have_objattr(0,obj,i)) 
	  if (n==++k) break;
      if (n==k && have_objattr(0,obj,i))
	op_objflag(3,obj,i); /* Toggle it */
    } else { /* Property */
      for(i=0;i<oprop_cnt;i++)
	if (have_objattr(1,obj,i)) 
	  if (n==++k) break;
      if (n==k && have_objattr(1,obj,i))
	op_objprop(1,obj,i,readval("New value:",AGT_NUM));
    }
  }
}


static void room_edit(int i)
{
  int n, j;

  for(;;) {
    agt_clrscr();
    writestr("ROOM ");
    print_objid(i+first_room);
    writeln("");
    writeln("");
    attrcol=0;attrwidth=2;
    writeattr("1.*WinGame:",room[i].win);
     writeattr("4. Seen:",room[i].seen); 
    writeattr("2.*EndGame:",room[i].end);
     writeattr("5. LockedDoor:",room[i].locked_door);
    writeattr("3.*Die:",room[i].killplayer);
    writeln("");writeln("");
    writeprop("6.*Key    =",room[i].key);
    writeprop("7. Light  =",room[i].light);
     writenum("8. Points =",room[i].points);
    writeprop("9. Class = ",room[i].oclass);
    writeln("");
    writeln("EXITS:");
    for(j=0;j<12;j++) {
      n=writedir(j+10,j,room[i].path[j]);
      if (j%4==3) writeln(""); else padout(15-n);
    }
    writeprop("22. SPECIAL:",room[i].path[12]);
    writeflags("23. Room Flags:",room[i].flag_noun_bits);
    writeln("24. Object properties and attributes.");
    writeln("");
    writeln("(Fields marked with an * are not saved or restored.)");
    /* writeln(""); */
    writestr("Field to change (0 to exit)? ");
    n=read_number();
    if (n==0) return;
    switch (n) 
      {
      case 1:room[i].win=!room[i].win;break;
      case 2:room[i].end=!room[i].end;break;
      case 3:room[i].killplayer=!room[i].killplayer;break;
      case 4:room[i].seen=!room[i].seen;break;
      case 5:room[i].locked_door=!room[i].locked_door;break; 
      case 6:room[i].key=readval("Key = ",AGT_ITEM|AGT_NONE);break;
      case 7:room[i].light=readval("Light = ",AGT_ITEM|AGT_NONE|AGT_SELF);
	break;
      case 8:room[i].points=readval("Points = ",AGT_NUM);break;
      case 9:room[i].oclass=readval("Class = ",AGT_ROOM|AGT_NONE);break;
      case 22:room[i].path[12]=readval("SPECIAL: ",AGT_NUM);break;
      case 23:readflags(&room[i].flag_noun_bits);break;
      case 24:edit_objattr(i+first_room);break;
      default:
	if (n>=10 && n<22) { /* Direction */
	  room[i].path[n-10]=readval(exitname[n-10],AGT_NUM);
	}
	else writeln("Invalid field");
      }
  }
}


#define tog(x) {x=!x;break;}

static void noun_edit(int i)
{
  int n;

  for(;;) {
    agt_clrscr();
    /* writeln("");*/
    writestr("NOUN       ");
    print_objid(i+first_noun);
    /*    writeln("");*/
    /* writeln("");*/
    writeprop("      Location=",noun[i].location);
    writeln("");
    attrcol=0;attrwidth=3;
    writeattr(" 1.*Pushable:",noun[i].pushable);
     writeattr(" 8.*Lockable:",noun[i].lockable);
     writeattr("15.*Drinkable:",noun[i].drinkable);
    writeattr(" 2.*Pullable:",noun[i].pullable);
     writeattr(" 9.*Light:",noun[i].light);
     writeattr("16.*Poisonous:",noun[i].poisonous);
    writeattr(" 3.*Turnable:",noun[i].turnable);
     writeattr("10.*Plural:",noun[i].plural);
     writeattr("17. Open:",noun[i].open);
    writeattr(" 4.*Playable:",noun[i].playable);
     writeattr("11. Movable:",noun[i].movable);
     writeattr("18. Locked:",noun[i].locked);
    writeattr(" 5.*Readable:",noun[i].readable);    
     writeattr("12.*Shootable:",noun[i].shootable);
     writeattr("19.*Win Game:",noun[i].win);
    writeattr(" 6.*Wearable:",noun[i].wearable);        
     writeattr("13. On:",noun[i].on);
     writeattr("20.*Global:",noun[i].isglobal);
    writeattr(" 7.*Closable:",noun[i].closable);
     writeattr("14.*Edible:",noun[i].edible); 
     writeattr("21.*Proper:",noun[i].proper);

    writeln("");
    writenum("22. Shots  =",noun[i].num_shots);
    writenum("23. Points =",noun[i].points);
    writenum("24. Weight =",noun[i].weight);
    writenum("25. Size   =",noun[i].size);
   writeprop("26.*Key    =",noun[i].key);
   writeprop("27. Class  =",noun[i].oclass);
    writenum("28. Flag   =",noun[i].flagnum);
     writeln("");
     /* writeln(""); */
    writeln("29. Object properties and attributes.");
    writeln("");
    writeln("(Fields marked with an * are not saved or restored.)");
    writestr("Field to change (0 to exit)? ");
    n=read_number();
    if (n==0) return;
    switch (n) 
      {
      case 1:tog(noun[i].pushable); /* tog() macro includes break */
      case 2:tog(noun[i].pullable);
      case 3:tog(noun[i].turnable);
      case 4:tog(noun[i].playable);
      case 5:tog(noun[i].readable);
      case 6:tog(noun[i].wearable);
      case 7:tog(noun[i].closable);
      case 8:tog(noun[i].lockable);
      case 9:tog(noun[i].light);
      case 10:tog(noun[i].plural);
      case 11:tog(noun[i].movable);
      case 12:tog(noun[i].shootable);
      case 13:tog(noun[i].on);
      case 14:tog(noun[i].edible);
      case 15:tog(noun[i].drinkable);
      case 16:tog(noun[i].poisonous);
      case 17:tog(noun[i].open);
      case 18:tog(noun[i].locked);
      case 19:tog(noun[i].win);
      case 20:tog(noun[i].isglobal);
      case 21:tog(noun[i].proper);

      case 22:noun[i].num_shots=readval("Shots =",AGT_NUM);break;
      case 23:noun[i].points=readval("Points =",AGT_NUM);break;
      case 24:noun[i].weight=readval("Weight =",AGT_NUM);break;
      case 25:noun[i].size=readval("Size =",AGT_NUM);break;
      case 26:noun[i].key=readval("Key =",AGT_ITEM|AGT_NONE);break;
      case 27:noun[i].oclass=readval("Class =",AGT_ITEM|AGT_NONE);break;
      case 28:noun[i].flagnum=readval("Flag Number=",AGT_ROOMFLAG);break;
      case 29:edit_objattr(i+first_noun);break;
      default:writeln("Invalid field");
      }
  }
}


static void creat_edit(int i)
{
  int n;

  for(;;) {
    agt_clrscr();
    writestr("CREATURE    ");
    print_objid(i+first_creat);
    writeln("");
    writeln("");
    writeprop("Location =",creature[i].location);
    writeln("");
    attrcol=0;attrwidth=2;
    writeattr(" 1. Hostile:",creature[i].hostile);
    writeattr(" 4. Global:",creature[i].isglobal);
    writeattr(" 2. Grp member:",creature[i].groupmemb);
    writeattr(" 5.*Proper:",creature[i].proper);
    writegender(" 3.*Gender:",creature[i].gender);
    writeln("");writeln("");
    writeprop(" 6.*Weapon = ",creature[i].weapon);
     writenum(" 7. Points = ",creature[i].points);    
     writenum(" 8.*Attack Threshold  = ",creature[i].threshold);
     writenum(" 9.   Attack counter  = ",creature[i].counter);
     writenum("10.*Attack Time Limit = ",creature[i].timethresh);
     writenum("11.   Attack timer    = ",creature[i].timecounter);
    writeprop("12. Class  = ",creature[i].oclass);
     writenum("13. Flag   = ",creature[i].flagnum);
    writeln("");
    writeln("14. Object properties and attributes.");
    writeln("");
    writeln("(Fields marked with an * are not saved or restored.)");
    writeln("");
    writestr("Field to change (0 to exit)? ");
    n=read_number();
    if (n==0) return;
    switch (n) 
      {
      case 1:tog(creature[i].hostile);
      case 2:tog(creature[i].groupmemb);
      case 3:tog(creature[i].isglobal);
      case 4:tog(creature[i].proper);

      case 5:creature[i].gender=readgender();break;
      case 6:creature[i].weapon=readval("Weapon =",AGT_ITEM|AGT_NONE);break;
      case 7:creature[i].points=readval("Points =",AGT_NUM);break;
      case 8:creature[i].threshold=readval("Threshold =",AGT_NUM);break;
      case 9:creature[i].counter=readval("Attack counter =",AGT_NUM);break;
      case 10:creature[i].timethresh=readval("Time limit =",AGT_NUM);break;
      case 11:creature[i].timecounter=readval("Timer =",AGT_NUM);break;
      case 12:creature[i].oclass=readval("Class =",AGT_ITEM|AGT_NONE);break;
      case 13:noun[i].flagnum=readval("Flag Number=",AGT_ROOMFLAG);break;
      case 14:edit_objattr(i+first_creat);break;
      default:writeln("Invalid field");
      }
  }
}

#undef tog





static void obj_edit(void)
{
  int n;

  for(;;) {
    writeln("");
    do {
      writestr("Enter object number (0 to exit)? ");
      n=read_number();
      if (n<=0) return;
    } while (!troom(n) && !tnoun(n) && !tcreat(n));

    if (troom(n)) room_edit(n-first_room);
    else if (tnoun(n)) noun_edit(n-first_noun);
    else if (tcreat(n)) creat_edit(n-first_creat);
    else writeln("[Not yet implemented]");
    
  }
}

static char *yesnostr[]={"No","Yes"};

static void set_debug_options(void)
{
  char buff[80];
  int n;

  for(;;) {
    agt_clrscr();
    writeln("DEBUGGING OPTIONS:");
    writeln("");
    sprintf(buff,"  1. Trace metacommands: %s",yesnostr[DEBUG_AGT_CMD]);
    writeln(buff);
    sprintf(buff,"  2. Trace ANY metacommands: %s",yesnostr[debug_any]);
    writeln(buff);
    sprintf(buff,"  3. Trace during disambiguation: %s",
	    yesnostr[debug_disambig]);
    writeln(buff);  
    writeln("");
    writeln("(<2> and <3> are ignored if <1> is not set; option <1> can"
	    " also be changed from the main debugging menu)");
    writeln("");
    writestr("Option to toggle (0 to exit): ");
    n=read_number();
    switch(n) 
      {
      case 0:return;
      case 1:DEBUG_AGT_CMD=!DEBUG_AGT_CMD;break;
      case 2:debug_any=!debug_any;break;
      case 3:debug_disambig=!debug_disambig;break;
      default:writeln("Not a valid option");
      }
  }
}


void get_debugcmd(void)
{
  int n;
  
  for(;;) {
    writeln("DEBUGGING COMMANDS");
    writeln("");
    writeln("1. Move player          8. List Rooms");
    writeln("2. Get Noun             9. List Nouns");
    writeln("3. Move object         10. List Creatures");
    writeln("4. View/Edit object    11. List/Set Flags");
    writeln("5. Toggle Trace        12. List/Set Variables");  
    writeln("6. Set Debug Options   13. List/Set Counters");
    writeln("7. Edit User Strings");
    writeln("");
    writestr("Enter choice (0 to exit): ");
    n=read_number();
    switch(n) 
      {
      case -1:
      case 0:return;
      case 1:d_moveobj(1,-1);break;
      case 2:d_moveobj(-1,1);break;
      case 3:d_moveobj(-1,-1);break;
      case 4:obj_edit();break;
      case 5:DEBUG_AGT_CMD=!DEBUG_AGT_CMD;break;
      case 6:set_debug_options();break;
      case 7:edit_str();break;
      case 8:d_listroom();break;
      case 9:d_listnoun();break;
      case 10:d_listcreat();break;
      case 11:var_edit(2);break;
      case 12:var_edit(0);break;
      case 13:var_edit(1);break;
      default:writeln("Not a valid option");
      }
    writeln("");
  };
}
