#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <netinet/in.h>
#include <netdb.h>
#include <signal.h>
#include <curses.h>

#define PORT	47350
#define K	1024

void initcurses(void) {
  initscr();
  meta(stdscr,TRUE);
  cbreak();
  noecho();
//   start_color();
//   for(int i=0; i < 8; i++)
//     for(int j=0; j < 8; j++)
//       init_pair(i<<3|j, i, j);
  intrflush(stdscr, FALSE);
//  keypad(stdscr, TRUE);
//  mousemask(ALL_MOUSE_EVENTS|REPORT_MOUSE_POSITION, NULL);
  clear();
  refresh();
}

void cleanup(int s) {
  move(LINES, 0);
  refresh();
  echo();
  nocbreak();
  endwin();
  exit(0);
}

int server(void) {
  struct sockaddr_in server;
  int sock, them, r;

  if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
    perror("socket");
    exit(1);
  }
  r = 1;
  setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &r, sizeof(r));
  
  server.sin_family = AF_INET;
  server.sin_addr.s_addr = INADDR_ANY;
  server.sin_port = htons(PORT);
  if (bind(sock, (struct sockaddr *)&server, sizeof(server))) {
    perror("bind");
    exit(1);
  }
  if (listen(sock, 5) < 0) {
    perror("listen");
    exit(1);
  }
  do {
    them = accept(sock, NULL, NULL);
  } while (them < 0);
  return them;
}

int client(char *host, char *port) {
  struct sockaddr_in server;
  struct hostent *he;
  int sock;

  if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
    perror("socket");
    exit(1);
  }
  
  server.sin_family = AF_INET;
  if ((he = gethostbyname(host)) == NULL) {
    fprintf(stderr,"Unknown host: %s\n", host);
    exit(1);
  }
  memcpy(&server.sin_addr, he->h_addr, he->h_length);
  server.sin_port = htons(atoi(port));
  
  if (connect(sock, (struct sockaddr *)&server, sizeof(server)) < 0) {
    fprintf(stderr,"Could not connect to host.\n");
    exit(1);
  }
  return sock;
}

int main(int argc, char *argv[])
{
  struct sigaction sa;
  WINDOW *top, *bot;
  char buf[K];
  int sock, i, r, rows, ch;
  char c;
  fd_set rfds, sfds;
  int highfd = 0;

  sa.sa_handler = cleanup;
  for(int i=1; i <= 32; i++) sigaction(i, &sa, NULL);

  initcurses();
  rows = (LINES - LINES%2)/2;
  top = newwin(rows, COLS, 0, 0);
  scrollok(top, 1);
  for(i=0; i < COLS; i++)
    mvaddch(rows, i, '-');
  bot = newwin(rows, COLS, rows+1, 0);
  scrollok(bot, 1);
  refresh();

  if (argc >= 3) {
    sock = client(argv[1], argv[2]);
  } else {
    sock = server();
  }

  FD_ZERO(&sfds);
  FD_SET(0, &sfds);
  FD_SET(sock, &sfds);
  highfd = sock+1;

  for(;;) {
    // Re-initialize fd set:
    memcpy(&rfds, &sfds, sizeof(sfds));

    r = select(highfd, &rfds, NULL, NULL, NULL);
    if (r <= 0) continue;
    
    if (FD_ISSET(sock,&rfds)) {
      if (read(sock, &c, 1) <= 0) break;
      if (c == '\b') {
	waddch(top, '\b');
	waddch(top, ' ');
      }
      waddch(top, (char)c);
      wrefresh(top);
    }
    if (FD_ISSET(0,&rfds)) {
      ch = getch();
      if (ch == EOF) break;
      if (ch == '\b') {
	waddch(bot, (char)ch);
	waddch(bot, ' ');
      }
      waddch(bot, (char)ch);
      wrefresh(bot);
      c = (char)ch;
      if (write(sock, &c, 1) < 0) break;
    }
  }
  cleanup(0);
  return 0;
}
