#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <poll.h>
#include <sys/time.h>
#include <time.h>
#include <netinet/in.h>
#include <netdb.h>
#include <errno.h>
#include <curses.h>

#define K	1024
#define BUFSIZE	4096

#define PORT	47350	// Use the digits of your username for your port #.

// Initialize curses:
void initcurses(void) {
  initscr();
  meta(stdscr,TRUE);
  cbreak();
  noecho();
  intrflush(stdscr, FALSE);
  clear();
  refresh();
}

// Cleanup curses on exit:
void cleanup(int s) {
  move(LINES, 0);
  refresh();
  echo();
  nocbreak();
  endwin();
  exit(0);
}

int main(int argc, char *argv[])
{
  struct sockaddr_in addr;
  struct hostent *he;
  time_t t;
  struct tm *lt;
  WINDOW *top, *bot;
  char buf[BUFSIZE], cbuf[K];
  int i, ch, r, sock, rows, ci=0;
  uint32_t m;
  char *username = getenv("USER");
  struct pollfd fds[2];

  if (argc < 2) {
    fprintf(stderr,"Usage: c3 <hostname>");
    exit(0);
  }
  if (username == NULL) username = "Mongo";

  // Creates a internet TCP socket (man 7 socket):
  sock = socket(AF_INET, SOCK_STREAM, 0);
  if (sock < 0) {
    perror("socket");
    exit(1);
  }
  
  addr.sin_family = AF_INET;
  // Use gethostbyname to resolve the hostname given in argv[1] to an IP address:
  he = gethostbyname(argv[1]);
  if (he == NULL) {
    fprintf(stderr,"Host not found [%s]\n", argv[1]);
    exit(1);
  }
  // Copy the IP address from the hostent structure into our address structure:
  memcpy(&addr.sin_addr, he->h_addr, he->h_length);
  // Conver the port number to a network short and assign it:
  addr.sin_port = htons(PORT);

  // Connects to the server specified by the addr structure we just filled out:
  // man 2 connect
  if (connect(sock, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) < 0) {
    perror("connect");
    exit(1);
  }

  // Setup interface:
  initcurses();
  rows = LINES -4;
  top = newwin(rows, COLS, 0, 0);
  scrollok(top, 1);
  for(i=0; i < COLS; i++) mvaddch(rows, i, ' '|A_REVERSE);
  bot = newwin(3, COLS, rows+1, 0);
  refresh();

  fds[0].fd = 0;
  fds[1].fd = sock;
  fds[1].events = fds[0].events = POLLIN;

  for(;;) {
    // Wait for I/O or 1 second which ever occurs first:
    r = poll(fds, 2, 1000);
    // Put time in the status bar:
    t = time(NULL);
    lt = localtime(&t);
    // Turn on reversed text:
    attrset(A_REVERSE);
    mvprintw(rows, COLS-10, "%2d:%02d:%02d", lt->tm_hour, lt->tm_min, lt->tm_sec);
    // Change text back to normal:
    attrset(0);
    refresh();
    // Puts the cursor back into the bottom window:
    wrefresh(bot);
    if (r <= 0) continue;

    // If there is I/O on stdin, read it and send to the socket:
    if (fds[0].revents & (POLLHUP | POLLERR)) break;
    if (fds[1].revents & (POLLHUP | POLLERR)) break;
    if (fds[0].revents & POLLIN) {
      ch = getch();
      cbuf[ci++] = ch;
      if (ch == '\n') {
	cbuf[ci] = 0;
	r = snprintf(buf, BUFSIZE, "%s: %s", username, cbuf);
	r = write(sock, buf, r+1);
	if (r < 0) break;
	ci = 0;
	wclear(bot);
	wmove(bot, 0, 0);
	wrefresh(bot);
      } else {
	waddch(bot, ch);
	wrefresh(bot);
      }
    }
    // If there is socket data, get it and print it:
    if (fds[1].revents & POLLIN) {
      r = read(sock, buf, K);
      if (r <= 0) break;
      wprintw(top,"%.*s", r, buf);
      wrefresh(top);
    }
  }
  cleanup(0);
  return 0;
}
