#include "socket.h"
#include <errno.h>

#define DEFAULT_PORT 23

object user;
string buff="",
       host,
       log_file;
int    sock,
       port;
status attached,
       connected;

static void callback(int fd, int act, mixed a, mixed b);
static void timeout();

void start(string h, int p)
{
    if (!adminp(this_player())) return 0;
    sock=(int)SOCKETD->socket_connect(h, p, #'callback);
    connected=0;
    printf("Connecting to %s port %d\n", h, p);
    user=this_player();
    host=h;
    port=p;
    attached=1;
    call_out(#'timeout, 15);
    input_to("input");
    return;
}

void reattach()
{
    printf("[telnet: Reattaching to %s%c%s]\n",
      host,
      port == DEFAULT_PORT ? 0 :' ',
      port == DEFAULT_PORT ? "":to_string(port));
    attached=1;
    write(buff);
    buff="";
    input_to("input");
    return;
}

void tell_user(string msg)
{
    if (attached)
	tell_object(user, msg);
    else
	buff=sprintf("%s%s", buff, msg);
    if (!attached && member(msg, '') != -1)
	tell_object(user, sprintf("[telnet: Beep from %s%c%s]\n",
	    host,
	    port == DEFAULT_PORT ? 0 :' ',
	    port == DEFAULT_PORT ? "":to_string(port)));
    if (log_file)
	write_file(log_file, msg);
    return;
}

static void timeout()
{
    if (connected) return;
    tell_user("telnet: connection timed out.\n");
    if (!attached)
	tell_object(user, buff);
    sock=-1;
    user=0;
    destruct(this_object());
}

void callback(int fd, int act, mixed a, mixed b)
{
    switch(act) {
      case SOCKET_READY:
	tell_user("telnet: connected.\n");
	remove_call_out(#'timeout);
	connected=1;
      break;
      case SOCKET_READ:
	tell_user(a);
	break;
      case SOCKET_CLOSE:
	tell_user("telnet: connection closed.\n");
	if (!attached)
	    tell_object(user, buff);
	sock=-1;
	user=0;
	connected=0;
	destruct(this_object());
	break;
      case SOCKET_ERROR: {
	string err;
	if (a == ERQ_E_NOTFOUND) err="Unknown host";
	else switch(b) {
	  case ECONNREFUSED:
            err="Connection Refused";
	    break;
          case EHOSTUNREACH:
            err="Host Unreachable";
            break;
          case ENETUNREACH:
            err="Net Unreachable";
            break;
          default:
            err=sprintf("Error %d", b);
	    break;
	}
	tell_user(sprintf("telnet: %s\nclosing connection.\n", err));
	if (!attached)
	    tell_object(user, buff);
	sock=-1;
	user=0;
	destruct(this_object());
      }
      break;
    }
    return;
}

void input(string str)
{
    if (sock<0) return;
    if (str[0]=='') {
      switch(str[1]) {
	case 'c': {
	  if (!log_file)
	      write("telnet: Not logging session.\n");
	  else {
	      printf("telnet: Stopped logging session to: %s.\n", log_file);
	      log_file=0;
          }
	  input_to("input");
	  return;
	}
	case 'd': {
	  tell_object(user, "Detached.  Type 'telnet' to resume.\n");
	  attached=0;
	  return;
	}
	case 'l': {
	  if (!str[2]) {
	      if (log_file)
		  printf("telnet: Logging session to: %s.\n", log_file);
	      else
		  write("telnet: Not logging session.\n");
	  } else {
	      str=eval_path(trimstr(str[2..]));
	      if (file_size(str) == -2)
		  str=sprintf("%s/telnet_log", str);
	      printf("telnet: Logging session to: %s.\n", str);
	      log_file=str;
	  }
	  input_to("input");
	  return;
	}
	case 'q': {
	  SOCKETD->socket_close(sock);
	  tell_user("telnet: connection closed.\n");
	  if (!attached)
	      tell_object(user, buff);
	  user=0;
	  sock=-1;
	  destruct(this_object());
	  return;
	}
	case 's': {
	  str=eval_path(trimstr(str[2..]));
	  if (file_size(str) < 0)
	      printf("telnet: Could not find file: %s.\n", str);
	  else if (connected) {
	      string *lines;
	      lines=explode(read_file(str), "\n");
	      tell_user(sprintf("telnet: Sending file: %s.\n", str));
	      for(; sizeof(lines); lines=lines[1..])
	          SOCKETD->socket_write(sock, sprintf("%s\n", lines[0]));
	      tell_user("telnet: File sent.\n");
	  }
	  input_to("input");
	  return;
	}
	case '!': {
	  str=str[1..];
	}
	case '?': {
	  write("telnet: Escape commands:\n"
		"All commands are preceeded by the escape (^[) character.\n"
		"\n"
		"c          Close session log.\n"
		"d          Detach telnet session.\n"
		"l<file>    Open session log into <file>.\n"
		"q          Forcefully close telnet session.\n"
		"s<file>    Send a file to telnet session.\n"
		"!<arg>     Send '!<arg>' to remote host.\n"
		"?          This help page.\n");
	  input_to("input");
	  return;
	}
      }
    }
    if (connected) {
	SOCKETD->socket_write(sock, str=sprintf("%s\n", str));
	if (log_file)
	    write_file(log_file, str);
    }
    input_to("input");
    return;
}

status clean_up(status arg)
{
    if (!user)
	destruct(this_object());
    return 1;
}
