/*

                                   _JNJ`                        
                               .JNMH`                           
                              JMMF`       `;.                   
                            .NMM)          `MN.                 
                            MMM)            (MML                
                           (MMM`             MMML               
                     M  I  D  N  I  G  H  T    C  o  D  E       
                           (NMMF             MHNH               
                            NMML            .MMM                
                             NMML          .NMH                 
                              4MMNL       .#F                   
                               `4HNNL_   `                      
                                   `"""`                        

                          Copyright (C) 2004-2007
       "Ian (Larry) Latter" <ian dot latter at midnightcode dot org>

    Midnight Code is a registered trademark of Ian Latter.

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, and mirrored at the Midnight Code web
    site; as at version 2 of the License only.
  
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    General Public License for more details.
  
    You should have received a copy of the GNU General Public License
    (version 2) along with this program; if not, write to the Free
    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
    02111-1307 USA, or see http://midnightcode.org/gplv2.txt

*/

/* cast.c :: Midnight Code cast peer program				*/


#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <curses.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <errno.h>
#include <time.h>


#include <libmidnightcode.h>

/*
 *  GRAPHICAL TUNING
 */
#define BACKGROUND_COLOUR 8
#define BORDER_COLOUR 3
#define TITLE_COLOUR 7
#define LABEL_COLOUR 6
#define INPUT_COLOUR 7
#define LOCAL_OUTPUT_COLOUR 3
#define REMOTE_OUTPUT_COLOUR 7


int
window_initialise(WINDOW **window_screen_p,
                  WINDOW **window_main_p, WINDOW **window_main_scroll_p,
                  WINDOW **window_enter_p, int width, int height) {
  WINDOW *window_screen;
  WINDOW *window_main;
  WINDOW *window_main_scroll;
  WINDOW *window_enter;
  int  screen_x, screen_y;
  int  offset_x, offset_y;


  window_screen = initscr();
  keypad(window_screen, TRUE);
  noecho();
  cbreak();
  nodelay(window_screen, TRUE);
  refresh();

  start_color();
  init_pair(1, COLOR_RED,     COLOR_BLACK);
  init_pair(2, COLOR_GREEN,   COLOR_BLACK);
  init_pair(3, COLOR_YELLOW,  COLOR_BLACK);
  init_pair(4, COLOR_BLUE,    COLOR_BLACK);
  init_pair(5, COLOR_MAGENTA, COLOR_BLACK);
  init_pair(6, COLOR_CYAN,    COLOR_BLACK);
  init_pair(7, COLOR_WHITE,   COLOR_BLACK);
  init_pair(8, COLOR_WHITE,   COLOR_BLUE);
  init_pair(9, COLOR_BLACK,   COLOR_YELLOW);
  curs_set(0);

  wbkgd(window_screen, COLOR_PAIR(BACKGROUND_COLOUR));
  wrefresh(window_screen);

  usleep(2500);

  getmaxyx(window_screen, screen_y, screen_x);
  offset_x = abs(screen_x / 2) - abs(width / 2);
  offset_y = abs(screen_y / 2) - abs((height + 4) / 2);

  window_main = newwin(height, width, offset_y, offset_x);
  wbkgd(window_main, COLOR_PAIR(BORDER_COLOUR));
  box(window_main, ACS_VLINE, ACS_HLINE);
  wrefresh(window_main);

  window_main_scroll = newwin(height - 2, width - 2,
                              offset_y + 1, offset_x + 1);
  scrollok(window_main_scroll, 1);
  wrefresh(window_main_scroll);

  window_enter = newwin(3, width, offset_y + height + 1, offset_x);
  wbkgd(window_enter, COLOR_PAIR(BORDER_COLOUR));
  box(window_enter, ACS_VLINE, ACS_HLINE);
  wrefresh(window_enter);

  *window_screen_p = window_screen;
  *window_main_p = window_main;
  *window_main_scroll_p = window_main_scroll;
  *window_enter_p = window_enter;


  return 0;
}



int
window_render_static(WINDOW **window_main_p, WINDOW **window_main_scroll_p,
                     WINDOW **window_enter_p, int width, int height) {
  WINDOW *window_main;
  WINDOW *window_main_scroll;
  WINDOW *window_enter;


  if(window_main_p == NULL)
    return -1;
  window_main = *window_main_p;
  if(window_main == NULL)
    return -1;
  if(window_main_scroll_p == NULL)
    return -1;
  window_main_scroll = *window_main_scroll_p;
  if(window_main_scroll == NULL)
    return -1;
  if(window_enter_p == NULL)
    return -1;
  window_enter = *window_enter_p;
  if(window_enter == NULL)
    return -1;

  curs_set(0);

  wcolor_set(window_main, BORDER_COLOUR, NULL);
  mvwprintw(window_main, 0, 2, "[             ]");
  wcolor_set(window_main, TITLE_COLOUR, NULL);
  mvwprintw(window_main, 0, 4, "Chat Window");
  wrefresh(window_main);
  refresh();

  wcolor_set(window_main_scroll, TITLE_COLOUR, NULL);
  wrefresh(window_main_scroll);
  refresh();

  wcolor_set(window_enter, BORDER_COLOUR, NULL);
  mvwprintw(window_enter, 0, 2, "[              ]");
  wcolor_set(window_enter, TITLE_COLOUR, NULL);
  mvwprintw(window_enter, 0, 4, "Local Window");
  wrefresh(window_enter);
  refresh();



  return 0;
}



int
window_render_enter(WINDOW **window_enter_p, char * input_buffer,
                    int x, int y, int width) {
  WINDOW *window_enter;
  char pattern[10];


  if(window_enter_p == NULL)
    return -1;
  window_enter = *window_enter_p;
  if(window_enter == NULL)
    return -1;

  memset(pattern, 0, sizeof(pattern));
  snprintf(pattern, sizeof(pattern) - 1, "%%-%d.%ds", width, width);
  wcolor_set(window_enter, INPUT_COLOUR, NULL);
  mvwprintw(window_enter, y, x, pattern, input_buffer);

  wrefresh(window_enter);
  refresh();


  return 0;
}



int
window_render_scroll(WINDOW **window_main_scroll_p,
                   char * address_s, int timestamp, char * input_buffer,
                   int self, int x, int y, int width) {
  WINDOW *window_main_scroll;
  char pattern[16];
  char iphex_s[16];
  char time_s[16];
  char * iphex_s_p;
  char * time_s_p;


  if(window_main_scroll_p == NULL)
    return -1;
  window_main_scroll = *window_main_scroll_p;
  if(window_main_scroll == NULL)
    return -1;

  memset(pattern, 0, sizeof(pattern));
  snprintf(pattern, sizeof(pattern) - 1, "%%-%d.%ds", width, width);
  iphex_s_p = iphex_s;
  memset(iphex_s, 0, sizeof(iphex_s));
  midnightcode_ip_tohex_s(address_s, iphex_s_p);
  memset(time_s, 0, sizeof(time_s));
  snprintf(time_s, sizeof(time_s) - 1, "%d", timestamp);
  time_s_p = time_s;
  time_s_p+= (strlen(time_s) - 4);

  scroll(window_main_scroll);
  if(self) {
    wcolor_set(window_main_scroll, LOCAL_OUTPUT_COLOUR, NULL);
  } else {
    wcolor_set(window_main_scroll, REMOTE_OUTPUT_COLOUR, NULL);
  }
  mvwprintw(window_main_scroll, y - 1, x - 1, "%8s", iphex_s);

  wcolor_set(window_main_scroll, LABEL_COLOUR, NULL);
  mvwprintw(window_main_scroll, y - 1, x + 8, "%s", time_s_p);

  if(self) {
    wcolor_set(window_main_scroll, LOCAL_OUTPUT_COLOUR, NULL);
  } else {
    wcolor_set(window_main_scroll, REMOTE_OUTPUT_COLOUR, NULL);
  }
  mvwprintw(window_main_scroll, y - 1, x + 13, pattern, input_buffer);

  touchwin(window_main_scroll);
  wrefresh(window_main_scroll);
  refresh();


  return 0;
}



int
window_destroy(WINDOW **window_screen_p, WINDOW **window_main_p,
               WINDOW **window_enter_p) {
  WINDOW *window_screen;
  WINDOW *window_main;
  WINDOW *window_enter;


  if(window_enter_p == NULL)
    return -1;
  window_enter = *window_enter_p;
  if(window_enter == NULL)
    return -1;
  if(window_main_p == NULL)
    return -1;
  window_main = *window_main_p;
  if(window_main == NULL)
    return -1;
  if(window_screen_p == NULL)
    return -1;
  window_screen = *window_screen_p;
  if(window_screen == NULL)
    return -1;

  curs_set(0);
  wbkgd(window_screen, COLOR_PAIR(BACKGROUND_COLOUR));
  wrefresh(window_screen);

  delwin(window_main);
  refresh();
  delwin(window_enter);
  refresh();
  usleep(2500);
  endwin();


  return 0;
}



int
main(int argc, char **argv) {
  struct in_addr cfg_self;
  struct in_addr cfg_seed;
  int cfg_help = 0;
  int cfg_width = 0;
  int cfg_height = 0;
  char self_str[21];
  char seed_str[21];
  char quit_str[6] = "/quit";
  WINDOW *window_screen;
  WINDOW *window_main;
  WINDOW *window_main_scroll;
  WINDOW *window_enter;
  char input_buffer[40 + 2];
  int input_count;
  char address_s[32];
  char * address_s_p;
  int timestamp;
  int character;
  int running;
  int self;
  struct midnightcode_interface_element_t * now_interface;
  struct midnightcode_socket_head_t * sockets;
  struct midnightcode_cast_head_t * cast;
  char * buffer;
  int buffer_size;
  void * tmp_void_p;
  int ret_i;

 
  // Prepare switches
  struct midnightcode_switch_t switches[] = {
    { "m", "mine", "My IP address",
      MIDNIGHTCODE_PARSE_TYPE_HOST, 0, 0, &cfg_self.s_addr },
    { "s", "seed", "Seed IP address",
      MIDNIGHTCODE_PARSE_TYPE_HOST, 0, 0, &cfg_seed.s_addr },
    { "x", "width", "Width of windows",
      MIDNIGHTCODE_PARSE_TYPE_INT, 0, 0, &cfg_width },
    { "y", "height", "Height of chat window",
      MIDNIGHTCODE_PARSE_TYPE_INT, 0, 0, &cfg_height },
    { "h", "help", "Display this help/usage screen",
      MIDNIGHTCODE_PARSE_TYPE_NONE, 0, 0, &cfg_help },
    { 0 }
  };



  // Set-up initial values;
  cfg_width = 60;
  cfg_height = 16;
  memset(&self_str, 0, 21);
  memset(&seed_str, 0, 21);
  memset(&cfg_self, 0, sizeof(struct in_addr));
  memset(&cfg_seed, 0, sizeof(struct in_addr));
  address_s_p = address_s;



  // Process switches
  if((ret_i = midnightcode_switch_process(argc, argv, switches)) != 0) {
    fprintf(stdout, "\n");
    midnightcode_logo(0, MIDNIGHTCODE_COLOUR_STYLE_BLOOD_MOON);
    midnightcode_license(0, MIDNIGHTCODE_COLOUR_STYLE_BLOOD_MOON, 
      MIDNIGHTCODE_LICENSE_TEXT_COPYRIGHT);
    fprintf(stdout, "    Error in switches, switch \"%s\" is unkown"
      " at parameter number %d.\n\n", argv[ret_i], ret_i);
    midnightcode_switch_usage(MIDNIGHTCODE_SWITCH_STYLE_SOFT, argv[0],
      switches);
    exit(1);
  }
  if(cfg_help) {
    midnightcode_logo(0, MIDNIGHTCODE_COLOUR_STYLE_BLOOD_MOON);
    midnightcode_license(0, MIDNIGHTCODE_COLOUR_STYLE_BLOOD_MOON, 
      MIDNIGHTCODE_LICENSE_TEXT_COPYRIGHT);
    midnightcode_switch_usage(MIDNIGHTCODE_SWITCH_STYLE_SOFT, argv[0],
      switches);
    exit(1);
  }
  fflush(stdout);



  // Process license and logo
  midnightcode_logo(0, MIDNIGHTCODE_COLOUR_STYLE_BLOOD_MOON);
  midnightcode_license(0, MIDNIGHTCODE_COLOUR_STYLE_BLOOD_MOON, 
    MIDNIGHTCODE_LICENSE_TEXT_ALL);



  // Program title
  midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_H1,
    MIDNIGHTCODE_STATUS_STYLE_NORMAL, "Route tool example: %s", argv[0]);

  fprintf(stdout, "\n");
  fflush(stdout);



  // Validate user params
  snprintf(self_str, 21, "%s", inet_ntoa(cfg_self));
  snprintf(seed_str, 21, "%s", inet_ntoa(cfg_seed));

  midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_TEXT,
    MIDNIGHTCODE_STATUS_STYLE_NORMAL,
    "Checking chat window dimensions");
  if(cfg_width < 60) {
    midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_FAIL);
    exit(1);
  }
  if(sizeof(input_buffer) > cfg_width) {
    midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_FAIL);
    exit(1);
  }
  if(cfg_height < 12) {
    midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_FAIL);
    exit(1);
  }
  midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_OKAY);

  if(cfg_self.s_addr == 0) {
    midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_TEXT,
      MIDNIGHTCODE_STATUS_STYLE_NORMAL,
      "Unspecified IP, finding default");
    if(midnightcode_init_nic_default(&now_interface) < 0) {
      midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_FAIL);
      exit(1);
    } else {
      snprintf(self_str, 20, "%s", now_interface->ipv4_address_s);
      midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_OKAY);
    }
  } else {
    midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_TEXT,
      MIDNIGHTCODE_STATUS_STYLE_NORMAL,
      "IP address specified");
    midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_OKAY);
  }
  midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_BLURB,
                           MIDNIGHTCODE_STATUS_STYLE_UNKNOWN,
    "Using IP address %s", self_str);
  fprintf(stdout, "\n");



  // Socket Initialisation
  midnightcode_list_init(socket_struct_head, midnightcode_socket_element_t);
  tmp_void_p = (void *)&socket_struct_head;
  sockets = tmp_void_p;

  midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_TEXT,
                           MIDNIGHTCODE_STATUS_STYLE_NORMAL,
                           "Initialising socket library");
  if(midnightcode_socket_initialise(sockets) < 0) {
    midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_FAIL);
    fprintf(stdout, "\n");
    exit(1);
  } else {
    midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_OKAY);
  }



  // Cast Initialisation
  midnightcode_list_init(cast_struct_head, midnightcode_cast_element_t);
  tmp_void_p = (void *)&cast_struct_head;
  cast = tmp_void_p;

  midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_TEXT,
                           MIDNIGHTCODE_STATUS_STYLE_NORMAL,
                           "Initialising cast library");
  if((ret_i = midnightcode_cast_initialise(cast,
    MIDNIGHTCODE_CAST_OPTION_UNICAST,
    self_str, MIDNIGHTCODE_CAST_PORT)) < 0) {
    midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_FAIL);
    fprintf(stdout, "\n");
    exit(ret_i);
  } else {
    midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_OKAY);
  }



  // Cast Seed
  if(cfg_seed.s_addr != 0) {
    midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_TEXT,
      MIDNIGHTCODE_STATUS_STYLE_NORMAL,
      "Seed IP specified, seeding cast agent");
    buffer = NULL;
    buffer_size = 0;
    if(midnightcode_cast_seed(cast, seed_str, MIDNIGHTCODE_CAST_PORT,
      buffer, buffer_size) < 0) {
      midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_FAIL);
      exit(1);
    } else {
      midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_OKAY);
    }
  }



  // Window Startup
  window_initialise(&window_screen, &window_main, &window_main_scroll,
    &window_enter, cfg_width, cfg_height);
  window_render_static(&window_main, &window_main_scroll, &window_enter,
    cfg_width, cfg_height);



  // Endless loop - check for /quit
  running = 1;
  input_count = 0;
  memset(input_buffer, 0, sizeof(input_buffer));
  while(running) {
    memset(address_s_p, 0, sizeof(address_s));
    if(midnightcode_cast_read(cast, address_s_p, &timestamp,
      &buffer, &buffer_size) > 0) {
      if(buffer_size > 0) {
        if(strlen(buffer) > 0) {
          self = 0;
          if(strncmp(self_str, address_s, strlen(self_str)) == 0)
            self = 1;
          window_render_scroll(&window_main_scroll,
            address_s, timestamp, buffer, self,
            2, cfg_height - 2, sizeof(input_buffer) - 2);
        }
      }
    }
    character = getch();
    if(character == ERR) {
      usleep(2500);
      continue;
    } else if(character >= 32 && character <= 125) {
      if(input_count < (sizeof(input_buffer) - 2)) {
        input_buffer[input_count] = character;
        input_count++;
      } else {
        flash();
      }
    } else if(character == KEY_BACKSPACE) {
      if(input_count > 0) {
        input_count--;
        input_buffer[input_count] = '\0';
      } else {
        flash();
      }
    } else if((character == KEY_ENTER || character == 10 || character == 27)
      && input_count > 0) {
      if(strncmp(input_buffer, quit_str, strlen(quit_str)) == 0) {
        running = 0;
      } else {
        input_count++;
        buffer = malloc(input_count);
        if(buffer) {
          memcpy(buffer, input_buffer, input_count);
          midnightcode_cast_write(cast, buffer, input_count);
        }
      }
      input_count = 0;
      memset(input_buffer, 0, sizeof(input_buffer));
    }
    window_render_enter(&window_enter, input_buffer, 2, 1, cfg_width - 4);
  }
  window_destroy(&window_screen, &window_main, &window_enter);




  // Cast shutdown
  midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_TEXT,
                           MIDNIGHTCODE_STATUS_STYLE_NORMAL,
                           "Shutdown cast library");
  if(midnightcode_cast_shutdown(cast) < 0) {
    midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_FAIL);
    fprintf(stdout, "\n");
    exit(1);
  } else {
    midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_OKAY);
  }



  // Socket shutdown
  midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_TEXT,
                           MIDNIGHTCODE_STATUS_STYLE_NORMAL,
                           "Shutdown socket library");
  if(midnightcode_socket_shutdown(sockets) < 0) {
    midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_FAIL);
    fprintf(stdout, "\n");
    exit(1);
  } else {
    midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_OKAY);
  }


  fprintf(stdout, "\n\n");


  exit(0);
}

