#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "global.h"
#include "constants.h"
#include "window.h"
#include "types.h"
#include "debug.h"

extern char g_sent[MAXNICKLEN], g_from[MAXNICKLEN], g_body[MAXLEN];
extern char g_public[MAXNICKLEN], g_join[MAXNICKLEN];
extern char curr_time[30];
extern server_ptr gsrv;
extern win_ptr gwin;

/* ------------------------------------------------------------------- */

assign_ptr alias_list = NULL, assign_list = NULL;

struct builtin_struct {
  char ack;
  void (*routine)();
};

void curr_nick(char *str)
  { sl_nick(gwin, str); }
void curr_query(char *str)
  { (void)getquery(str); }
void curr_chan_query(char *str)
  { if (!getquery(str)) (void)get_channel(str); }
void curr_commandchar(char *str)
  { (void)strcpy(str, "/"); }
void curr_get_channel(char *str)
  { (void)get_channel(str); }
void curr_server(char *str)
  { if (gsrv) strcpy(str, gsrv->server_name); else str[0] = 0; }
void curr_body(char *str)
  { (void) strcpy(str, g_body); }
void curr_sent(char *str)
  { (void) strcpy(str, g_sent); }
void curr_from(char *str)
  { (void) strcpy(str, g_from); }
void curr_join(char *str)
  { (void) strcpy(str, g_join); }
void curr_public(char *str)
  { (void) strcpy(str, g_public); }
void curr_ztime(char *str)
  { (void) strcpy(str, curr_time); }

struct builtin_struct builtins[] = {
  {',', curr_from         },
  {'.', curr_sent         },
  {':', curr_join         },
  {';', curr_public       },
  {'B', curr_body         },
  {'C', curr_get_channel  },
  {'K', curr_commandchar  },
  {'N', curr_nick         },
  {'Q', curr_query        },
  {'S', curr_server       },
  {'T', curr_chan_query   },
  {'Z', curr_ztime        },
  { 0,  NULL              }
};

int check_builtins(char ch, char *str)
{
  int loop = 0;
  while (builtins[loop].ack) {
    if (builtins[loop].ack == ch) {
      (void) builtins[loop].routine(str);
      return TRUE;
    }
    loop++;
  }
  return FALSE;
}

/* ------------------------------------------------------------------- */
/* Assumes string is big enough to hold the entire string..            */
/* ------------------------------------------------------------------- */

void replace(str, token, len_token, replace)
  char *str, *token;
  int len_token;
  char *replace;
{
  char *temp, end[MAXLEN], msg[MAXLEN];
  int len, size;

  len = strlen(str);
  temp = strstr(str, token);
  if (!temp) return;
  size = len-(temp-str)-len_token;
  (void)strncpy(end, temp+len_token, size);
  end[size]=0;
  (void)sprintf(temp, "%s", replace);
  temp += strlen(replace);
  (void)sprintf(temp, "%s\0", end);
}

/* ------------------------------------------------------------------- */

extern int debug;
int alias_depth = 0;

int exec_alias(char *word, char *string, char *params)
{
  assign_ptr temp;
  int res, added = 1;
  char expanded[MAXLEN*10], *stemp;

  if (alias_depth++>get_intvariable("MAX_RECURSIONS")) {
    alias_depth--;
    say("*** Recursion too deep!");
    return FALSE;
  }
  while (*string==' ') string++;
  for (temp = alias_list; temp; temp = temp->next) {
    if ((res = strcasecmp(temp->word, word)) == 0) 
    {
/*
      expand_alias(string, params, &added, expanded, sizeof(expanded));
      if (debug & DEBUG$EXPAND)
        yell("Expanded params: [%s] (%s) to [%s]", string,
             params?params:"null", expanded);
      parse_line(temp->value, expanded, TRUE, FALSE);
*/
      parse_line(temp->value, string, TRUE, FALSE);
      alias_depth--;
      return TRUE;
    }
    else if (res == 1) break;
  }
  alias_depth--;
  return FALSE;
}

/* ------------------------------------------------------------------- */

void delete_fromlist(list, key)
  assign_ptr *list;
  char *key;
{
  assign_ptr temp, f, b = NULL;
  f = *list;
  while (f)
    if (strcasecmp(f->word, key) == 0) {
      temp = f;
      if (!b) *list = f->next;
      else b->next = f->next;
      f = f->next;
      free(temp->word);
      free(temp->value);
      free(temp);
    } else {
      b = f;
      f = f->next;
    }
}

/* ------------------------------------------------------------------- */
char *bad_chars = "!@#$%^&*(){}[]-=+\";|\\?/,";

int check_var(char *key)
{
  char *loc, *loc2;
  loc = strchr(key, '[');
  loc2 = strchr(key, ']');
  if (loc && (loc2>loc)) {
   *loc = '.';
   *loc2 = 0;
  }
  if (strpbrk(key, bad_chars)) {
    say("*** %s is an invalid variable name.", key);
    return FALSE;
  }
  return TRUE;
}

/* ------------------------------------------------------------------- */

/* if (first == TRUE) then this was called by /assign else by /@ */
void raw_list(char *string, int first, assign_ptr *list, char *mess)
{
  char *ack;
  char strip[MAXLEN], *key, msg[MAXLEN], word[MAXLEN*4];
  assign_ptr temp, back, walk;

  ack = string;
  key = m_grab_word(&ack, ' ');
  if (key && (*key == '-')) {
    delete_fromlist(list, &key[1]);
    return;
  }
  if (!check_var(key)) return;

  if (*ack=='{') {
    (void)strip_ends(ack, '{', '}', word, sizeof(word));
    strcpy(ack, word);
  }

  if ((!*ack) && first) {
    say("*** %s's:", mess);
    for (walk = *list; walk; walk = walk->next)
      if (strncasecmp(walk->word, key, strlen(key)) == 0)
        say("*** %-15s %s", walk->word, walk->value);
    free(key);
  } else {
    delete_fromlist(list, key);
    temp = (assign_ptr)malloc(sizeof(struct assign));
    if (!temp) return;
    temp->word = key;
    temp->value = ms_strcpy(ack);
    back = NULL;
    walk = *list;
    while (walk && (strcasecmp(walk->word, key) < 0)) {
      back = walk;
      walk = walk->next;
    }
    if (!back) *list = temp;
    else back->next = temp;
    temp->next = walk;
    say("%s %s added", mess, key);
  }
}

void user_assign(char *str, int first)
{
  raw_list(str, first, &assign_list, "Assign");
}

void user_alias(char *str)
{
  raw_list(str, TRUE, &alias_list, "Alias");
}

void lookup_assign_value(char *str, char *ret, int len)
{
  assign_ptr walk;
  int max;
  for (walk = assign_list; walk; walk = walk->next) {
    if (strcasecmp(walk->word, str) == 0) {
      max = (len<strlen(walk->value))?len:strlen(walk->value);
      strncpy(ret, walk->value, max);
      ret[max] = 0;
      return;
    }
  }
  *ret = 0;
}

assign_ptr lookup_assign(char *str)
{
  assign_ptr walk;
  for (walk = assign_list; walk; walk = walk->next) {
    if (strcasecmp(walk->word, str) == 0){
      return walk;
    }
  }
  return (assign_ptr)NULL;
}

int assign_matches(char *str, char *ret, int max)
{
  assign_ptr walk;
  *ret = 0;
  for (walk = assign_list; walk; walk = walk->next)
    if (strncasecmp(walk->word, str, strlen(str)) == 0)
    {
      if (max--) strcat(ret, " ");
      if (max>strlen(walk->word)) strcat(ret, walk->word);
      max -= strlen(walk->word);
    }
  return (*ret);
}
