#include <stdarg.h>
#include <sys/types.h>

#include "quirc.h"
#include "hash.h"
#include "config.h"
#include "format.h"
#include "echo.h"
#include "channel.h"

#include <string>
#ifdef USING_STD_STRING
using std::string;
#endif

#define NUMBER '0':\
               case '1':\
               case '2':\
               case '3':\
               case '4':\
               case '5':\
               case '6':\
               case '7':\
               case '8':\
               case '9'

char *fparse_result;

static char buflow[20];
static char bufhigh[20];
static char *buflowp;
static char *bufhighp;

int append(char *low, char *high, string &assemble, int argc,
	   const char * const * argv) {
  int start=atoi(low);
  int end=atoi(high);
  int n;
  char *dst;
  int flags;

  for(n=start;n<=end;n++) {
    if(n>=argc) return 1;
    dst=(char *)malloc(Tcl_ScanElement(argv[n],&flags));
    if(!dst) return 1;
    flags|=TCL_DONT_USE_BRACES;
    Tcl_ConvertElement(argv[n], dst, flags);
    if(strcmp(dst,"{}")) {
      assemble+=dst;
    }
    free(dst);
    if(n!=end) assemble+="\\ ";
  }
  strcpy(buflow,"0");
  sprintf(bufhigh,"%d",argc-1);
  bufhighp=bufhigh;
  buflowp=buflow;
  return 0;
}

char *fparsev(const char *type, int server, int argc,
	      const char * const * argv) {
  // %X- %-X %X-Y %- %%
  // Replaces arguments with an escaped version of a space seperated arg list.
  // Argument is then evaluated within a set.
  // Return 0 if there is an error (ie. no arg for format number)

  // Set buflow to 0, bufhigh to argc-1

  // N is number, D is default.

  // State 0:
  //  Got nothing
  //   %, go to state 1.
  //   D, insert letter.
  // State 1:
  //  Got %
  //   %, simply insert a %, go back to state 0.
  //   N, could be %X- %X %X-Y, set buflow to it, go to state 2.
  //   -, could be %-X %-, go to state 3.
  //   D, insert %, backup, go to state 0.
  // State 2:
  //  Got %N
  //   N, append to buflow, loop.
  //   -, go to state 3
  //   D, insert argument buflow, backup, go to state 0.
  // State 3:
  //  Got %- or %N-
  //   N, set bufhigh to it, go to state 4.
  //   D, insert buflow to bufhigh, backup, go to state 0.
  // State 4:
  //  Got %-N or %N-N
  //   N, append to bufhigh, loop.
  //   D, insert buflow to bufhigh, backup, go to state 0.
  // Post loop processing is identical to default without backup or state 0
  // stuff.

  string assemble;
  char *format;
  int n;
  int state=0;

  if(fparse_result) {
    free(fparse_result);
    fparse_result=0;
  }
  format=hash_get(&formats,type);
  if(!format) return 0;

  strcpy(buflow,"0");
  sprintf(bufhigh,"%d",argc-1);
  bufhighp=bufhigh;
  buflowp=buflow;

  for(n=0;n<(signed)strlen(format);n++) {
    switch(state) {
    case 0:
      switch(format[n]) {
      case '%':
	state=1;
	break;
      default:
	assemble+=format[n];
      }
      break;
    case 1:
      switch(format[n]) {
      case '%':
	assemble+='%';
	state=0;
	break;
      case NUMBER:
	*(buflowp++)=format[n];
	*buflowp=0;
	state=2;
	break;
      case '-':
	state=3;
	break;
      default:
	assemble+='%';
	n--;
	state=0;
      }
      break;
    case 2:
      switch(format[n]) {
      case NUMBER:
	*(buflowp++)=format[n];
	*buflowp=0;
	break;
      case '-':
	state=3;
	break;
      default:
	if(append(buflow,buflow,assemble,argc,argv)) return 0;
	n--;
	state=0;
      }
      break;
    case 3:
      switch(format[n]) {
      case NUMBER:
	*(bufhighp++)=format[n];
	*bufhighp=0;
	break;
      default:
	if(append(buflow,bufhigh,assemble,argc,argv)) return 0;
	n--;
	state=0;
      }
      break;
    }
  }
  switch(state) {
  case 1:
    assemble+='%';
    break;
  case 2:
    if(append(buflow,buflow,assemble,argc,argv)) return 0;
    break;
  case 3:
    if(append(buflow,bufhigh,assemble,argc,argv)) return 0;
    break;
  }
  if(server==-1) {
    return (fparse_result=strdup(TT_StrF(TT_ARGS,"set ::internal::junk \"%s\"",assemble.c_str())));
  } else {
    return (fparse_result=strdup(TT_StrF(TT_ARGS,"namespace eval ::%d \"set ::internal::junk \\\"%q\\\"\"",server,assemble.c_str())));
  }
}

char *fparse(char *type, int server, int arguments, ...) {
  char *args[100];  // Make this dynamic.
  int argpos=0;
  int n;
  va_list ap;

  // Error kludge
  if(arguments>100) {
    fprintf(stderr,"fparse argument limit hit.  Contact author.\n");
    exit(1);
  }

  va_start(ap,arguments);
  for(n=0;n<arguments;n++) {
    args[argpos++]=va_arg(ap,char*);
  }
  va_end(ap);

  return(fparsev(type, server, arguments, args));
}

char *fgetformat(const char *type) {
  return hash_get(&formats,type);
}

char *fgetpathnames(const char *type, int server, int argc,
		    const char * const * argv) {
  char *location;
  char *newloc;
  twindow window;
  twindow *windowp;
  twindow *windowp2;
  tchan channel;
  tchan *channelp;
  int arg;

  // errors should be handled here via bgerror
  location=hash_get(&locations,type);
  if(!location) {
    // Replace this with an fparse'd or M_ error.
    TT_Eval(TT_ARGS,"error {fdisplay:  No location present for given display type.}");
    return 0;
  }
  if(server==-1) {
    if(!(newloc=strdup(TT_StrF(TT_ARGS,"set ::internal::junk \"%s\"",location)))) {
      // Replace this with an fparse'd or M_ error.
      TT_Eval(TT_ARGS,"error {fdisplay:  Out of memory.}");
      return 0;
    }
    windowp=0;  /* Avoids a warning message.  Make sure server!=-1 or
		   windowp!=0 before attempting to use the windowp pointer.
		   It will only be valid when called in a server manner. */
  } else {
    if(!(newloc=strdup(TT_StrF(TT_ARGS,"namespace eval ::%d \"set ::internal::junk \\\"%q\\\"\"",server,location)))) {
      // Replace this with an fparse'd or M_ error.
      TT_Eval(TT_ARGS,"error {fdisplay:  Out of memory.}");
      return 0;
    }
    if(!strcmp(newloc,"{}")) {
      free(newloc);
      if(!(newloc=strdup(""))) {
	// Replace this with an fparse'd or M_ error.
	TT_Eval(TT_ARGS,"error {fdisplay:  Out of memory.}");
	return 0;
      }
    }

    sprintf(window.pathname,".status%d",server);
    if(!(windowp=windows.find(window)) || !windowp->server) {
      free(newloc);
      TT_Eval(TT_ARGS,"error {fdisplay:  Invalid server index.}");
      return 0;
    }
  }

  if(!strncmp(newloc,"onchannel ",10)) {
    if((arg=atoi(newloc+10))>=argc) {
      free(newloc);
      TT_Eval(TT_ARGS,"error {fdisplay:  No argument present for location format argument number.}");
      return 0;
    }
    if(server==-1) {
      free(newloc);
      TT_Eval(TT_ARGS,"error {fdisplay:  Cannot run server specific format globally.}");
      return 0;
    }
    *newloc=0;
    strcpy(channel.name,argv[arg]);
    if((channelp=windowp->server->chanlist.find(channel))) {
      if(channelp->ison) {
	free(newloc);
	if(!(newloc=strdup(channelp->pathname))) {
	  TT_Eval(TT_ARGS,"error {fdisplay:  Out of memory.}");
	  return 0;
	}
      }
    }
  }
  if(!strncmp(newloc,"channelorstatus ",16)) {
    if((arg=atoi(newloc+16))>=argc) {
      free(newloc);
      TT_Eval(TT_ARGS,"error {fdisplay:  No argument present for location format argument number.}");
      return 0;
    }
    if(server==-1) {
      free(newloc);
      TT_Eval(TT_ARGS,"error {fdisplay:  Cannot run server specific format globally.}");
      return 0;
    }
    free(newloc);
    strcpy(channel.name,argv[arg]);
    if((channelp=windowp->server->chanlist.find(channel))) {
      if(!(newloc=strdup(channelp->pathname))) {
	TT_Eval(TT_ARGS,"error {fdisplay:  Out of memory.}");
	return 0;
      }
    } else {
      if(!(newloc=strdup(windowp->server->pathname))) {
	TT_Eval(TT_ARGS,"error {fdisplay:  Out of memory.}");
	return 0;
      }
    }
  }
  if(!strncmp(newloc,"chat ",5)) {
    if((arg=atoi(newloc+5))>=argc) {
      free(newloc);
      TT_Eval(TT_ARGS,"error {fdisplay:  No argument present for location format argument number.}");
      return 0;
    }
    if(server==-1) {
      free(newloc);
      TT_Eval(TT_ARGS,"error {fdisplay:  Cannot run server specific format globally.}");
      return 0;
    }
    free(newloc);
    
    windows.init_trav();
    while((windowp2=windows.trav())) {
      if(windowp2->server==windowp->server&&windowp2->dcc) {
	if((!strcasecmp(windowp2->dcc->getnick(),argv[arg]))||(!strcasecmp(windowp2->name,argv[arg]))) {
	  if(!(newloc=strdup(windowp2->pathname))) {
	    TT_Eval(TT_ARGS,"error {fdisplay:  Out of memory.}");
	    return 0;
	  }
	}
      }
    }
  }
  
  if(!strncmp(newloc,"pathname ",9)) {
    if((arg=atoi(newloc+9))>=argc) {
      free(newloc);
      TT_Eval(TT_ARGS,"error {fdisplay:  No argument present for location format argument number.}");
      return 0;
    }
    free(newloc);
    if(!(newloc=strdup(argv[arg]))) {
      TT_Eval(TT_ARGS,"error {fdisplay:  Out of memory.}");
      return 0;
    }
  }

  return newloc;
}

void fdisplayv(const char *type, int server, int argc,
	       const char * const * argv) {
  char *result;
  char *newloc;

  int listc;
  char **listv;
  int n;

  if(!(newloc = fgetpathnames(type,server,argc,argv))) return;

  if(!(result=fparsev(type,server,argc,argv))) {
    free(newloc);
    TT_Eval(TT_ARGS,"error {fparse:  Error parsing format (No argument present for format argument number?).}");
    return;
  }

  if(Tcl_SplitList(TT_Interp,newloc,&listc,(const char ***)&listv)==TCL_ERROR) {
    free(newloc);
    TT_Eval(TT_ARGS,"error {fdisplay:  Given pathname argument is not a proper TCL list.}");
    return;
  }
  for(n=0;n<listc;n++) {
    echo(result,listv[n],atoi(hash_get(&activate,type)));
  }
  Tcl_Free((char *)listv);
  free(newloc);
}

void fdisplay(char *type, int server, int arguments, ...) {
  char *args[100];  // Make this dynamic.
  int argpos=0;
  int n;
  va_list ap;

  // Error kludge
  if(arguments>100) {
    fprintf(stderr,"fparse argument limit hit.  Contact author.\n");
    exit(1);
  }

  va_start(ap,arguments);
  for(n=0;n<arguments;n++) {
    args[argpos++]=va_arg(ap,char*);
  }
  va_end(ap);

  fdisplayv(type, server, arguments, args);
}

int fexists(const char *type) {
  return (int)*hash_get(&formats,type);
}


syntax highlighted by Code2HTML, v. 0.9.1