#include <stdio.h>
#include <iodef.h>
#include <brkdef.h>
#include <descrip.h>
#include <errno.h>
#include <ssdef.h>
#include <string.h>
#include <stdlib.h>
#include "global.h"
#include "constants.h"
#include "window.h"
#include "defines.h"
#include "debug.h"

#define lf '\012'                    /* Terminator for a line in irc */

unsigned short port = DEF_PORT;      /* The standard IRC port. */

extern int display, history, sys$setast(), sys$clref(),
       sys$cantim(), lib$get_ef(), sys$setimr(), sys$bintim(), debug;
extern char nameserver[];
extern unsigned long myaddress;
extern win_ptr awins;
extern int setup_qio_hooks(char *);

int quit = FALSE;
char ast_wait[] = "0000 00:00:00.30";
$DESCRIPTOR(ast_wait_d, ast_wait);
unsigned ast_time[2];
void irc_receive();
char def_nick[MAXNICKLEN];
FILE *log;

server_ptr server_list = NULL;
extern server_ptr gsrv;
extern int print_window();
void process_receive();

/* ------------------------------------------------------------------------ */
void clear_redirect(server_ptr srv)
{
  if (srv && srv->redirect) {
    free(srv->redirect);
    srv->redirect = NULL;
  }
}

/* ------------------------------------------------------------------------ */
void set_redirect(char *str, server_ptr srv)
{
  if (!srv) return;
  if (srv->redirect) free(srv->redirect);
  srv->redirect = ms_strcpy(str);
}

/* ------------------------------------------------------------------------ */
/* Send a message to the screen -this routine determines which window       */

void new_printmess(server, type, win, format, p1, p2, p3, p4, p5, p6, p7, p8, p9,p10)
  server_ptr server;
  int type;
  win_ptr win;
  char *format, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8, *p9, *p10;
{
  int old;
  char string[MAXLEN*100];
  if (display<=0) return;
  old = sys$setast(0);
  (void)sprintf(string, format, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10);

  if (win == NW)  win = type_winget(server, type);
  (void)print_window(win, string, type);
  if (old == SS$_WASSET) {
    (void)sys$setast(1);
  }
}

/* ------------------------------------------------------------------------ */
server_ptr lookup_server(char *server, char *nick)
{
  server_ptr walk;
  for (walk=server_list;walk;walk=walk->next) {
    if ((strcasecmp(server, walk->server_name)==0) &&
        (!nick || (strcasecmp(nick, walk->nick) == 0)))
      return walk;
  }
  return NULL;
}

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

int scan_server_list(short chan)
{
  server_ptr temp;
  unsigned status;

  temp = server_list;
  while (temp) {
    if (sys$clref(temp->event_flag) == SS$_WASSET)
      if (temp->connected) 
      {
        status = sys$cantim(temp->reqidt, 0);
        if (!ODD(status)) my_sig(status, "cantim in sock");
        process_receive(temp);
      }
    temp = temp->next;
  }
  return 1;
}

/* ------------------------------------------------------------------------ */
extern unsigned lib$free_ef();
/* ------------------------------------------------------------------------ */
void remove_server(server_ptr srv)
{
  lib$free_ef(&srv->event_flag);
  new_send(srv, "QUIT :No more windows left!\n");
  srv->connected = FALSE;
}

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

int *add_server(server, port, pass, nick, iochan, oldserv)
  char *server, *pass, *nick;
  unsigned short iochan, port;
  server_ptr oldserv;
{
  unsigned status;
  win_ptr walk;
  int index, good = -1;
  int hid = 10922;
  server_ptr temp;

  if (server_list) hid = server_list->reqidt+1;

  temp = (server_ptr)malloc(sizeof(struct server));
  status = lib$get_ef(&(temp->event_flag));
  if (!status) {
    free(temp);
    return NULL;
  }

  temp->iochan = iochan;
  (void)sys$clref(temp->event_flag);
  temp->iosb.status = 0;
  temp->iosb.length = 0;
  (void)strcpy(temp->server_name, server);
  temp->redirect = NULL;
  temp->buffer[0] = 0;
  temp->start[0] = 0;
  temp->reqidt = hid;
  temp->connected = TRUE;
  (void)strcpy(temp->nick, nick);
  temp->away = FALSE;
  temp->chan_list = NULL;
  temp->notify_list = NULL;
  temp->last_notify = NULL;
  temp->next = server_list;
  server_list = temp;

/* Code to kill the old server off */
  if (oldserv) {
    oldserv->connected = FALSE;
    tcp_shutdown(oldserv->iochan);
    for (walk=awins; walk; walk = walk->next) {
      if (walk->server == oldserv)
        walk->server = temp;
    }
  }
/* End killing old server */

  (void)startup(temp);
  return (int *)temp;
}

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

void start_receives(num)
  unsigned num;
{
  server_ptr temp;
  temp = server_list;
  while (temp) {
    if (num == temp->reqidt) irc_receive(temp);
    temp = temp->next;
  }
}

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

void process_receive(temp)
  server_ptr temp;
{
  int len;
  char line[MAXLEN];
  char *dummy, *walk, *sline;
  unsigned status;

  if (quit) return;
  if ((int)temp->iosb.length > 0)
  {
    temp->buffer[(int)temp->iosb.length] = 0;
    (void)strcat(temp->start, temp->buffer);
    dummy = temp->start;
    do {
      sline = line;
      for (walk=dummy, len = 0; (*walk) && (*walk!=lf); len++)
        *sline++ = *walk++;
      if (*walk==lf) {
        dummy = ++walk;
        *sline = 0;
        if (line[len-1] == 13) line[len-1] = 0;
        if (*line) handle_line(line, temp);
      } else len = 0;
    } while (len);
    (void)strcpy(temp->start, dummy);
    status = sys$setimr(0, ast_time, start_receives, temp->reqidt, 0);
    if (!ODD(status)) my_sig(status, "setimr in sock");
  }
  else {
    say("The connection has been closed by the server.");
    tcp_shutdown(temp->iochan);    /**/
    temp->iochan = 0;
    if (temp->connected) (void)handle_on(o_disconnect, temp->server_name, 0);
    temp->connected = FALSE;
  }
}

/* ------------------------------------------------------------------------ */
connect_node *master_connect_list = NULL;
/* ------------------------------------------------------------------------ */
void master_list(char *str)
{
  connect_node *w;
  say("*** T chan acce host_id   port [%s]", str);
  for (w = master_connect_list; w; w = w->next)
  {
    say("*** %1.1d %4.4d %4.4d %9.9u %5.5d", w->type, w->iochan,
        w->accept_chan, w->sin?tcp_htonl(w->sin->sin_address):-1,
        w->sin?tcp_htons(w->sin->sin_port):-1);
  }
  say("*** End of connect list");
}

/* ------------------------------------------------------------------------ */
int master_read(short chan)
{              
  int old, kill_it;
  connect_node *walk, *save, *p = NULL;
  old = sys$setast(0);
  walk = master_connect_list;
  while (walk)
    if ((sys$clref(walk->event_flag) == SS$_WASSET) &&
        (walk->iosb.status != 0))
    {
      kill_it = FALSE;
      if ((walk->iosb.status != 1) || (walk->iosb.length <= 0))
      {
        if (walk->on_exit) walk->on_exit(walk);
        kill_it = TRUE;
      } else {
        walk->buffer[walk->iosb.length] = 0;
        if (walk->on_read)
          if (walk->on_read(walk))
            tcp_receive(walk->iochan, walk->buffer, sizeof(walk->buffer),
                        &walk->iosb, walk->event_flag, master_read);
          else kill_it = TRUE;
        else say("%s", walk->buffer);
      }
      if (kill_it) {
        tcp_shutdown(walk->iochan);
        lib$free_ef(&walk->event_flag);
        if (walk->sin) free(walk->sin);
        if (walk->specific) free(walk->specific);
        if (p) p->next = walk->next;
        else master_connect_list = walk->next;
        save = walk;
        walk = walk->next;
        free(save);
      } else { p = walk; walk = walk->next; }
    } else { p = walk; walk = walk->next; }
  if (old==SS$_WASSET) sys$setast(1);
  return 1;
}

/* ------------------------------------------------------------------------ */
char *start_read(short chan, short nchan, READ_ROUT on_read, READ_ROUT on_exit)
{
  int old;
  connect_node *walk;
  old = sys$setast(0);
  for (walk = master_connect_list; walk; walk = walk->next)
    if (walk->iochan == chan)
    {
      walk->iochan = nchan;
      walk->on_read = on_read;
      walk->on_exit = on_exit;
      tcp_receive(walk->iochan, walk->buffer, sizeof(walk->buffer)-1,
                  &walk->iosb, walk->event_flag, master_read);
      if (old==SS$_WASSET) sys$setast(1);
      return walk->specific;
    }
  if (old==SS$_WASSET) sys$setast(1);
  return NULL;
}

/* ------------------------------------------------------------------------ */
void add_connect_to_list(short chan, SIN *sin, char *specific, int type)
{
  int status;
  connect_node *temp;
  temp = (connect_node *)malloc(sizeof(connect_node));
  if (!temp) return;
  temp->type = type;
  temp->iochan = chan;
  temp->sin = sin;
  temp->buffer[0] = 0;
  status = lib$get_ef(&(temp->event_flag));
  if (!(status & 1)) temp->event_flag = 0;
  temp->on_read = temp->on_exit = NULL;
  temp->specific = specific;
  temp->next = master_connect_list;
  master_connect_list = temp;
}

/* ------------------------------------------------------------------------ */
int new_send(node, format, p1, p2, p3, p4, p5, p6, p7, p8, p9)
  server_ptr node;
  char *format, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8, *p9;
{
  unsigned ss;
  char string[MAXLEN*2];

  if (!node) return 0;   /* Sending to a null server */
  if (!node->connected) {
    say("*** You are not connect to a server - try /server %s",
        node->server_name);
    return 0;
  }
  if (debug & DEBUG$SEND) yell(format, p1, p2, p3, p4, p5, p6, p7, p8, p9);
  (void)sprintf(string, format, p1, p2, p3, p4, p5, p6, p7, p8, p9);

  if ((ss = tcp_send(node->iochan, string, strlen(string))) == -1) {
    get_socket_error(string);
    say("send: %s", string);
    tcp_shutdown(node->iochan);
    node->connected = FALSE;
  }
  return ss;
}

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

void irc_receive(temp)
  server_ptr temp;
{
  int ss;
  char msg[80];
  if (!temp) return;
  ss = tcp_receive(temp->iochan, temp->buffer, MAXBRK-1, &(temp->iosb),
               temp->event_flag, scan_server_list);
  if (ss==-1) {
    get_socket_error(msg);
    say("irc_receive: %s", msg);
  }
}

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

int good_host(int_addr, host)
  unsigned char int_addr[4];
  char *host;
{
  int ack;
  ack = sscanf(host, "%d.%d.%d.%d", &int_addr[0], &int_addr[1], &int_addr[2],
               &int_addr[3]);
  if (ack==4) return TRUE;
  return FALSE;
}

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

short open_connection(server, port, status, oldserver)
  char *server;
  unsigned short port;
  int *status;
  server_ptr oldserver;
{
  char msg[80];
  unsigned short serv_chan;
  struct sockaddr_in_type sin;         /* The sin for our connection */

  if (oldserver) {
    if ((strcasecmp(server, oldserver->server_name) == 0) &&
        (oldserver->connected)) {

      *status = 1;
      return -1;
    }
  }
  *status = 0;
  sin.sin_family = AF_INET;
  sin.sin_port = tcp_htons(port);
  sin.sin_zero[0] = 0;
  sin.sin_zero[1] = 0;
  say("*** Connecting to port %d of [%s]", port, server);
  if (!good_host(&sin.sin_address, server))
    if (!(nslookup(nameserver, server, &sin.sin_address) ||
          nslookup("128.205.1.2", server, &sin.sin_address) ||
          nslookup("128.205.7.1", server, &sin.sin_address)))
    {
      say("*** Fatal error in nslookup of %s.", server);
      return -1;
    }
  serv_chan = tcp_socket (AF_INET, SOCK_STREAM, PF_INET);
  if ((int)serv_chan == -1) {
    get_socket_error(msg);
    say("socket: %d [%s]", *status, msg);
    return -1;
  }
  *status = tcp_connect(serv_chan, &sin, sizeof(sin));
  if (*status == -1) {
    get_socket_error(msg);
    say("connect: %d [%s]", *status, msg);
    return -1;
  }
  return serv_chan;
}

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

void getmy_host(void)
{
  char hostname[80];
#ifdef MULTINET
  hostname[0] = 0;
  if (!get_logical("UCX$INET_HOST", hostname, TRUE))
    if (!get_logical("ARPANET_HOST_NAME", hostname, TRUE))
      (void)get_logical("TCPIP_HOSTNAME", hostname, TRUE);
#else
#ifdef UCX
  char host1[80], domain[80];
  hostname[0] = 0;
  if (get_logical("UCX$INET_HOST", host1, TRUE) &&
      get_logical("UCX$BIND_DOMAIN", domain, TRUE))
    if (!strstr(host1, "."))
      (void)sprintf(hostname, "%s.%s\0", host1, domain);
    else
      (void)strcpy(hostname, host1);
#endif
#endif
  if (hostname[0])
    if (!(nslookup(nameserver, hostname, &myaddress) ||
          nslookup("128.205.1.2", hostname, &myaddress) ||
          nslookup("128.205.7.1", hostname, &myaddress)))
    {
      say("*** Fatal error in nslookup - 1. Aborting");
      say("NameServer [%s]", nameserver);
      say("HostName [%s]", hostname);
      exit(0);
    }
    say("*** %s translated.", hostname);
}

/* ------------------------------------------------------------------------ */
extern void free_hold();
/* ------------------------------------------------------------------------ */
void interactive(void)
{
  char input[MAXLEN], *walk, line[MAXLEN];

  while (1) {
    walk = input;
    (void)read_input(input, 560, NULL);
    if (*input) {
      if (get_boolvariable("INPUT_ALIASES")) 
        parse_line(input, "", FALSE, TRUE);
      else
        parse_line(input, NULL, FALSE, TRUE);
      add_input_list(input);
    }
    free_hold();
    updatestatwin();
    find_input_prompt(FALSE);
  }
}

/* ------------------------------------------------------------------------ */
void error_handler(void)
{
  quit = TRUE;
  delete_windows();
  cleanup();
  (void)printf("This is really lame\n");
}

/* ------------------------------------------------------------------------ */
extern int DUMB, lib$establish();
extern void win_serverset(server_ptr srv);
extern char global_ircrc[MAXLEN];

typedef struct server_list_struct *server_list_ptr;
struct server_list_struct {
  char server[80];
  int port;
  server_ptr active;
  server_list_ptr next;
};
server_list_ptr all_server_list;

/* ------------------------------------------------------------------------ */
server_list_ptr add_server_to_list(char *serv, int port)
{
  int max;
  server_list_ptr temp, mall;
  temp = all_server_list;
  while ((temp) && (temp->next)) {
    if ((strcasecmp(temp->server, serv)==0) && (temp->port == port))
      return temp;
    temp = temp->next;
  }
  mall = (server_list_ptr)malloc(sizeof(struct server_list_struct));
  if (!mall) return NULL;
  max = (strlen(serv)>79)?79:strlen(serv);
  strncpy(mall->server, serv, max);
  mall->server[max] = 0;
  mall->port = port;
  mall->active = NULL;
  mall->next = NULL;
  if (!temp) all_server_list = mall;
  else temp->next = mall;
}

/* ------------------------------------------------------------------------ */
void read_servers_file(char *file)
{
  FILE *fp;
  char *word, str[MAXLEN], *walk;
  if (!(fp = fopen(file, "r"))) return;
  while (fgets(str, sizeof(str)-1, fp))
  {
    walk = str;
    word = next_arg(&walk);
    add_server_to_list(word, atoi(walk));
  }
  fclose(fp);
}

/* ------------------------------------------------------------------------ */
void old_main(argc, argv)
  int argc;
  char **argv;
{
  unsigned status, dummy;
  short chan;
  char server[MAXLEN], loadfile[80], *dumb, *ds = NULL;
  int loop, curr = 0, port = DEF_PORT, set_server = 0;
  server_list_ptr walk;

  if (!setup_qio_hooks("disk$userdisk1:[mas0.masdough.irc]"))
    exit(0);

/* This wipes the binds, so it should be done near the top */
  init_binds();
  init_input_list(history);
  init_keybdio();

  strcpy(loadfile, argv[0]);
  dumb = loadfile;
  while ((dumb = strstr(dumb, "]"))) ds = ++dumb;
  if (ds) *ds = 0;
  sprintf(global_ircrc, "%sglobal.ircrc", loadfile);
  load_file(global_ircrc);

  if (!get_logical("IRCNICK", def_nick, FALSE))
    (void)get_username(def_nick, FALSE);
  if (!get_logical("IRCSERVER", server, FALSE))
    (void)strcpy(server, DEF_SERVER);

  for (loop=1;loop<argc;loop++) {
    if (argv[loop][0] == '-') {
      if (argv[loop][1] == 'd') DUMB = TRUE;
      else if (argv[loop][1] == 'p') {
         loop++;
         if (loop<argc) port = atoi(argv[loop]);
      }
    } else {
      if (curr==0) (void)strcpy(def_nick, argv[loop]);
      if (curr==1) {
        (void)strcpy(server, argv[loop]);
        set_server = 1;
      }
      curr++;
    }
  }
  if (set_server) add_server_to_list(server, port);
  sprintf(global_ircrc, "%sircii.servers", loadfile);
  read_servers_file(global_ircrc);

  init_window();

  getmy_host();
  status = sys$bintim(&ast_wait_d, ast_time);
  if (!ODD(status)) my_sig(status, "ast bintime");

  for (walk = all_server_list; !gsrv && walk; walk = walk->next) 
  {
    if ((chan = open_connection(walk->server, walk->port, &status, 0)) == -1)
      gsrv = NULL;
    else {
     gsrv = (server_ptr)add_server(walk->server, walk->port, NULL, def_nick,
                                   chan, NULL);
     (void)handle_on(o_connect, server, 0);
     if (!gsrv) (void)printf("Could not allocate space for the initial server.\n");
     walk->active = gsrv;
    }
  }
  win_serverset(gsrv);
  reset_outwin();
  irc_receive(gsrv);
  if (get_logical("IRCRC", loadfile, FALSE))
    load_file(loadfile);
  else
    load_file("sys$login:.ircrc");
  (void)launch_timer();
  interactive();
}

int main(int argc, char *argv[])
{
  (void)lib$establish(error_handler); /**/
  old_main(argc, argv);
}
