/* Dialog Manager */
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <string.h>
#include <ctype.h>
#include <malloc.h>

#include <pconf.h>
#include <parse.h>
#include "dm.h"

char *next_line(char *p);
char *is_child( aFrame *frame, char *slot, char *child );
char *is_relevant( aFrame *frame, char *slot, char *child );
char *get_best_parse(char *parse_str);
void strip_blanks(char *s);
void flatten(char *s);

extern char *dir;
extern char *script_file;
extern char *config_file;
extern char *mode;

char task_file[LINE_LEN]=	"task";

// command line parm defaults
int   MaxParses          = 10;
int   MaxPrompt          = 10;
int   OutBufSize         = 10000;

// This was both in this and in parse.cc and is larger in there
// so keeping that number
extern int SymBufSize;

// Global context variables
aFrame	*context[MAX_FRAME_STACK];
aFrame	*extracts[MAX_FRAME_STACK];
aFrame	*history[MAX_FRAME_STACK];

aFrame *focus;		// pointer to most recent frame

int	hist_idx;
Prompt  prompted;	// frame, slot and index of slot prompted for
char	*prompt;
char	prompt_str[LINE_LEN];
int	verify_prompted= 0;

char	query_key[LINE_LEN];	// specific query for db

//int	ins_ctr = 0;
int	hangup = 0;


// structures

aFrameDef	*ontology;  // Array of all the frame definitions
int		num_forms;  // Number of ontology elements there are

DateTime	today;
DateTime	ref_date;
DateTime	dt;

char	**dm_parses;
char	line[LINE_LEN];

// method: extract_values
//
// arguments:
//  char *parse_string: (input) 
//
// return: int; 
//
// extract values in parse string to temporary frames
int extract_values(char *parse_string) {
  char	frame_name[LABEL_LEN];
  char  val[LINE_LEN];
  char  key[LINE_LEN];
  char  key_val[LINE_LEN];
  char  line[LINE_LEN];
  int	i, cnt;
  char	*p;
  char 	*kv;
  char	*full_key;
  char	**key_ptr;
  char	*vv;
  char	*ps;
  aFrame	*frame;
  aFrameDef	*fd;
  int		xx;
  
  
  /* process each line in parse */
  /* map strings to canonical form, create frame and fill in from parse */
  for (ps = parse_string; sscanf(ps, "%[^\n]", line) > 0;
       ps= next_line(ps)) {
    
    if( !line[0] ) continue;
    
    /* get frame name, and key-val string */
    frame_name[0]= (char)0;
    key_val[0]= (char)0;

    //break the line into everything left of colon, and everything right
    //of colon, UP TO BUT NOT INCLUDING the newline
    if( sscanf(line, "%[^:]:%[^\n]", frame_name, key_val) < 1 ) break;
    
    // if no root net, create an empty frame and move ptr to start
    // of next line
    //    	if( *key_val != '[' ) continue;
    if( *key_val != '[' ) {
      if( !(frame= getFrame(extracts, frame_name, "last", &xx)) ) {
	if( (frame= createFrame(frame_name)) ) {
	  addFrame(extracts, frame);
	}
      }
      continue;
    }
    
    /* get temporary frame */
    if( !(frame= getFrame(extracts, frame_name, "last", &xx)) ) {
      // impossible for createFrame to return false value
      // program will exit w/ -1 if it has an error
      //
      if( !(frame= createFrame(frame_name)) ) return(-1);
      addFrame(extracts, frame);
    }
    
    /* apply transform functions */
    for( p=key_val;  (p= index(p, '[')); p++ )
      {
	//mwe debug
	//printf("==BEGIN==Entire key-val: ^^%s^^\n",key_val);
	//printf("Current 'p': ^^%s^^\n",p);
	check_transform(p); 
	//printf("Ending key-val: ^^%s^^\n",key_val);
      }
    
    /* flatten the representation */
    flatten(key_val);
    full_key= (char *)0;
    /* for each  key - value */
    for( kv= key_val; sscanf(kv, "%[^\n]", line) >0; kv= next_line(kv) ){
      
      /* extract key and value from line of parse */
      if( !(p= rindex( line, (int) ']')) ) break;
      p++;
      if( *p != '.' ) break;
      *p= ':';
      key[0]= val[0]= (char)0;
      cnt= sscanf(line, "%[^:]:%[^\n]", key, val);
      strip_blanks(val);
      
      /* resolve under-specified keys */
      if( !(key_ptr= getKeyPtr(frame, key)) ) {
	
	/* if responsive to prompted key */
	if( prompted.key && strstr(prompted.key, key) ) {
	  full_key= prompted.key;
	}
	else if( prompted.key &&
		 (full_key= is_child(frame,prompted.key, key)) ) {
	}
	else if( prompted.key &&
		 (full_key= is_relevant(frame,prompted.key, key)) ) {
	}
	else {
	  Slot *s;
	  if( !(fd= getFrameDef(frame->name)) ) return(-1);
	  
	  /* use first relevant slot with required flag */
	  for(i=0; i < fd->n_slot; i++ ) {
	    s= (fd->slot)+i;
	    if( s->key && strstr(s->key, key) && 
		(s->required == '+' || s->required == '*') ) 
	      break;
	  }
	  
	  /* use first relevant slot */
	  if (i >= fd->n_slot) {
	    for(i=0; i < fd->n_slot; i++ ) {
	      s= (fd->slot)+i;
	      if( s->key && strstr(s->key, key) )
		break;
	    }
	  }
	  
	  if( i < fd->n_slot ) { full_key= s->key; }
	  /* if no relevant slot in frame */
	  else { full_key= key; }
	}
      }
      else full_key= key;
      
      /* whw - for now, don't overwrite or start new leg */
      if( (vv= getVal(frame, full_key)) ) continue;
      
      /* execute a function associated with slot */
      check_function(frame_name, full_key,  val);
      
      /* fill slot in frame */
      if( val[0] && frame ) setKey(frame, full_key, val);
      
    }
  }
  return(0);
}

// method: init_dm
//
// arguments:
//  char *dir: (input)
//
// return: none.
//
void init_dm(char *dir) {
  /* read ontology */
  read_task(dir, task_file);
  
  /* set ref dates */
  set_today(&today);
  ref_date= today;
  set_ref_date(&ref_date);
  printf("Today is %d-%d-%d\n", ref_date.month, ref_date.day, ref_date.year);
  
  /* read script */
  //if(script_file) read_script(dir, script_file);
  
  /* malloc space for parse pointers */
  if(!(dm_parses=(char **) malloc( MaxParses * sizeof(char *)))){
    fprintf(stderr, "ERROR: can't allocate space for parses\n");
    exit(-1);
  }
  
  reset_dm();
}

// method: reset_dm
//
// arguments:
//  none.
//
// return: none.
//
void reset_dm() {
  
  //hangup= 0;
  focus= (aFrame *)0;
  set_prompted((aFrame *)0, (char *)0, (char *)0);
  query_key[0]= 0;
  
  /* set reference date to today */
  ref_date= today;
  set_ref_date(&ref_date);
  
  /* free all frames */
  clear_all_frames();
  hist_idx= 0;
  
}


// method: init_dm
//
// arguments:
//  char *str: (input)
//  char *tok: (input)
//  char *val: (input)
//
// return: none.
//
void get_leaf_val(char *str, char *tok, char *val) {
  char *c;
  char *t;

  // Error check
  if (!str || !tok || !val) return;
  
  // find first token
  if( !(t= index(str,'[')) ) return;
  
  // find leaf string
  for(c= t; (c= index(t,'.'));  ) {
    c++;
    if( *c != '[' ) break;
    t= c;
  }
  if( !c ) return;
  
  // extract value
  sscanf(c, "%[^[\n]", val);
  // strip trailing blanks
  for(c = val+ strlen(val)-1; isspace((int) *c); c--) *c=0;
  
  // copy token name
  sscanf(t, "%[^.]", tok);
}

// method: get_token
//
// arguments:
//  char *str: (input)
//  char *tok: (output)
//
// return: int; 0 on success, -1 on error
//
/* copy next token string into tok */
int get_token(char *str, char *tok) {
  char *c;
  char *t;

  // Error check
  if (!str || !tok) return (-1);
  
  // find first token
  if( !(t= index(str,'[')) ) return(-1);
  
  for(c= tok; *t && (*t != ']'); t++) *c++ = *t;
  *c++= ']';
  *c=0; 
  return(0);
}

// method: cp_str
//
// arguments:
//  char **to: (output)
//  char *from: (input)
//
// return: none.
//
void cp_str(char **to, char *from) {
  char *s;
  
  if( !to ) return;
  
  // free old string
  if( *to ) free(*to);
  
  if( !from ) {
    *to= (char *)0;
    return;
  }
  
  /* malloc space for new token */
  if( !(s=(char *)malloc(strlen(from)+1 ))) {
    fprintf(stderr,"ERROR: can't allocate space for string\n");
    exit(-1);
  }
  
  *to= s;
  strcpy(s, from);
}



// method: fill_prompted
//
// arguments:
//  char *str: (input)
//
// return: none.
//
/* Assign string as key value in frame */
void fill_prompted(char *str) {
  if( !prompted.frame || !prompted.key ) return;
  
  setKey(prompted.frame, prompted.key, str);
}


// method: next_line
//
// arguments:
//  char *p: (input)
//
// return: char* that is >= input p
//
// Uses no global variables
// Give a string *p, will go character by character forward
// while the next character is not a '\n' or '\0'.
// If the stopping character is a '\n', will go one more to
// be positioned at beginning of new line
char *next_line(char *p) {
  for( ; *p && (*p != '\n' ); p++);
  if( *p == '\n') p++;
  return(p);
}


// method: strip_blanks
//
// arguments:
//  char *s: (input)
//
// return: char* that is >= input p
//
/* strip trailing whitespace */
void strip_blanks(char *s) {
  char *p;
  
  for(p= s + (strlen(s)-1); *p && isspace((int)*p); p--);
  *(p+1)= (char)0;
}

/* separate multiple values on single line to separate lines */
/*mwe
  This function is not named very well. Given something like this:
  =begin====
  Query_List:[Query_List].query_list [Arrive_Loc].[Place_Type].GAS STATIONS 
  [Arrive_Near].[Place_Name].[Supermarket_Name].WALMART 
  =end====
  with everything on a single line, will place on different lines such as
  =begin===
  [Query_List].query_list
  [Arrive_Loc].[Place_Type].GAS STATIONS 
  [Arrive_Near].[Place_Name].[Supermarket_Name].WALMART
  =end===
  
  asdasd
*/

// method: flatten
//
// arguments:
//  char *s: (input\output)
//
// return: none.
//
void flatten(char *s) {
  char *p;
  char *f;
  char *t;
  char *h;
  char inbuf[LINE_LEN];
  char outbuf[LINE_LEN];
  char head[LABEL_LEN];
  
  outbuf[0]= (char)0;
  /* process each line in buffer*/
  for(p=s; *p && sscanf(p, "%[^\n]", inbuf) > 0;  ) {
    /* get head token */
    head[0]= (char)0;
    if( !(h= index(inbuf,(int)'[')) ) continue;
    if( sscanf(h, "%[^.]", head) < 1 ) continue;
    if( !(t= index(h,(int)'.')) ) continue;
    t++;
    
    /* if no head token */
    {
      char *c;
      int cnt;
      
      cnt= 0;
      for(c=t; *c && !isspace((int)*c); c++) {
	if( *c == '.' ) cnt++;
      }
      /* no head token */
      if( !cnt ) {
	t= h;
	head[0]= (char)0;
      }
    }
    
    /* while another token on line */
    while( (f= index(t,(int)' ')) ) {
      
      /* find start of next token */
      if( !(f= index(f,(int)'[')) ) break;
      *(f-1)=0;
      
      if( head[0] ) {
	strcat(outbuf, head);
	strcat(outbuf, ".");
      }
      strcat(outbuf, t);
      strcat(outbuf, "\n");
      t=f;
    }
    if( head[0] ) {
      strcat(outbuf, head);
      strcat(outbuf, ".");
    }
    strcat(outbuf, t);
    strcat(outbuf, "\n");
    
    if( !(p=index(p,(int)'\n'))) break;
    p++;
  }
  if(outbuf[0]) strcpy(s, outbuf);
}

// method: scmp
//
// arguments:
//  char *s1: (input)
//  char *s2: (input)
//
// return: int; 1 if s1 or s2 is null. 0 if they are the same, otherwise same
//         output as strncmp() would return [-1, 0, 1]
//
int scmp(char *s1, char *s2) {
  if( !s1 || !s2 ) return( 1 );
  return( strncmp(s1, s2, strlen(s1)) );
}

// method: get_best_parse
//
// arguments:
//  char *parse_str: (input)
//
// return: char*
//
/* find best parse in buffer */
char *get_best_parse(char *parse_str) {
  char *s,
    *best;
  char token[100];
  int  num_parses;
  int	 score, best_score;
  int	i, j;
  
  best= (char *)0;
  best_score= 0;
  for(s= parse_str, num_parses= 0; *s; num_parses++) {
    score= 0;
    /* find start of next parse */
    if( !(s= strstr(s, "PARSE")) ) break;
    if( !(s= index(s, (int) '\n')) ) break;
    if( !*(++s)  ) break;
    dm_parses[num_parses]= s;
    
    /* find end of parse */
    if( !(s= strstr(s, "END_PARSE")) ) break;
    *s++= (char)0;
    
    /* see if responsive to prompt */
    if( prompted.frame && prompted.key ) {
      sprintf(token, "%s:%s", prompted.frame->name, prompted.key);
      if( strstr(dm_parses[num_parses], token) )
	score += 100;
      else if(strstr(dm_parses[num_parses],prompted.frame->name))
	score +=75;
    }
    
    /* see if same frame in history window */
    if( !score ) {
      for( i=1; i < 4; i++) {
	if( (j= hist_idx-i) < 0) j= MAX_FRAME_STACK-1;
	if( !history[j] ) break;
	if( strstr(dm_parses[num_parses], history[j]->name) ) {
	  score += 10;
	}
      }
    }
    
    if( !best || (score > best_score)) {
      best= dm_parses[num_parses];
      best_score= score;
    }
    
    if( !(s= index(s, (int) '\n')) ) break;
  }
  //mwe
  printf("Found %i parses, the best one has value %i and is:::\n%s\n",
	 num_parses,best_score,best);
  return(best);
}
