#define tell(x) if (find_living("inspiral")) tell_object(find_living("inspiral"), x + "\n");
/*
 * Reads in the nameserver config file and gets all of the
 * ip's from it.
 */
#include "socket.h"
#include "/adm/net/inet.h"
#define SERVER_PORT 7685
#define TIMEOUT (5*60)
#define CONFIG "/adm/net/config/"
#define ME machines["tabor"]
#define JUST_ADDR(x) sscanf(x, "%s %d", x, womble)

#define NAME 0
#define REVERSE 1
#define LAST_REQ 2
#define SERVICE 3

mapping machines,
        reverse,
        services,
        failed,
        requests,
        service_req,
        sent,
        my_write,
        services;
string *nameservers,
       *pending;
int my_port, womble;
int my_socket, open_server, current_ns;

void load_mappings(string str, string gf);
void setup_nameserver(int port);
void query_nameserver();
void close_callback(int fd);
void write_callback();
void write_service(int fd);

void create() {
  string bing;

  nameservers = ({ });
  machines = ([ ]);
  services = ([ "default" : ([ ]) ]);
  failed = ([ ]);
  reverse = ([ ]);
  pending = ({ });
  sent = ([ ]);
  requests = ([ ]);
  my_write = ([ ]);
  load_mappings(CONFIG+"nameserver", CONFIG+"services");
/* The default... */
  setup_nameserver(my_port);
} /* create() */

void load_mappings(string filen, string ser) {
  string file, *bits;
  string name, address, flags, tmp;
  int i, port, de_port;

  file = read_file(filen);
  if (file) {
/* Need to do it twice me thinks.  Other wise odd sized bits would loose. */
    file = implode(explode(file, " ") - ({ "" }), " ");
    bits = explode(file, "\n");
    for (i=0;i<sizeof(bits);i++) {
/* Do we have a comment? */
      if (bits[i][0] == '#' || !strlen(bits[i]))
        continue;
/* Ok, addresses are of the form, mudname address flags */
/* flags are NS for nameserver.  (All that is named at the moment) */
      flags = "";
      if (sscanf(bits[i], "%s %s", name, address) == 2) {
        port = SERVER_PORT;
        if (sscanf(address, "%s:%d%s", address, port, flags) != 3)
          sscanf(address, "%s %s", address, flags);
        else
          sscanf(flags, " %s", flags);
        switch (lower_case(flags)) {
          case "ns" :
            nameservers += ({ address+" "+port });
            break;
        }
        machines[name] = address+" "+port;
/* Case for multiple muds? frog is lets sleeze it... */
        reverse[address] = name;
        reverse[address+" "+port] = name;
      }
    }
  }
  my_port = SERVER_PORT;
  sscanf(ME, "%s %d", file, my_port);
  file = read_file(ser);
  if (file) {
    file = implode(explode(file, " ") - ({ "" }), " ");
    file = lower_case(file);
    bits = explode(file, "\n");
    for (i=0;i<sizeof(bits);i++) {
      if (bits[i][0] == '#' || !strlen(bits[i]))
        continue;
/* The pattern is, name, port */
      if (sscanf(bits[i], "%s %s %s", name, address, tmp)) {
        if (name != "default") {
          name = machines[name];
          de_port = SERVER_PORT;
          sscanf(name, "%s %d", file, de_port);
        } else
          de_port = 0;
/* Can only set up things about known muds... */
        if (!name) continue;
        if (!services[name])
          services[name] = ([ ]);
        if (tmp[0] == '-') {
          sscanf(tmp[1..100], "%d", port);
          services[name][address] = de_port+port;
        } else if (tmp[0] == '+') {
          sscanf(tmp[1..100], "%d", port);
          services[name][address] = de_port+port;
        } else if (sscanf(tmp, "%d", port) == 1)
          if (name == "default")
            services[name][address] = ({ port });
          else
            services[name][address] = port;
      }
    }
  }
} /* load_mappings() */

/*
 * Ok, we lookup in our nice array, if we get a hit we returns that name
 * if not, we try the first name server.  Bit of a problem here though.
 *
 * Means the return time is not deterministic.
 * Oh well.  They will have to live with it.
 * Keep a failed array so we don't try the same name several million times.
 */
void do_request(int type, string name, string func, mixed args, string *ser) {
  if (!ser) ser = ({ });
  if (!requests[type])
    requests[type] = ([ ]);
  if (!requests[type][name]) {
    pending += ({ type, name });
    if (current_ns == 0)
      requests[type][name] = ({ sizeof(nameservers)-1, ser });
    else
      requests[type][name] = ({ current_ns - 1, ser });
  } else
    requests[type][name][1] += ser;
  requests[type][name] += ({ ({ previous_object(), func, args }) });
  query_nameserver();
} /* do_request() */

void service_request(string name, string host, mixed ob, string func,
                     mixed args) {
  int fd, i;

  if (!service_req[host]) {
/* We need to open one... */
    fd = socket_create(STREAM, "got_service", "close_service");
    i = socket_connect(fd, host+" "+SERVER_PORT,
                       "got_service", "write_service");
    if (i < 0) {
      JUST_ADDR(host);
      call_other(ob, func, name, host, 0, args);
      return ;
    }
    service_req[fd] = host;
    service_req[host] = ({ fd, 2, ({ ob, func, name, args }) });
    return ;
  }
  service_req[host] += ({ ({ ob, func, name, args }) });
  write_service(service_req[host][0]);
} /* service_request() */

void got_service(int fd, string mess) {
  string name, host, str, host2;
  int port, i;
  mixed *bit;

  sscanf(mess, "%s^%d", name, port);
  host = service_req[fd];
  if (!host) return ;
  for (i=2;i<sizeof(service_req[host]);i++)
    if (service_req[host][i][2] == name) {
      bit = service_req[host][i];
      services[host][bit[2]] = port;
      host2 = host;
      JUST_ADDR(host2);
      call_other(bit[0], bit[1], bit[2], host2, port, bit[3]);
      service_req[host] = exclude_array(service_req[host], i);
      if (service_req[host][1] >= i)
        service_req[host][1]--;
      i--;
    }
  if (sizeof(service_req[host]) == 2) {
/* Just the control info... */
    map_delete(service_req, host);
    map_delete(service_req, fd);
    socket_close(fd);
  }
} /* got_service() */

void close_service(int fd) {
  int i;
  string host, host2;
  mixed *bit;

/* This means we fluffed it... */
  host2 = host = service_req[fd];
  if (!host) return ;
  JUST_ADDR(host2);
  for (i=2;i<sizeof(service_req[host]);i++) {
    bit = service_req[host][i];
    call_other(bit[0], bit[1], bit[2], host2, 0, bit[3]);
  }
  map_delete(service_req, host);
  map_delete(service_req, fd);
} /* close_service() */

void write_service(int fd) {
  string host;
  int pos;

  host = service_req[fd];
  if (!host) {
/* How did this get here? */
    socket_close(fd);
    return ;
  }
/* Anything left to write? */
  if ((pos = service_req[host][1]) > sizeof(service_req[host]))
    return ;
/* Yep! */
  if (socket_write(fd, SERVICE+"^"+service_req[host][pos][2]+"^0") < 0)
    return ;
  service_req[host][1]++;
} /* write_service() */

void lookup_mud(string name, string finitio, mixed args, string *ser) {
  string host;

  if (machines[name]) {
    host = machines[name];
/*
    JUST_ADDR(host);
 */
    call_other(previous_object(), finitio, name, host, args);
    return ;
  }
  if (failed[name] || !sizeof(nameservers)) {
    call_other(previous_object(), finitio, name, 0, args);
    return ;
  }
/* Ok, ask any name servers we know about */
  do_request(NAME, name, finitio, args, ser);
} /* lookup_mud() */

void reverse_lookup(string address, string func, mixed args, string *ser) {
  string *bits;
  int i;

  bits = keys(machines);
  if ((i=member_array(address, bits)) != -1) {
    bits = values(machines);
    call_other(previous_object(), func, address, bits[i], args);
    return ;
  }
  if (failed[address] || !sizeof(nameservers)) {
    call_other(previous_object(), func, address, 0, args);
    return ;
  }
/* Ok, now we be a nice little frog and setup a nameserver lookup thingy. */
  do_request(REVERSE, address, func, args, ser);
} /* reverse_lookup() */

void lookup_service(string name, string host, string func, string args) {
  string blue;

  if (host[0] < '0' || host[0] > '9') {
/* Need to lookup the host first... */
    this_object()->lookup_mud(host, "finish_hostlookup",
                           ({ previous_object(), func, name, args }), ({ }));
    return ;
  }
  if (services["default"][name]) {
    string hname;
    int port;

    port = SERVER_PORT;
    sscanf(host, "%s %d", hname, port);
    if (pointerp(services["default"][name]))
      call_other(previous_object(), func, name, hname,
                                          services["default"][name][0], args);
    else
      call_other(previous_object(), func, name, hname,
                                          port+services["default"][name], args);
    return ;
  }
  if (!host || host == ME) {
    host = ME;
    JUST_ADDR(host);
    if (!services[ME] || !services[ME][name])
      call_other(previous_object(), func, name, host, 0, args);
    else
      call_other(previous_object(), func, name, host, services[ME][name], args);
    return ;
  }
/* Irk!  Now we need to look the damn thing up :( */
  if (services[host][name]) {
    blue = host;
    JUST_ADDR(blue);
    call_other(previous_object(), func, name, blue, services[host][name], args);
    return ;
  }
	tell("Call to service_request");
  service_request(name, host, previous_object(), func, args);
} /* lookup_service() */

void finish_hostlookup(string host, string found, mixed *args) {
  if (!found) {
    call_other(args[0], args[1], args[2], found, 0, args[3]);
    return ;
  }
  if (services["default"][args[2]]) {
    string hname;
    int port;

    port = SERVER_PORT;
    sscanf(found, "%s %d", hname, port);
    if (pointerp(services["default"][args[2]]))
      call_other(args[0], args[1], args[2], hname,
                             services["default"][args[2]][0], args[3]);
   else
      call_other(args[0], args[1], args[2], hname,
                             services["default"][args[2]]+port, args[3]);
    return ;
  }
  if (services[found][args[2]]) {
    host = found;
    JUST_ADDR(host);
    call_other(args[0], args[1], args[2], host, services[found][args[2]],
                                 args[3]);
    return ;
  }
  service_request(args[2], found, args[0], args[1], args[3]);
} /* finish_hostlookup() */

/*
 * This will open a connection to a nameserver and ask it, hey!  Do you
 * know about this name?
 *
 * This same port is used for service lookups.  So we need a nice flag
 * We also keep a list of places we have been.  To stop loops.
 */
void query_nameserver() {
  int i;
  if (open_server) {
    write_callback();
    return ;
  }
  open_server = socket_create(STREAM, "got_address", "close_callback");
  i = socket_connect(open_server, nameservers[current_ns],
                     "got_address", "write_callback");
  if (i < 0)
    close_callback(0);
/*
  else
    write_callback();
 */
} /* query_nameserver() */

void finish_request(int type, string first, mixed ret) {
  mixed *bing;
  int i;

  if (!requests[type])
    return ;
  if (!requests[type][first])
    return ;
  bing = requests[type][first];
  for (i=2;i<sizeof(bing);i++)
{
    catch(call_other(bing[i][0], bing[i][1], first, ret, bing[i][2]));
}
  map_delete(requests[type], first);
  if (!sizeof(requests[type]))
    map_delete(requests, type);
} /* finish_request() */

/*
 * This will only get called if our lookup failed.  See if we asked
 * the last name server, if so.  Sulk.
 */
int check_finish(int type, string first) {
  if (!requests[type] || !requests[type][first])
    return 1; /* Finished */
  if (requests[type][first][0] == current_ns) {
    finish_request(type, first, 0);
    return 1;
  }
} /* check_finish() */

void got_address(int fd, mixed message) {
  int i;
  int type;
  string name, res, tmp;

tell("in got_address");
  sscanf(message, "%d^%s^%s", type, name, res);
  if (sent[type]) {
    sent[type] = (mixed *)sent[type] - ({ name });
    if (!sizeof(sent[type]))
      map_delete(sent, type);
  }
  switch (type) {
    case NAME :
      if (res== "0") {
/* Frog! We failed!  Sulk. */
/* Sigh....  We failed tell em about the bad news */
        if (check_finish(type, name))
          failed[type][name] = 1;
        break;
      }
/* We got an answer!  Yes! */
      tmp = res;
/*  We want more than just an address for this bit.  We just want an 
 * address with services...
      JUST_ADDR(tmp);
 */
tell("finsihing request");
      finish_request(type, name, tmp);
      machines[name] = res;
      break;
    case REVERSE :
      if (res == "0") {
        check_finish(type, name);
        break;
      }
      finish_request(type, name, res);
      machines[res] = name;
      break;
  }
/* Wait till we give up on this nameserver and move onto the next */
  if (!sizeof(sent) && !sizeof(pending)) {
/* Replyed to all of them */
    socket_close(open_server);
/* Does this call the close function? */
    close_callback(open_server);
  }
} /* got_address() */

void setup_nameserver(int port) {
  int i;

  my_socket = socket_create(STREAM, "read_callback", "interaction_close");
  socket_bind(my_socket, port);
  i = socket_listen(my_socket, "connect_callback");
} /* setup_nameserver() */

void write_callback() {
  string *path, s;
/*
 * Put all of the nameservers in the path as we will ask them all eventually.
 */
  if (!sizeof(pending))
    return ;
  if (requests[pending[0]])
    path = requests[pending[0]][1] + nameservers + ({ machines["tabor"] });
  else
    path = nameservers + ({ machines["tabor"] });
  if (socket_write(open_server, (s=(pending[0]+"^"+pending[1]+"^"
                                +implode(path, "$")))) >= 0) {
    if (sent[pending[0]])
      sent[pending[0]] += ({ pending[1] });
    else
      sent[pending[0]] = ({ pending[1] });
    pending = pending[2..1000];
  }
} /* write_callback() */

void connect_callback(int fd) {
  int connected;

  if ((connected = socket_accept(fd, "read_callback",
                                     "my_write_callback")) < 0) {
    return ;
  }
  my_write[connected] = 1;
  call_out("time_out", TIMEOUT, connected);
} /* connect_callback() */

void time_out(int fd) {
  socket_close(fd);
/*
  map_delete(my_write, fd);
*/
} /* time_out() */

void my_write_callback(int fd) {
  if (!my_write[fd])
    my_write[fd] = 1;
  if (intp(my_write[fd]))
    return ;
  if (socket_write(fd, my_write[fd][0]) >= 0)
    my_write[fd] = my_write[fd][1..1000];
  if (!sizeof(my_write[fd]))
    map_delete(my_write, fd);
} /* my_write_callback() */

void interaction_close(int fd) {
  INETD->close_service(fd);
  map_delete(my_write, fd);
} /* interaction_close() */

void send_back(int fd, string mess) {
  if (my_write[fd] && intp(my_write[fd])) {
/* Its ready to be sent to. */
    if (socket_write(fd, mess) < 0) {
/* Hmm.  bit of the old fail there... */
      my_write[fd] = ({ mess });
      return ;
    }
    map_delete(my_write, fd);
    return ;
  }
  my_write[fd] += ({ mess });
} /* send_back() */

void read_callback(int fd, mixed message, mixed blue) {
  string *bits;
  int i, type;
  string name, path;
  string *new_p;

  if (message[0] >= 'A' && message[0] <= 'Z') {
/* They are talking to our service.  Oblige them. */
    sscanf(message, "%s^%s", name, path);
    INETD->service_contact(fd, name, path);
    return ;
  }
  sscanf(message, "%d^%s^%s", type, name, path);
  new_p = explode(path, "$");
  switch (type) {
    case NAME :
/* Name lookup */
      if (machines[name]) {
        send_back(fd, type+"^"+name+"^"+machines[name]);
        return ;
      }
/*
 * Ok, we don't know about it.  Do we know about any other nameservers
 * not in the asked list?
 */
      if (sizeof(nameservers - new_p)) {
/* There are some! */
        lookup_mud(name, "tell_other_server", ({ fd, type }),
                   new_p);
      } else {
        send_back(fd, type+"^"+name+"^"+machines[name]);
      }
      return ;
    case REVERSE :
/* Reverse name lookup */
      bits = keys(machines);
      if ((i = member_array(name, bits)) != -1) {
        bits = values(machines);
        send_back(fd, type+"^"+name+"^"+bits[i]);
        return ;
      }
      if (sizeof(nameservers - new_p)) {
        reverse_lookup(name, "tell_other_server", ({ fd, type }),
                       new_p);
      } else {
        send_back(fd, type+"^"+name+"^0");
      }
      return ;
    case SERVICE :
/* Service lookup */
      if (!services[ME])
        send_back(fd, type+"^"+name+"^0");
      else
        send_back(fd, type+"^"+name+"^"+services[ME][name]);
      return ;
  } 
} /* read_callback() */

void tell_other_server(string name, string address, mixed *fd) {
/*
 * Don't bother checking if it is still around.  Just shove stuff down it.
 * it wont complain.
 */
  socket_write(fd[0], fd[1]+"^"+name+"^"+address);
} /* tell_other_server() */

void close_callback(int fd) {
  int i, j;
  mixed *tmp;

/*
 * This should be one of our outstanding connections to the rest of
 * of the world
 */
  current_ns++;
  if (current_ns >= sizeof(nameservers))
    current_ns = 0;
  pending = ({ });
  for (i=0;i<LAST_REQ;i++) {
    if (!requests[i]) continue;
    tmp = keys(requests[i]);
    for (j=0;j<sizeof(tmp);j++) {
/* Check for fail */
      if (!check_finish(i, tmp[j]) &&
          member_array(nameservers[current_ns], requests[i][tmp[j]][1]) == -1)
        pending += ({ i, tmp[j] });
    }
    if (!sizeof(requests[i]))
      map_delete(requests, i);
  }
  if (sizeof(pending)) {
    open_server = 0;
    query_nameserver();
  } else if (sizeof(requests))
/* We still have some unfinished bisness. */
    close_callback(open_server);
  else
    open_server = 0;
} /* close_callback() */


mapping query_services() { return services + ([ ]); }

mapping query_machines() { return machines; }