#include <stdio.h>
#include <string.h>
#ifdef __GNUC__
#else
#include <unixio.h>
#endif
#include <file.h>
#include <ctype.h>
#include <stdlib.h>
#include <time.h>
#include <descrip.h>
#include <lib$routines.h>
#include "base_includes.h"
#include "qio.h"
#include "on.h"
#include "window_i.h"
#include "split.h"
#include "winfunc.h"
#include "system.h"
#include "ff.h"
#include "chan.h"
#include "dcc.h"
#include "builtin.h"
#include "mycompare.h"
#include "sock.h"
#include "window.h"
#include "alias.h"

extern int sys$setddir(); /* not in header files */
extern char userhost[], curr_time_s[];
extern server_ptr gsrv;

/* ---------------------------------------------------------------------- */
/* --------------------------------------------------------------------- */
/* This is where we handle all of the builtin functions.                 */
/* --------------------------------------------------------------------- */
struct command_node2 {
  char *name;
  void (*routine)();
};

/* ---------------------------------------------------------------------- */
extern long myaddress;
static void built_ip(char *str, char *ret, int len)
{
  sprintf(ret, "%u", tcp_htonl(myaddress));
}

/* ---------------------------------------------------------------------- */
static void built_index(char *str, char *ret, int len)
{
  char *word;
  int ind, l0, l1, good = -1;
  word = next_arg(&str);
  for (l0=0; (l0<strlen(str)) && (good == -1) ;l0++)
    for (l1=0;l1<strlen(word);l1++)
      if (tolower(word[l1]) == tolower(str[l0])) good = l0;
  (void)sprintf(ret, "%d\0", good);
}

/* ---------------------------------------------------------------------- */
static void built_rindex(char *str, char *ret, int len)
{
  int loop1, loop2, good = -1;
  char *word;
  word = next_arg(&str);
  for (loop1 = strlen(str)-1; (loop1>=0) && (good==-1); loop1--)
     for (loop2 = 0; loop2<strlen(word); loop2++)
       if (tolower(word[loop2]) == tolower(str[loop1])) good = loop1;
  (void)sprintf(ret, "%d\0", good);
}

/* ---------------------------------------------------------------------- */
static void built_ischannel(char *str, char *ret, int len)
{
  if (is_chan(str)) strcpy(ret, "1");
  else (void)strcpy(ret, "0");
}

/* ---------------------------------------------------------------------- */
static void built_ischanop(char *str, char *ret, int len)
{
  char *nick, *chan;
  nick = next_arg(&str);
  chan = next_arg(&str);
  if (gsrv) sprintf(ret, "%ld",is_chanop(get_server_chanlist(gsrv),chan,nick));
  else (void)strcpy(ret, "0");
}

/* ---------------------------------------------------------------------- */
static void built_userhost(char *str, char *ret, int len)
{
  (void)strcpy(ret, userhost);
}

/* ---------------------------------------------------------------------- */
static void built_left(char *str, char *ret, int len)
{
  int x;
  char *word;
  word = next_arg(&str);
  x = atoi(word);
  if (x<0)
    ret[0] = 0;
  else {
/* IF there aren't x characters, then it will copy up to and including NULL */
    (void)strncpy(ret, str, x);
    ret[x] = 0;
  }
}

/* ---------------------------------------------------------------------- */
static void built_right(char *str, char *ret, int len)
{
  char *word;
  int wlen, x;
  word = next_arg(&str);
  wlen = strlen(str);
  x = atoi(word);
  if (x>wlen) x = wlen;
  if (x < 0) *ret = 0;
  else strcpy(ret, &str[wlen-x]);
}

/* ---------------------------------------------------------------------- */
static void built_rand(char *str, char *ret, int len)
{
  int xx;
  xx = atoi(str);
  (void)sprintf(ret, "%d\0", rand()%xx);
}

/* ---------------------------------------------------------------------- */
static void built_tolower(char *str, char *ret, int len)
{
  while (*str) *ret++ = tolower(*str++);
  *ret = 0;
}

/* ---------------------------------------------------------------------- */
static void built_toupper(char *str, char *ret, int len)
{
  while (*str) *ret++ = toupper(*str++);
  *ret = 0;
}

/* ---------------------------------------------------------------------- */
static void built_mid(char *str, char *ret, int len)
{
  char *w1, *w2;
  int left, count, wlen;
  w1 = next_arg(&str);
  w2 = next_arg(&str);
  left = atoi(w1);
  count = atoi(w2);
  wlen = strlen(str);
  if ((left>=wlen) || (left<0) || (count<0)) *ret = 0;
  else {
    if ((left+count) > wlen) count = wlen-left;
    (void)strncpy(ret, &str[left], count);
    ret[count] = 0;
  }
}

/* ---------------------------------------------------------------------- */
static void built_match(char *str, char *ret, int len)
{
  char *pattern, *word;
  int xx = 1, good = 0;

  pattern = next_arg(&str);
  do {
    word = next_arg(&str);
    if (wild_match(pattern, word)!=-1) good = xx;
    xx++;
  } while ((good==0) && (*str));
  (void)sprintf(ret, "%d\0", good);
}

/* ---------------------------------------------------------------------- */
static void built_time(char *str, char *ret, int len)
{
  (void)sprintf(ret, "%d\0", time(0));
}

/* ---------------------------------------------------------------------- */
static void built_srand(char *str, char *ret, int len)
{
  unsigned some_time;
  some_time = time(NULL);
  srand(some_time);
  sprintf(ret, "%d", some_time);
}
/* ---------------------------------------------------------------------- */
static void built_stime(char *str, char *ret, int len)
{
  unsigned time;
  time = atoi(str);
  (void)sprintf(ret, "%s\0", ctime( (unsigned long *) (&time)) );
  ret[strlen(ret)-1] = 0;
}

/* ---------------------------------------------------------------------- */
static void built_strip(char *str, char *ret, int len)
{
  char *word;
  word = next_arg(&str);
  while (*str) {
    if (!strchr(word, *str)) *ret++ = *str;
    str++;
  }
  *ret = 0;
}

/* ---------------------------------------------------------------------- */
static void built_rmatch(char *str, char *ret, int len)
{
  char *word1, *word2;
  int count = 0, best_num, this, best = 0;
  best_num = -1;
  word1 = next_arg(&str);
  word2 = next_arg(&str);
  while (*word2) {
    count++;
    this = wild_match(word2, word1);
    if (this>best_num) {
      best_num = this;
      best = count;
    }
    word2 = next_arg(&str);
  }
  (void)sprintf(ret, "%d\0", best);
}

/* ---------------------------------------------------------------------- */
static void built_encode(char *str, char *ret, int len)
{
  while (*str) {
    *ret++ = (*str >> 4) + 'A';
    *ret++ = (*str & 0x0f) + 'A';
    str++;
  }
  *ret = 0;
}

/* ---------------------------------------------------------------------- */
static void built_decode(char *str, char *ret, int len)
{
  while (*str && *(str + 1)) {
    *ret++ = ((*str - 'A')  << 4) | (*(str+1) - 'A');
    str = (char *)((int)str + 2);
  }
  *ret = 0;
}

/* ---------------------------------------------------------------------- */
static void built_word(char *str, char *ret, int len)
{
  char *word, *start;
  word = next_arg(&str);
  start = find_word_num(str, atoi(word));
  while (!isspace(*start) && *start && (--len > 0))
    *ret++ = *start++;
  *ret = 0;
}

/* ---------------------------------------------------------------------- */
static void built_tdiff(char *str, char *ret, int len)
{
  int num, days, hours, minutes, seconds, time;
  time = atoi(str);
  days = time / (60 * 60 * 24);
  hours = (time / (60 * 60)) % 24;
  minutes = (time / 60) % 60;
  seconds = (time) % 60;
  *ret = 0;
  if (days>0) sprintf(ret, "%d days ", days);
  ret += strlen(ret);
  if (hours>0) sprintf(ret, "%d hours ", hours);
  ret += strlen(ret);
  if (minutes>0) sprintf(ret, "%d minutes ", minutes);
  ret += strlen(ret);
  sprintf(ret, "%d seconds", seconds);
}

/* ---------------------------------------------------------------------- */
int read_raw_exit(connect_node *node)
{
  char line[80];
  sprintf(line, "%d host C", node->iochan);
  handle_on(o_dcc_raw, line, 0);
  return 1;
}

/* ---------------------------------------------------------------------- */
int read_raw_connect(connect_node *node)
{
  char *walk, *back, line[MAXLEN];
  node->buffer[node->iosb.length] = 0;
  for (back = walk = node->buffer; *walk; walk++)
  {
    if (*walk == '\n') {
      *walk = 0;
      if ((walk > node->buffer) && (walk[-1]=='\015')) walk[-1] = 0;
      sprintf(line, "%d host D %s", node->iochan, back);
      if (!handle_on(o_dcc_raw, line, 0))
        say("+ %s", back);
      back = walk + 1;
    }
  }
  if (*back) say("+ %s", back);
  return 1;
}

/* ---------------------------------------------------------------------- */
static int lame_routine(short chan)
{
  char line[80];
  short nchan;
  SIN sin;
  nchan = tcp_accept_after_wait(chan, chan, &sin, sizeof(sin));
  sprintf(line, "%d host N %d", nchan, tcp_htons(sin.sin_port));
  handle_on(o_dcc_raw, line, 0);
  start_read(chan, nchan, read_raw_connect, read_raw_exit);
  return 1;
}

/* ---------------------------------------------------------------------- */
void built_write(char *str, char *ret, int len)
{
  int crlf, num, chan;
  char line[MAXLEN], *send;

  send = line;
  chan = atoi(next_arg(&str));
  crlf = atoi(next_arg(&str));
  if (crlf) sprintf(line, "%s\015\012", str);
  else send = str;
  num = tcp_send(chan, send, strlen(send));
  sprintf(ret, "%d", num);
}

/* ---------------------------------------------------------------------- */
static void built_connect_start(char *str, char *ret, int len)
{
  int chan;
  chan = atoi(str);
  start_read(chan, chan, read_raw_connect, read_raw_exit);
}
/* ---------------------------------------------------------------------- */
static void built_connect(char *str, char *ret, int len)
{
  extern int nslookup(char *host, char *ns, char *answer);
  char *serv, errmsg[80];
  short chan, port, wait;
  SIN sin, *new_sin;
  strcpy(ret, "-1 -1");
  serv = next_arg(&str);
  port = atoi(next_arg(&str));
  wait = atoi(next_arg(&str));
  memset(&sin, 0, sizeof(sin));
  if (!good_host((unsigned char *)&sin.sin_address, serv))
    if (!nslookup(getvar(VAR_NAMESERVER), serv, (char *)&sin.sin_address))
    {
      yell("*** could not resolve %s via [%s]\n", serv,
           getvar(VAR_NAMESERVER));
      return;
    }
  sin.sin_port = tcp_htons(port);
  sin.sin_family = AF_INET;
  chan = tcp_socket_and_connect(&sin, sizeof(sin));
  if (chan!=-1)
  {
    sprintf(ret, "%d", chan);
    new_sin = (SIN *)mymalloc(sizeof(sin));
    memcpy(new_sin, &sin, sizeof(sin));
    add_connect_to_list(chan, new_sin, NULL, CONNECT_RAW);
    if (!wait) start_read(chan, chan, read_raw_connect, read_raw_exit);
  } else {
    get_socket_error(errmsg);
    yell("qio_socket_and_connect: %s", errmsg);
  }
}

/* ---------------------------------------------------------------------- */
static void built_listen(char *str, char *ret, int len)
{
  SIN *sin;
  short chan;
  sin = tcp_bind_and_listen(&chan, lame_routine);
  if (sin) {
    sprintf(ret, "%d", tcp_htons(sin->sin_port));
    add_connect_to_list(chan, sin, NULL, CONNECT_RAW);
  }
  else strcpy(ret, "-1");
}

/* ---------------------------------------------------------------------- */
static void built_fopenw(char *str, char *ret, int len)
{
  char *tmp;
  tmp = next_arg(&str);
  if (atoi(str))
    sprintf(ret, "%d", open(tmp, O_WRONLY|O_CREAT|O_APPEND, 0700));
  else
    sprintf(ret, "%d", open(tmp, O_WRONLY|O_CREAT|O_TRUNC, 0700));
}

/* ---------------------------------------------------------------------- */
static void built_fopenr(char *str, char *ret, int len)
{
  sprintf(ret, "%d", open(str, O_RDONLY, 0700));
}

/* ---------------------------------------------------------------------- */
static void built_fclose(char *str, char *ret, int len)
{
  sprintf(ret, "%d", close(atoi(str)));
}

/* ---------------------------------------------------------------------- */
static void built_fread(char *str, char *ret, int len)
{
  char *word;
  int crlf;
  word = next_arg(&str);
  sprintf(ret, "%d", read(atoi(word), ret, 512));
}

/* ---------------------------------------------------------------------- */
static void built_fwrite(char *str, char *ret, int len)
{
  char *word;
  int crlf;
  word = next_arg(&str);
  crlf = atoi(next_arg(&str));
  sprintf(ret, "%d", write(atoi(word), str, strlen(str)));
  if (crlf) write(atoi(word), "\n", strlen("\n"));
}

/* ---------------------------------------------------------------------- */
static void built_mychannels(char *str, char *ret, int len)
{
  win_ptr win;
  server_ptr tsrv = gsrv;
  if (*str) {
    win = get_win_by_name(str);
    if (win) tsrv = win->server;
    else tsrv = NULL;
  }
  get_channels_from_server(tsrv, ret, len);
}

/* ---------------------------------------------------------------------- */
static void built_currtime(char *str, char *ret, int len)
{
  real_time();
  sprintf(ret, "%s", curr_time_s);
}

/* ---------------------------------------------------------------------- */
static void built_logdcc(char *str, char *ret, int len)
{
  SIN *sin;
  char *file, *send;
  short chan;
  int flags;
  file = next_arg(&str);
  send = next_arg(&str);
  if (atoi(send)) flags = DCC_SEND|DCC_FILE;
  else flags = DCC_GET|DCC_FILE;
  sin = dcc_bind(&chan);
  if (sin)
    if (logdcc_request(0, 0, "FTP", file, flags, sin, chan))
      sprintf(ret, "%d", tcp_htons(sin->sin_port));
    else strcpy(ret, "0");
  else strcpy(ret, "0");  
}

/* ---------------------------------------------------------------------- */
static void built_getalias(char *str, char *ret, int len)
{
  char *exp = get_alias_text(str, TRUE);
  if (!exp) *ret = 0;
  else {
    strncpy(ret, exp, len-1);
    ret[len-1] = 0;
  }
}

/* ---------------------------------------------------------------------- */
static void built_menu_choice(char *str, char *ret, int len)
{
  int num_words, ind, ret_ind = -1, loop;
  char **word_list;
  num_words = word_count(str);
  word_list = (char **)mymalloc(num_words * sizeof(char *));
  if (word_list)
  {
    for (loop=0; loop<num_words; loop++)
      word_list[loop] = next_arg(&str);
    ret_ind = get_menu_choice(num_words, word_list);
  }
  sprintf(ret, "%d", ret_ind);
}
/* ---------------------------------------------------------------------- */
static void built_winnum(char *str, char *ret, int len)
{
  sl_window2(get_curr_win(), ret);
}
/* ---------------------------------------------------------------------- */
static void built_cwd(char *str, char *ret, int len)
{
  char werd[512];
  int status, mlen, prefix, context = 0;
  struct dsc$descriptor_s old_d, new_d;
  new_d.dsc$b_dtype = old_d.dsc$b_dtype = DSC$K_DTYPE_T;
  new_d.dsc$b_class = old_d.dsc$b_class = DSC$K_CLASS_S;
  if (str && *str)
  {
    new_d.dsc$w_length = sizeof(werd);
    new_d.dsc$a_pointer = werd;
    old_d.dsc$w_length = strlen(str);
    old_d.dsc$a_pointer = str;
    status = lib$find_file(&old_d, &new_d, &context);

    str = strrchr(werd, ']');
    if (!str) str = strrchr(werd, '>');
    if (str) str[1] = 0;

    str = strchr(werd, ' ');
    if (str) *str = 0;

    str = strchr(werd, ':');
    if (str) *str++ = 0;
    if (str)
    {
      new_d.dsc$a_pointer = werd;
      new_d.dsc$w_length = strlen(werd);
      lib$set_logical("SYS$DISK", &new_d);
      new_d.dsc$a_pointer = str;
      new_d.dsc$w_length = strlen(str);
      status = sys$setddir(&new_d, NULL, NULL);
    }
  }
  get_logical("SYS$DISK", ret, FALSE);
  prefix = strlen(ret);
  len -= prefix;
  old_d.dsc$w_length = len;
  old_d.dsc$a_pointer = &ret[strlen(ret)];
  status = sys$setddir(0, &len, &old_d);
  if (status & 1)
    ret[len+prefix] = 0;
  else
    strcpy(ret, "ERROR");
}
/* ---------------------------------------------------------------------- */
static struct command_node2 in_functions[] =
{
  {{"ALIAS"},         built_getalias},
  {{"CONNECT"},       built_connect},
  {{"CONNECT_START"}, built_connect_start},
  {{"CURRTIME"},      built_currtime},
  {{"CWD"},           built_cwd},
  {{"DECODE"},        built_decode},
  {{"ENCODE"},        built_encode},
  {{"FCLOSE"},        built_fclose},
  {{"FOPEN_READ"},    built_fopenr},
  {{"FOPEN_WRITE"},   built_fopenw},
  {{"FREAD"},         built_fread},
  {{"FWRITE"},        built_fwrite},
  {{"INDEX"},         built_index},
  {{"IP"},            built_ip},
  {{"ISCHANNEL"},     built_ischannel},
  {{"ISCHANOP"},      built_ischanop},
  {{"LEFT"},          built_left},
  {{"LISTEN"},        built_listen},
  {{"LOGDCC"},        built_logdcc},
  {{"MATCH"},         built_match},
/*  {{"MENU_CHOICE"},   built_menu_choice}, */
  {{"MID"},           built_mid},
  {{"MYCHANNELS"},    built_mychannels},
  {{"RAND"},          built_rand},
  {{"RIGHT"},         built_right},
  {{"RINDEX"},        built_rindex},
  {{"RMATCH"},        built_rmatch},
  {{"SRAND"},         built_srand},
  {{"STIME"},         built_stime},
  {{"STRIP"},         built_strip},
  {{"TDIFF"},         built_tdiff},
  {{"TIME"},          built_time},
  {{"TOLOWER"},       built_tolower},
  {{"TOUPPER"},       built_toupper},
  {{"USERHOST"},      built_userhost},
/*
  {{"WINNAME"},       built_winname},
*/
  {{"WINNUM"},        built_winnum},
  {{"WORD"},          built_word},
  {{"WRITE"},         built_write},
  {{NULL},            NULL}
};

/* ------------------------------------------------------------------------ */
int is_builtin(char *func, char *args, char *ret, int len)
{
  char dummy[MAXLEN];
  int ddd,indx;
  ddd = lookup_command((command_node *)in_functions, func, &indx);
  if (ddd<0) return 0;
  (in_functions[ddd].routine)(args, ret, len);
  return 1;
}
/* ------------------------------------------------------------------------ */
