/*

                                   _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-2010
       "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

*/

/* klirc :: Midnight Code Kinect UDP LIRC client program                */


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

#include <XnOpenNI.h>
#include <XnVSessionManager.h>
#include <XnVWaveDetector.h>

extern "C" {
#include <libmidnightcode.h>
}
#include "klirc.h"


/* Variables - LIRC Packet Data */
struct remote_element_t * lirc_remote;
u_int8_t * packet_data;
int code_length;
int packet_length;

/* Variables - Kinect Gesture Data */
XnBool * g_pValueToSet = NULL;
XnBool g_bQuit = false;
int state_plane, state_button;
float state_time, state_time_delay;
float ini_x, ini_y, ini_z;

/* Paramaters - Should be in Config File */ 
float button_time_min = 0.05;
float button_time_max = 0.8;
float button_time_delta = 0.2;
float button_width = 200;
float button_height = 200;
float centre_button_xoffset = 0;
float centre_button_yoffset = 0;
float left_button_xoffset = 200;
float left_button_yoffset = centre_button_yoffset;
float right_button_xoffset = -200;
float right_button_yoffset = centre_button_yoffset;
float up_button_xoffset = centre_button_xoffset;
float up_button_yoffset = -200;
float down_button_xoffset = centre_button_xoffset;
float down_button_yoffset = 200;
float plane_depth = 100;
float front_plane_offset = 200;
float mid_plane_offset = 50;
float back_plane_offset = -125;

/* One-time Calculations : Buttons */ 
float centre_button_xmin = centre_button_xoffset - (button_width / 2);
float centre_button_xmax = centre_button_xoffset + (button_width / 2);
float centre_button_ymin = centre_button_yoffset - (button_height / 2);
float centre_button_ymax = centre_button_yoffset + (button_height / 2);
float left_button_xmin   = left_button_xoffset - (button_width / 2);
float left_button_xmax   = left_button_xoffset + (button_width / 2);
float left_button_ymin   = left_button_yoffset - (button_height / 2);
float left_button_ymax   = left_button_yoffset + (button_height / 2);
float right_button_xmin  = right_button_xoffset - (button_width / 2);
float right_button_xmax  = right_button_xoffset + (button_width / 2);
float right_button_ymin  = right_button_yoffset - (button_height / 2);
float right_button_ymax  = right_button_yoffset + (button_height / 2);
float up_button_xmin     = up_button_xoffset - (button_width / 2);
float up_button_xmax     = up_button_xoffset + (button_width / 2);
float up_button_ymin     = up_button_yoffset - (button_height / 2);
float up_button_ymax     = up_button_yoffset + (button_height / 2);
float down_button_xmin   = down_button_xoffset - (button_width / 2);
float down_button_xmax   = down_button_xoffset + (button_width / 2);
float down_button_ymin   = down_button_yoffset - (button_height / 2);
float down_button_ymax   = down_button_yoffset + (button_height / 2);

/* One-time Calculations : Planes */ 
float front_plane_min    = front_plane_offset - (plane_depth / 2);
float front_plane_max    = front_plane_offset + (plane_depth / 2);
float mid_plane_min      = mid_plane_offset - (plane_depth / 2);
float mid_plane_max      = mid_plane_offset + (plane_depth / 2);
float back_plane_min     = back_plane_offset - (plane_depth / 2);
float back_plane_max     = back_plane_offset + (plane_depth / 2);


void
signal_handler(
                     int nSignal) {


  if(NULL != g_pValueToSet)
    *g_pValueToSet = true;
}


void
init_signals(
                     XnBool* bSignalWasCaught) {
  struct sigaction act;


  g_pValueToSet = bSignalWasCaught;

  memset(&act, 0, sizeof(act));
  act.sa_handler = &signal_handler;
  act.sa_flags = 0;
  sigaction( SIGINT, &act, NULL );
  sigaction( SIGTERM, &act, NULL );
  sigaction( SIGKILL, &act, NULL );
}



int
remote_to_lircrc(
                     struct remote_head_t * remotes) {
  struct remote_element_t * remote;
  char *buffer;
  char *buf_ptr;
  int cr_flag;
  int buffer_size;


  fprintf(stdout, "#\n");

  buffer_size = midnightcode_logo_buffer(&buffer, 0,
    MIDNIGHTCODE_COLOUR_STYLE_ASCII);
  if(buffer == NULL)
    return -1;
  if(buffer_size <= 0) {
    if(buffer)
      free(buffer);
    return -1;
  }
  cr_flag = 1;
  for(buf_ptr = buffer; *buf_ptr != '\0'; buf_ptr++) {
    if(cr_flag)
      fprintf(stdout, "# ");
    fprintf(stdout, "%c", *buf_ptr);
    if(*buf_ptr == 0x0a) {
      cr_flag = 1;
    } else {
      cr_flag = 0;
    }
  }
  free(buffer);

  buffer_size = midnightcode_license_buffer(&buffer, 0,
    MIDNIGHTCODE_COLOUR_STYLE_ASCII, MIDNIGHTCODE_LICENSE_TEXT_ALL);
  if(buffer == NULL)
    return -1;
  if(buffer_size <= 0) {
    if(buffer)
      free(buffer);
    return -1;
  }
  for(buf_ptr = buffer; *buf_ptr != '\0'; buf_ptr++) {
    if(cr_flag)
      fprintf(stdout, "# ");
    fprintf(stdout, "%c", *buf_ptr);
    if(*buf_ptr == 0x0a) {
      cr_flag = 1;
    } else {
      cr_flag = 0;
    }
  }
  free(buffer);

  midnightcode_list_for_each(remote, remotes) {

    /* BUTTONS ----------------------------------------------------------- */

    fprintf(stdout, "\n");
    fprintf(stdout, "begin\n");
    fprintf(stdout, "    remote = %s\n", remote->name);
    fprintf(stdout, "    prog = mythtv\n");
    fprintf(stdout, "    button = Up\n");
    fprintf(stdout, "    config = Up\n");
    fprintf(stdout, "    repeat = 1\n");
    fprintf(stdout, "    delay = 1\n");
    fprintf(stdout, "end\n");
    fprintf(stdout, "\n");
    fprintf(stdout, "begin\n");
    fprintf(stdout, "    remote = %s\n", remote->name);
    fprintf(stdout, "    prog = mythtv\n");
    fprintf(stdout, "    button = Left\n");
    fprintf(stdout, "    config = Left\n");
    fprintf(stdout, "    repeat = 1\n");
    fprintf(stdout, "    delay = 1\n");
    fprintf(stdout, "end\n");
    fprintf(stdout, "\n");
    fprintf(stdout, "begin\n");
    fprintf(stdout, "    remote = %s\n", remote->name);
    fprintf(stdout, "    prog = mythtv\n");
    fprintf(stdout, "    button = Enter\n");
    fprintf(stdout, "    config = Return\n");
    fprintf(stdout, "    repeat = 0\n");
    fprintf(stdout, "    delay = 0\n");
    fprintf(stdout, "end\n");
    fprintf(stdout, "\n");
    fprintf(stdout, "begin\n");
    fprintf(stdout, "    remote = %s\n", remote->name);
    fprintf(stdout, "    prog = mythtv\n");
    fprintf(stdout, "    button = Right\n");
    fprintf(stdout, "    config = Right\n");
    fprintf(stdout, "    repeat = 1\n");
    fprintf(stdout, "    delay = 1\n");
    fprintf(stdout, "end\n");
    fprintf(stdout, "\n");
    fprintf(stdout, "begin\n");
    fprintf(stdout, "    remote = %s\n", remote->name);
    fprintf(stdout, "    prog = mythtv\n");
    fprintf(stdout, "    button = Down\n");
    fprintf(stdout, "    config = Down\n");
    fprintf(stdout, "    repeat = 1\n");
    fprintf(stdout, "    delay = 1\n");
    fprintf(stdout, "end\n");
    fprintf(stdout, "\n");
    fprintf(stdout, "begin\n");
    fprintf(stdout, "    remote = %s\n", remote->name);
    fprintf(stdout, "    prog = mythtv\n");
    fprintf(stdout, "    button = Menu\n");
    fprintf(stdout, "    config = M\n");
    fprintf(stdout, "    repeat = 0\n");
    fprintf(stdout, "    delay = 0\n");
    fprintf(stdout, "end\n");
    fprintf(stdout, "\n");
    fprintf(stdout, "begin\n");
    fprintf(stdout, "    remote = %s\n", remote->name);
    fprintf(stdout, "    prog = mythtv\n");
    fprintf(stdout, "    button = Exit\n");
    fprintf(stdout, "    config = Escape\n");
    fprintf(stdout, "    repeat = 0\n");
    fprintf(stdout, "    delay = 0\n");
    fprintf(stdout, "end\n");
    fprintf(stdout, "\n");
    fprintf(stdout, "begin\n");
    fprintf(stdout, "    remote = %s\n", remote->name);
    fprintf(stdout, "    prog = mythtv\n");
    fprintf(stdout, "    button = Play\n");
    fprintf(stdout, "    config = P\n");
    fprintf(stdout, "    repeat = 0\n");
    fprintf(stdout, "    delay = 0\n");
    fprintf(stdout, "end\n");
    fprintf(stdout, "\n");
  }
  fflush(stdout);

  return 0;
}


int
remote_to_lircd(
                     struct remote_head_t * remotes) {
  struct remote_element_t * remote;
  char *buffer;
  char *buf_ptr;
  int cr_flag;
  int buffer_size;


  if(!remotes)
    return -1;

  fprintf(stdout, "#\n");

  buffer_size = midnightcode_logo_buffer(&buffer, 0,
    MIDNIGHTCODE_COLOUR_STYLE_ASCII);
  if(buffer == NULL)
    return -1;
  if(buffer_size <= 0) {
    if(buffer)
      free(buffer);
    return -1;
  }
  cr_flag = 1;
  for(buf_ptr = buffer; *buf_ptr != '\0'; buf_ptr++) {
    if(cr_flag)
      fprintf(stdout, "# ");
    fprintf(stdout, "%c", *buf_ptr);
    if(*buf_ptr == 0x0a) {
      cr_flag = 1;
    } else {
      cr_flag = 0;
    }
  }
  free(buffer);

  buffer_size = midnightcode_license_buffer(&buffer, 0,
    MIDNIGHTCODE_COLOUR_STYLE_ASCII, MIDNIGHTCODE_LICENSE_TEXT_ALL);
  if(buffer == NULL)
    return -1;
  if(buffer_size <= 0) {
    if(buffer)
      free(buffer);
    return -1;
  }
  for(buf_ptr = buffer; *buf_ptr != '\0'; buf_ptr++) {
    if(cr_flag)
      fprintf(stdout, "# ");
    fprintf(stdout, "%c", *buf_ptr);
    if(*buf_ptr == 0x0a) {
      cr_flag = 1;
    } else {
      cr_flag = 0;
    }
  }
  free(buffer);

  midnightcode_list_for_each(remote, remotes) {

    fprintf(stdout, "\n");
    fprintf(stdout, "begin remote\n");
    fprintf(stdout, "\n");
    fprintf(stdout, "  name\t\t%s\n", remote->name);
    fprintf(stdout, "\n");
    fprintf(stdout, "  bits\t\t%d\n", remote->signal_data_bits);
    fprintf(stdout, "  flags\t\tSPACE_ENC|CONST_LENGTH\n");
    fprintf(stdout, "\n");
    fprintf(stdout, "  eps\t\t30\n");
    fprintf(stdout, "  aeps\t\t100\n");

    /* SIGNALS ----------------------------------------------------------- */

    fprintf(stdout, "  header\t%d\t%d\n",
      remote->signal_header_pulse, remote->signal_header_space);
    fprintf(stdout, "  one\t\t%d\t%d\n",
      remote->signal_one_pulse, remote->signal_one_space);
    fprintf(stdout, "  zero\t\t%d\t%d\n",
      remote->signal_zero_pulse, remote->signal_zero_space);
    fprintf(stdout, "  ptrail\t%d\n",
      remote->signal_trail_pulse);
    fprintf(stdout, "  gap\t\t%d\n",
      remote->signal_gap_space);
    fprintf(stdout, "  toggle_bit\t0\n");

    /* BUTTONS ----------------------------------------------------------- */

    fprintf(stdout, "\n");
    fprintf(stdout, "  begin codes\n");
    fprintf(stdout, "  \tVolDown\t\t\t%s\n", remote->button_voldown_s);
    fprintf(stdout, "  \tVolUp\t\t\t%s\n", remote->button_volup_s);
    fprintf(stdout, "  \tMute\t\t\t%s\n", remote->button_mute_s);
    fprintf(stdout, "  \tChanUp\t\t\t%s\n", remote->button_chanup_s);
    fprintf(stdout, "  \tChanDown\t\t%s\n", remote->button_chandown_s);
    fprintf(stdout, "  \tUp\t\t\t%s\n", remote->button_up_s);
    fprintf(stdout, "  \tDown\t\t\t%s\n", remote->button_down_s);
    fprintf(stdout, "  \tLeft\t\t\t%s\n", remote->button_left_s);
    fprintf(stdout, "  \tRight\t\t\t%s\n", remote->button_right_s);
    fprintf(stdout, "  \tEnter\t\t\t%s\n", remote->button_enter_s);
    fprintf(stdout, "  \tRewind\t\t\t%s\n", remote->button_rewind_s);
    fprintf(stdout, "  \tPlay\t\t\t%s\n", remote->button_play_s);
    fprintf(stdout, "  \tForward\t\t\t%s\n", remote->button_forward_s);
    fprintf(stdout, "  \tStop\t\t\t%s\n", remote->button_stop_s);
    fprintf(stdout, "  \tPause\t\t\t%s\n", remote->button_pause_s);
    fprintf(stdout, "  \tMenu\t\t\t%s\n", remote->button_menu_s);
    fprintf(stdout, "  \tExit\t\t\t%s\n", remote->button_exit_s);
    fprintf(stdout, "  end codes\n");
    fprintf(stdout, "\n");

    fprintf(stdout, "end remote\n");
  }
  fflush(stdout);

  return 0;
}


int
lirc_button_code_to_number(
                     long long * remote_code_n,
                     char * remote_code_s) {
  const char * num_ptr;
  char * end_ptr;
  long long value;
  int ret_i;


  ret_i = -1;
  num_ptr = remote_code_s;
  errno = 0;
  value = strtoll(num_ptr, &end_ptr, 0);
  if(errno == ERANGE || (errno != 0 && value == 0)) {
    perror("strtol");
    return ret_i;
  }
  if(end_ptr - num_ptr == (int)strlen(num_ptr) && end_ptr != num_ptr) {
    ret_i = 0;
    *remote_code_n = value;
  }

  return ret_i;
}


int
remote_from_config(
                     struct remote_head_t * remote_elements,
                     struct midnightcode_config_head_t * config_elements) {
  struct remote_element_t * remote_element;
  struct midnightcode_config_section_element_t * section_element;
  struct midnightcode_config_section_element_t * hold_section_element;
  struct midnightcode_config_section_head_t * section_elements;
  void * tmp_void_p;
  int ret_i;
  midnightcode_list_init(config_section_struct_head,
                         midnightcode_config_section_element_t);


  if(!remote_elements || !config_elements)
    return -1;

  tmp_void_p = (void *)&config_section_struct_head;
  section_elements = (struct midnightcode_config_section_head_t *)tmp_void_p;

  if(!midnightcode_config_get_sections(config_elements,
                                       section_elements,
                                       REMOTE_STRUCTURE))
    return -1;

  ret_i = 0;
  midnightcode_list_for_each_safe(section_element, hold_section_element,
    section_elements) {

    remote_element = (struct remote_element_t *)malloc(
                          sizeof(struct remote_element_t));
    if(!remote_element)
      break;
    memset(remote_element, 0,
      sizeof(struct remote_element_t));

    snprintf(remote_element->name, 63, "%s", section_element->section);

    /* SERVER ------------------------------------------------------------ */

    ret_i = -1;
    if(midnightcode_config_get_pair(config_elements,
                                    REMOTE_STRUCTURE,
                                    remote_element->name,
                                    "lirc_server",
                                    &remote_element->lirc_address, 0,
                                    MIDNIGHTCODE_PARSE_TYPE_HOST)) {
      break;
    } else {
      inet_ntop(AF_INET, &remote_element->lirc_address,
        remote_element->lirc_address_s, 31);
    }

    if(midnightcode_config_get_pair(config_elements,
                                    REMOTE_STRUCTURE,
                                    remote_element->name,
                                    "lirc_port",
                                    &remote_element->lirc_port, 0,
                                    MIDNIGHTCODE_PARSE_TYPE_INT))
      break;

    /* SIGNALS ----------------------------------------------------------- */

    if(midnightcode_config_get_pair(config_elements,
                                    REMOTE_STRUCTURE,
                                    remote_element->name,
                                    "signal_data_bits",
                                    &remote_element->signal_data_bits, 0,
                                    MIDNIGHTCODE_PARSE_TYPE_INT))
      break;

    if(midnightcode_config_get_pair(config_elements,
                                    REMOTE_STRUCTURE,
                                    remote_element->name,
                                    "signal_header_pulse",
                                    &remote_element->signal_header_pulse, 0,
                                    MIDNIGHTCODE_PARSE_TYPE_INT))
      break;

    if(midnightcode_config_get_pair(config_elements,
                                    REMOTE_STRUCTURE,
                                    remote_element->name,
                                    "signal_header_space",
                                    &remote_element->signal_header_space, 0,
                                    MIDNIGHTCODE_PARSE_TYPE_INT))
      break;

    if(midnightcode_config_get_pair(config_elements,
                                    REMOTE_STRUCTURE,
                                    remote_element->name,
                                    "signal_one_pulse",
                                    &remote_element->signal_one_pulse, 0,
                                    MIDNIGHTCODE_PARSE_TYPE_INT))
      break;

    if(midnightcode_config_get_pair(config_elements,
                                    REMOTE_STRUCTURE,
                                    remote_element->name,
                                    "signal_one_space",
                                    &remote_element->signal_one_space, 0,
                                    MIDNIGHTCODE_PARSE_TYPE_INT))
      break;

    if(midnightcode_config_get_pair(config_elements,
                                    REMOTE_STRUCTURE,
                                    remote_element->name,
                                    "signal_zero_pulse",
                                    &remote_element->signal_zero_pulse, 0,
                                    MIDNIGHTCODE_PARSE_TYPE_INT))
      break;

    if(midnightcode_config_get_pair(config_elements,
                                    REMOTE_STRUCTURE,
                                    remote_element->name,
                                    "signal_zero_space",
                                    &remote_element->signal_zero_space, 0,
                                    MIDNIGHTCODE_PARSE_TYPE_INT))
      break;

    if(midnightcode_config_get_pair(config_elements,
                                    REMOTE_STRUCTURE,
                                    remote_element->name,
                                    "signal_trail_pulse",
                                    &remote_element->signal_trail_pulse, 0,
                                    MIDNIGHTCODE_PARSE_TYPE_INT))
      break;

    if(midnightcode_config_get_pair(config_elements,
                                    REMOTE_STRUCTURE,
                                    remote_element->name,
                                    "signal_gap_space",
                                    &remote_element->signal_gap_space, 0,
                                    MIDNIGHTCODE_PARSE_TYPE_INT))
      break;

    /* BUTTONS ----------------------------------------------------------- */

    if(midnightcode_config_get_pair(config_elements,
                                    REMOTE_STRUCTURE,
                                    remote_element->name,
                                    "button_voldown",
                                    &remote_element->button_voldown_s, 16,
                                    MIDNIGHTCODE_PARSE_TYPE_STRING))
      break;

    if(lirc_button_code_to_number(&remote_element->button_voldown_n,
      (char *)&remote_element->button_voldown_s))
      break;

    if(midnightcode_config_get_pair(config_elements,
                                    REMOTE_STRUCTURE,
                                    remote_element->name,
                                    "button_volup",
                                    &remote_element->button_volup_s, 16,
                                    MIDNIGHTCODE_PARSE_TYPE_STRING))
      break;

    if(lirc_button_code_to_number(&remote_element->button_volup_n,
      (char *)&remote_element->button_volup_s))
      break;

    if(midnightcode_config_get_pair(config_elements,
                                    REMOTE_STRUCTURE,
                                    remote_element->name,
                                    "button_mute",
                                    &remote_element->button_mute_s, 16,
                                    MIDNIGHTCODE_PARSE_TYPE_STRING))
      break;

    if(lirc_button_code_to_number(&remote_element->button_mute_n,
      (char *)&remote_element->button_mute_s))
      break;

    if(midnightcode_config_get_pair(config_elements,
                                    REMOTE_STRUCTURE,
                                    remote_element->name,
                                    "button_chanup",
                                    &remote_element->button_chanup_s, 16,
                                    MIDNIGHTCODE_PARSE_TYPE_STRING))
      break;

    if(lirc_button_code_to_number(&remote_element->button_chanup_n,
      (char *)&remote_element->button_chanup_s))
      break;

    if(midnightcode_config_get_pair(config_elements,
                                    REMOTE_STRUCTURE,
                                    remote_element->name,
                                    "button_chandown",
                                    &remote_element->button_chandown_s, 16,
                                    MIDNIGHTCODE_PARSE_TYPE_STRING))
      break;

    if(lirc_button_code_to_number(&remote_element->button_chandown_n,
      (char *)&remote_element->button_chandown_s))
      break;

    if(midnightcode_config_get_pair(config_elements,
                                    REMOTE_STRUCTURE,
                                    remote_element->name,
                                    "button_up",
                                    &remote_element->button_up_s, 16,
                                    MIDNIGHTCODE_PARSE_TYPE_STRING))
      break;

    if(lirc_button_code_to_number(&remote_element->button_up_n,
      (char *)&remote_element->button_up_s))
      break;

    if(midnightcode_config_get_pair(config_elements,
                                    REMOTE_STRUCTURE,
                                    remote_element->name,
                                    "button_down",
                                    &remote_element->button_down_s, 16,
                                    MIDNIGHTCODE_PARSE_TYPE_STRING))
      break;

    if(lirc_button_code_to_number(&remote_element->button_down_n,
      (char *)&remote_element->button_down_s))
      break;

    if(midnightcode_config_get_pair(config_elements,
                                    REMOTE_STRUCTURE,
                                    remote_element->name,
                                    "button_left",
                                    &remote_element->button_left_s, 16,
                                    MIDNIGHTCODE_PARSE_TYPE_STRING))
      break;

    if(lirc_button_code_to_number(&remote_element->button_left_n,
      (char *)&remote_element->button_left_s))
      break;

    if(midnightcode_config_get_pair(config_elements,
                                    REMOTE_STRUCTURE,
                                    remote_element->name,
                                    "button_right",
                                    &remote_element->button_right_s, 16,
                                    MIDNIGHTCODE_PARSE_TYPE_STRING))
      break;

    if(lirc_button_code_to_number(&remote_element->button_right_n,
      (char *)&remote_element->button_right_s))
      break;

    if(midnightcode_config_get_pair(config_elements,
                                    REMOTE_STRUCTURE,
                                    remote_element->name,
                                    "button_enter",
                                    &remote_element->button_enter_s, 16,
                                    MIDNIGHTCODE_PARSE_TYPE_STRING))
      break;

    if(lirc_button_code_to_number(&remote_element->button_enter_n,
      (char *)&remote_element->button_enter_s))
      break;

    if(midnightcode_config_get_pair(config_elements,
                                    REMOTE_STRUCTURE,
                                    remote_element->name,
                                    "button_rewind",
                                    &remote_element->button_rewind_s, 16,
                                    MIDNIGHTCODE_PARSE_TYPE_STRING))
      break;

    if(lirc_button_code_to_number(&remote_element->button_rewind_n,
      (char *)&remote_element->button_rewind_s))
      break;

    if(midnightcode_config_get_pair(config_elements,
                                    REMOTE_STRUCTURE,
                                    remote_element->name,
                                    "button_play",
                                    &remote_element->button_play_s, 16,
                                    MIDNIGHTCODE_PARSE_TYPE_STRING))
      break;

    if(lirc_button_code_to_number(&remote_element->button_play_n,
      (char *)&remote_element->button_play_s))
      break;

    if(midnightcode_config_get_pair(config_elements,
                                    REMOTE_STRUCTURE,
                                    remote_element->name,
                                    "button_forward",
                                    &remote_element->button_forward_s, 16,
                                    MIDNIGHTCODE_PARSE_TYPE_STRING))
      break;

    if(lirc_button_code_to_number(&remote_element->button_forward_n,
      (char *)&remote_element->button_forward_s))
      break;

    if(midnightcode_config_get_pair(config_elements,
                                    REMOTE_STRUCTURE,
                                    remote_element->name,
                                    "button_stop",
                                    &remote_element->button_stop_s, 16,
                                    MIDNIGHTCODE_PARSE_TYPE_STRING))
      break;

    if(lirc_button_code_to_number(&remote_element->button_stop_n,
      (char *)&remote_element->button_stop_s))
      break;

    if(midnightcode_config_get_pair(config_elements,
                                    REMOTE_STRUCTURE,
                                    remote_element->name,
                                    "button_pause",
                                    &remote_element->button_pause_s, 16,
                                    MIDNIGHTCODE_PARSE_TYPE_STRING))
      break;

    if(lirc_button_code_to_number(&remote_element->button_pause_n,
      (char *)&remote_element->button_pause_s))
      break;

    if(midnightcode_config_get_pair(config_elements,
                                    REMOTE_STRUCTURE,
                                    remote_element->name,
                                    "button_menu",
                                    &remote_element->button_menu_s, 16,
                                    MIDNIGHTCODE_PARSE_TYPE_STRING))
      break;

    if(lirc_button_code_to_number(&remote_element->button_menu_n,
      (char *)&remote_element->button_menu_s))
      break;

    if(midnightcode_config_get_pair(config_elements,
                                    REMOTE_STRUCTURE,
                                    remote_element->name,
                                    "button_exit",
                                    &remote_element->button_exit_s, 16,
                                    MIDNIGHTCODE_PARSE_TYPE_STRING))
      break;

    if(lirc_button_code_to_number(&remote_element->button_exit_n,
      (char *)&remote_element->button_exit_s))
      break;

    ret_i = 0;
    midnightcode_list_append(remote_elements, remote_element);

    midnightcode_list_remove(section_elements, section_element);
    if(section_element != NULL)
      free(section_element);

    midnightcode_list_for_each_safe_loop(section_element, hold_section_element,
      section_elements);
  }
  midnightcode_list_for_each_safe(section_element, hold_section_element,
    section_elements) {
    midnightcode_list_remove(section_elements, section_element);
    if(section_element != NULL)
      free(section_element);
    midnightcode_list_for_each_safe_loop(section_element, hold_section_element,
      section_elements);
  }


  return ret_i;
}


u_int8_t
lirc_get_high_byte(
                     int code) {
  unsigned int value;
  u_int8_t ret_i;


  value = (code * 256) / 15625;
  value = value / 256;
  ret_i = (u_int8_t)value;

  return ret_i;
}


u_int8_t
lirc_get_low_byte(
                     int code) {
  unsigned int value;
  u_int8_t ret_i;


  value = (code * 256) / 15625;
  value = value % 256;
  ret_i = (u_int8_t)value;

  return ret_i;
}


int
lirc_packet_init(
                     struct remote_element_t * remote) {
  int pd_offset;

  /*
      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
      | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
      |        Gap Space (Low)        |        Gap Space (High)       |
      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
      |      Header Pulse (Low)       |      Header Pulse (High)      |
      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
      |      Header Space (Low)       |      Header Space (High)      |
      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

  Button (Byte n, Bit n): 5 bytes x 8 bits = 40 messages x 4 bytes
      .                                                               .
      :                                                               :
      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
      |     [One|Zero] Pulse (Low)    |    [One|Zero] Pulse (High)    |
      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
      |     [One|Zero] Space (Low)    |    [One|Zero] Space (High)    |
      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
      :                                                               :
      .                                                               .

      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
      |       Trail Pulse (Low)       |      Trail Pulse (High)       |
      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
  */


  if(!remote)
    return -1;
  code_length = remote->signal_data_bits / 8;
  packet_length = 6 + (remote->signal_data_bits * 4) + 2;
  packet_data = (u_int8_t *)malloc(packet_length);
  if(!packet_data)
    return -1;
  memset(packet_data, 0, packet_length);

  pd_offset = 0;
  *(packet_data + pd_offset++) =
    lirc_get_low_byte(remote->signal_gap_space);
  *(packet_data + pd_offset++) =
    lirc_get_high_byte(remote->signal_gap_space) | 0x080;
  *(packet_data + pd_offset++) =
    lirc_get_low_byte(remote->signal_header_pulse);
  *(packet_data + pd_offset++) =
    lirc_get_high_byte(remote->signal_header_pulse);
  *(packet_data + pd_offset++) =
    lirc_get_low_byte(remote->signal_header_space);
  *(packet_data + pd_offset++) =
    lirc_get_high_byte(remote->signal_header_space) | 0x080;

  pd_offset = 6 + (remote->signal_data_bits * 4);
  *(packet_data + pd_offset++) =
    lirc_get_low_byte(remote->signal_trail_pulse);
  *(packet_data + pd_offset++) =
    lirc_get_high_byte(remote->signal_trail_pulse);

  return 0;
}


int
lirc_packet_code(
                     struct remote_element_t * remote,
                     long long code) {
  int pd_offset;
  int bit;
  int byte;
  u_int8_t data;
  u_int8_t mask;
  u_int8_t * uint_p;

  uint_p = (u_int8_t *)&code;
  pd_offset = 6;

  if(!remote)
    return -1;
  for(byte = code_length - 1; byte >= 0; byte--) {
    data = *(uint_p + byte);
    for(bit = 7; bit >= 0; bit--) {
      mask = 1 << bit;
      if((data & mask) == 0) {
        *(packet_data + pd_offset++) =
          lirc_get_low_byte(remote->signal_zero_pulse);
        *(packet_data + pd_offset++) =
          lirc_get_high_byte(remote->signal_zero_pulse);
        *(packet_data + pd_offset++) =
          lirc_get_low_byte(remote->signal_zero_space);
        *(packet_data + pd_offset++) = 
          lirc_get_high_byte(remote->signal_zero_space) | 0x080;
      } else {
        *(packet_data + pd_offset++) =
          lirc_get_low_byte(remote->signal_one_pulse);
        *(packet_data + pd_offset++) =
          lirc_get_high_byte(remote->signal_one_pulse);
        *(packet_data + pd_offset++) =
          lirc_get_low_byte(remote->signal_one_space);
        *(packet_data + pd_offset++) = 
          lirc_get_high_byte(remote->signal_one_space) | 0x080;
      }
    }
  }


  return 0;
}


int
lirc_packet_send(
                     struct remote_element_t * remote) {
  struct sockaddr_in si_other;
  int sock;


  if(!remote)
    return -1;
  if((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
    return -1;

  memset((char *) &si_other, 0, sizeof(si_other));
  si_other.sin_family = AF_INET;
  si_other.sin_addr.s_addr = inet_addr(remote->lirc_address_s);
  si_other.sin_port = htons(remote->lirc_port);

  if(sendto(sock, packet_data, packet_length, 0, 
    (struct sockaddr *)&si_other, sizeof(si_other)) == -1) {
    perror("sendto");
    return -1;
  }

  close(sock);


  return 0;
}


long long 
code_for_button(
                     struct remote_element_t * remote,
                     char * cfg_send) {


  if(!remote)
    return -1;

  if(!strcasecmp("voldown", cfg_send))
    return remote->button_voldown_n;
  if(!strcasecmp("volup", cfg_send))
    return remote->button_volup_n;
  if(!strcasecmp("mute", cfg_send))
    return remote->button_mute_n;
  if(!strcasecmp("chanup", cfg_send))
    return remote->button_chanup_n;
  if(!strcasecmp("chandown", cfg_send))
    return remote->button_chandown_n;
  if(!strcasecmp("up", cfg_send))
    return remote->button_up_n;
  if(!strcasecmp("down", cfg_send))
    return remote->button_down_n;
  if(!strcasecmp("left", cfg_send))
    return remote->button_left_n;
  if(!strcasecmp("right", cfg_send))
    return remote->button_right_n;
  if(!strcasecmp("enter", cfg_send))
    return remote->button_enter_n;
  if(!strcasecmp("rewind", cfg_send))
    return remote->button_rewind_n;
  if(!strcasecmp("play", cfg_send))
    return remote->button_play_n;
  if(!strcasecmp("forward", cfg_send))
    return remote->button_forward_n;
  if(!strcasecmp("stop", cfg_send))
    return remote->button_stop_n;
  if(!strcasecmp("pause", cfg_send))
    return remote->button_pause_n;
  if(!strcasecmp("menu", cfg_send))
    return remote->button_menu_n;
  if(!strcasecmp("exit", cfg_send))
    return remote->button_exit_n;


  return -1;
}


int
lirc_gesture(
                     int plane,
                     int button) {
  long long code;


  code = 0;

  switch(plane) {

    case KINECT_PLANE_FRONT:
      switch(button) {
        case KINECT_BUTTON_CENTRE:
          code = lirc_remote->button_enter_n;
          midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_BLURB,
            MIDNIGHTCODE_STATUS_STYLE_SOFT,
            "  Plane: FRONT, Button: CENTRE");
          break;
      }
      break;

    case KINECT_PLANE_MID:
      switch(button) {
        case KINECT_BUTTON_UP:
          code = lirc_remote->button_up_n;
          midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_BLURB,
            MIDNIGHTCODE_STATUS_STYLE_SOFT,
            "  Plane: MID, Button: UP");
          break;
        case KINECT_BUTTON_DOWN:
          code = lirc_remote->button_down_n;
          midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_BLURB,
            MIDNIGHTCODE_STATUS_STYLE_SOFT,
            "  Plane: MID, Button: DOWN");
          break;
        case KINECT_BUTTON_LEFT:
          code = lirc_remote->button_left_n;
          midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_BLURB,
            MIDNIGHTCODE_STATUS_STYLE_SOFT,
            "  Plane: MID, Button: LEFT");
          break;
        case KINECT_BUTTON_RIGHT:
          code = lirc_remote->button_right_n;
          midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_BLURB,
            MIDNIGHTCODE_STATUS_STYLE_SOFT,
            "  Plane: MID, Button: RIGHT");
          break;
      }
      break;

    case KINECT_PLANE_BACK:
      switch(button) {
        case KINECT_BUTTON_UP:
          code = lirc_remote->button_menu_n;
          midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_BLURB,
            MIDNIGHTCODE_STATUS_STYLE_SOFT,
            "  Plane: BACK, Button: UP");
          break;
        case KINECT_BUTTON_LEFT:
          code = lirc_remote->button_exit_n;
          midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_BLURB,
            MIDNIGHTCODE_STATUS_STYLE_SOFT,
            "  Plane: BACK, Button: LEFT");
          break;
        case KINECT_BUTTON_RIGHT:
          code = lirc_remote->button_play_n;
          midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_BLURB,
            MIDNIGHTCODE_STATUS_STYLE_SOFT,
            "  Plane: BACK, Button: RIGHT");
          break;
      }
      break;

  }

  if(code == 0)
    return -1;
  if(lirc_packet_code(lirc_remote, code) != 0)
    return -2;
  if(lirc_packet_send(lirc_remote) != 0)
    return -3;


  return 0;
}


int
kinect_state_manager(
                     int plane,
                     int button,
                     float time) {


  if(state_plane == plane && state_button == button) {
    if(time >= (state_time + state_time_delay)) {
      state_time = time;
      if(state_time_delay != button_time_min) {
        if(state_time_delay > button_time_min)
          state_time_delay -= button_time_delta;
        if(state_time_delay < button_time_min)
          state_time_delay = button_time_min;
      }
      return 1;
    }
  } else {
    state_plane = plane;
    state_button = button;
    state_time = time;
    state_time_delay = button_time_max;
    return 1;
  }


  return 0;
}


int
kinect_button_finder(
                     int plane,
                     float rel_x,
                     float rel_y,
                     float rel_z,
                     float time) {


  if(rel_x > centre_button_xmin && rel_x < centre_button_xmax) {
    if(rel_y > centre_button_ymin && rel_y < centre_button_ymax) {
      if(kinect_state_manager(plane, KINECT_BUTTON_CENTRE, time))
        return KINECT_BUTTON_CENTRE;
    } else {
      if(rel_y > up_button_ymin && rel_y < up_button_ymax) {
        if(kinect_state_manager(plane, KINECT_BUTTON_UP, time))
          return KINECT_BUTTON_UP;
      } else {
        if(rel_y > down_button_ymin && rel_y < down_button_ymax) {
          if(kinect_state_manager(plane, KINECT_BUTTON_DOWN, time))
            return KINECT_BUTTON_DOWN;
        }
      }
    }
  } else {
    if(rel_y > centre_button_ymin && rel_y < centre_button_ymax) {
      if(rel_x > left_button_xmin && rel_x < left_button_xmax) {
        if(kinect_state_manager(plane, KINECT_BUTTON_LEFT, time))
          return KINECT_BUTTON_LEFT;
      } else {
        if(rel_x > right_button_xmin && rel_x < right_button_xmax) {
          if(kinect_state_manager(plane, KINECT_BUTTON_RIGHT, time))
            return KINECT_BUTTON_RIGHT;
        }
      }
    }
  }


  return KINECT_BUTTON_NIL;
} 


void XN_CALLBACK_TYPE
kinect_SessionProgress(
                     const XnChar* strFocus,
                     const XnPoint3D& ptFocusPoint,
                     XnFloat fProgress,
                     void* UserCxt) {


  midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_BLURB,
    MIDNIGHTCODE_STATUS_STYLE_UNKNOWN, "Session: Progress ..");
}


void XN_CALLBACK_TYPE
kinect_SessionStart(
                     const XnPoint3D& ptFocusPoint,
                     void* UserCxt) {


  /* TODO: set LED to red */
  ini_x = ptFocusPoint.X;
  ini_y = ptFocusPoint.Y;
  ini_z = ptFocusPoint.Z;
  midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_BLURB,
    MIDNIGHTCODE_STATUS_STYLE_UNKNOWN, "Session: Start ..");
}


void XN_CALLBACK_TYPE
kinect_SessionEnd(
                     void* UserCxt) {


  /* TODO: set LED to off */
  midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_BLURB,
    MIDNIGHTCODE_STATUS_STYLE_UNKNOWN, "Session: .. End");
  midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_H1,
    MIDNIGHTCODE_STATUS_STYLE_NORMAL, "Awaiting gesture");
}


void XN_CALLBACK_TYPE
kinect_OnWaveCB(
                     void* cxt) {


  midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_BLURB,
    MIDNIGHTCODE_STATUS_STYLE_UNKNOWN, "Session: [wave]");
}


void XN_CALLBACK_TYPE
kinect_OnPointUpdate(
                     const XnVHandPointContext* pContext,
                     void* cxt) {
  float rel_x, rel_y, rel_z;
  int button;


  rel_x = ini_x - pContext->ptPosition.X;
  rel_y = ini_y - pContext->ptPosition.Y;
  rel_z = ini_z - pContext->ptPosition.Z;

  if(rel_z > front_plane_min && rel_z < front_plane_max) {
    if((button = kinect_button_finder(KINECT_PLANE_FRONT, rel_x, rel_y, rel_z,
      pContext->fTime))) {
      lirc_gesture(KINECT_PLANE_FRONT, button);
    }
  } else {
    if(rel_z > mid_plane_min && rel_z < mid_plane_max) {
      if((button = kinect_button_finder(KINECT_PLANE_MID, rel_x, rel_y, rel_z,
        pContext->fTime))) {
        lirc_gesture(KINECT_PLANE_MID, button);
      }
    } else {
      if(rel_z > back_plane_min && rel_z < back_plane_max) {
        if((button = kinect_button_finder(KINECT_PLANE_BACK, rel_x, rel_y,
        rel_z, pContext->fTime))) {
          if(button == KINECT_BUTTON_RIGHT) {
            /* TODO: kill session track */
            // g_pSessionManager->EndSession();
          } else {
            lirc_gesture(KINECT_PLANE_BACK, button);
          }
        }
      }
    }
  }
}


int
main(int argc, char **argv) {
  int cfg_help = 0;
  int cfg_lircd = 0;
  int cfg_lircrc = 0;
  char cfg_file[128];
  char cfg_remote[128];
  char cfg_send[128];
  int cfg_usage = 0;
  struct midnightcode_config_head_t * config_list_head_p;
  struct remote_head_t * remotes;
  struct remote_element_t * remote;
  long long button_n;
  void * tmp_void_p;
  int ret_i;

 
  /* Prepare switches */
  struct midnightcode_switch_t switches[] = {
    { "c", "config", "Config file [klirc.cfg]",
      MIDNIGHTCODE_PARSE_TYPE_STRING, 64, 0, &cfg_file },
    { "l", "lircd", "Generate lirc.conf-kinect and exit",
      MIDNIGHTCODE_PARSE_TYPE_NONE, 0, 0, &cfg_lircd },
    { "m", "lircrc", "Generate lircrc-mythtv and exit",
      MIDNIGHTCODE_PARSE_TYPE_NONE, 0, 0, &cfg_lircrc },
    { "r", "remote", "Which remote this button is for [kinect]",
      MIDNIGHTCODE_PARSE_TYPE_STRING, 64, 0, &cfg_remote },
    { "h", "help", "Display this help/usage screen",
      MIDNIGHTCODE_PARSE_TYPE_NONE, 0, 0, &cfg_help },
    { "u", "usage", "Display additional help/usage screen",
      MIDNIGHTCODE_PARSE_TYPE_NONE, 0, 0, &cfg_usage },
    { 0 }
  };


  /* Set-up initial values; */
  memset(&cfg_file, 0, sizeof(cfg_file));
  snprintf(cfg_file, sizeof(cfg_file), "klirc.cfg");
  memset(&cfg_remote, 0, sizeof(cfg_remote));
  snprintf(cfg_remote, sizeof(cfg_remote), "kinect");
  memset(&cfg_send, 0, sizeof(cfg_send));
  snprintf(cfg_send, sizeof(cfg_send), "play");
  cfg_lircd = 0;
  cfg_lircrc = 0;
  cfg_help = 0;
  cfg_usage = 0;

  midnightcode_list_init(config_struct_head,
                         midnightcode_config_element_t);
  tmp_void_p = (void *)&config_struct_head;
  config_list_head_p = (struct midnightcode_config_head_t *)tmp_void_p;
  midnightcode_list_init(remote_struct_head,
                         remote_element_t);
  tmp_void_p = (void *)&remote_struct_head;
  remotes = (struct remote_head_t *)tmp_void_p;


  /* 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);
  }

  /* Help */
  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);
    printf("\n");
    fflush(stdout);

    exit(1);
  }

  /* Usage */
  if(cfg_usage) {
    midnightcode_logo(0, MIDNIGHTCODE_COLOUR_STYLE_BLOOD_MOON);
    midnightcode_license(0, MIDNIGHTCODE_COLOUR_STYLE_BLOOD_MOON, 
      MIDNIGHTCODE_LICENSE_TEXT_COPYRIGHT);
    printf("\n");
    midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_TEXT,
      MIDNIGHTCODE_STATUS_STYLE_NORMAL,
      "Additional usage notes;");
    printf("\n\n");
    midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_TEXT,
      MIDNIGHTCODE_STATUS_STYLE_SOFT,
      "  To generate lircd.conf from this configuration;");
    printf("\n");
    midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_TEXT,
      MIDNIGHTCODE_STATUS_STYLE_NORMAL,
      "    %s -l > /etc/lirc/lircd.conf", argv[0]);
    printf("\n");
    midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_TEXT,
      MIDNIGHTCODE_STATUS_STYLE_SOFT,
      "  To run lircd as a UDP listener;");
    printf("\n");
    midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_TEXT,
      MIDNIGHTCODE_STATUS_STYLE_NORMAL,
      "    lircd --driver udp");
    printf("\n");
    midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_TEXT,
      MIDNIGHTCODE_STATUS_STYLE_SOFT,
      "  To generate a mythtv lircrc file;");
    printf("\n");
    midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_TEXT,
      MIDNIGHTCODE_STATUS_STYLE_NORMAL,
      "    %s -m > ~/.lirc/mythtv", argv[0]);
    printf("\n\n");
    midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_TEXT,
      MIDNIGHTCODE_STATUS_STYLE_SOFT,
      "  If the mythtv frontend server isn't localhost, then change");
    printf("\n");
    midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_TEXT,
      MIDNIGHTCODE_STATUS_STYLE_SOFT,
      "  the lirc_server entry in the klirc.cfg configuration file.");
    printf("\n");
    fflush(stdout);

    exit(1);
  }
  fflush(stdout);


  /* Generate ~/lirc/mythtv and exit */
  if(cfg_lircrc) {
    if((ret_i = midnightcode_config_read_file(cfg_file,
                           config_list_head_p,
                           MIDNIGHTCODE_CONFIG_TYPE_NEWSET,
                           MIDNIGHTCODE_CONFIG_OPTION_AUTONEW |
                           MIDNIGHTCODE_CONFIG_OPTION_MERGEARGS |
                           MIDNIGHTCODE_CONFIG_OPTION_SILENT )) != 0) {
      fprintf(stdout, "Error: failed to load and parse config file %s\n",
        cfg_file);
      fflush(stdout);
      exit(1);
    }
    if(remote_from_config(remotes, config_list_head_p) != 0) {
      fprintf(stdout, "Error: failed to retrieve configuration from file %s\n",
        cfg_file);
      fflush(stdout);
      exit(1);
    }
    remote_to_lircrc(remotes);
    exit(0);
  }


  /* Generate /etc/lirc/lircd.conf and exit */
  if(cfg_lircd) {
    if((ret_i = midnightcode_config_read_file(cfg_file,
                           config_list_head_p,
                           MIDNIGHTCODE_CONFIG_TYPE_NEWSET,
                           MIDNIGHTCODE_CONFIG_OPTION_AUTONEW |
                           MIDNIGHTCODE_CONFIG_OPTION_MERGEARGS |
                           MIDNIGHTCODE_CONFIG_OPTION_SILENT )) != 0) {
      fprintf(stdout, "Error: failed to load and parse config file %s\n",
        cfg_file);
      fflush(stdout);
      exit(1);
    }
    if(remote_from_config(remotes, config_list_head_p) != 0) {
      fprintf(stdout, "Error: failed to retrieve configuration from file %s\n",
        cfg_file);
      fflush(stdout);
      exit(1);
    }
    remote_to_lircd(remotes);
    exit(0);
  }


  /* 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, "Kinect UDP LIRC client: %s", argv[0]);

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


  midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_TEXT,
                           MIDNIGHTCODE_STATUS_STYLE_NORMAL,
                           "Config file [%s] load and parse",
                           cfg_file);
  if((ret_i = midnightcode_config_read_file(cfg_file,
                           config_list_head_p,
                           MIDNIGHTCODE_CONFIG_TYPE_NEWSET,
                           MIDNIGHTCODE_CONFIG_OPTION_AUTONEW |
                           MIDNIGHTCODE_CONFIG_OPTION_MERGEARGS |
                           MIDNIGHTCODE_CONFIG_OPTION_SILENT )) != 0) {
    midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_FAIL);
    fflush(stdout);
    exit(1);
  } else {
    midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_OKAY);
  }

  midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_TEXT,
                           MIDNIGHTCODE_STATUS_STYLE_NORMAL,
                           "Retrieving remote configuration");
  if(remote_from_config(remotes, config_list_head_p) != 0) {
    midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_FAIL);
    fflush(stdout);
    exit(1);
  } else {
    midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_OKAY);
  }

  /* Find target remote */
  lirc_remote = NULL;
  midnightcode_list_for_each(remote, remotes) {
    if(!strcasecmp(cfg_remote, remote->name))
      lirc_remote = remote;
  }

  midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_TEXT,
                           MIDNIGHTCODE_STATUS_STYLE_NORMAL,
                           "Remote match [%s]", lirc_remote->name);
  if(lirc_remote == NULL) {
    midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_FAIL);
    fflush(stdout);
    exit(1);
  } else {
    midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_OKAY);
  }

  midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_TEXT,
                           MIDNIGHTCODE_STATUS_STYLE_NORMAL,
                           "Packet Init");
  if(lirc_packet_init(lirc_remote) != 0) {
    midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_FAIL);
    fflush(stdout);
    exit(1);
  } else {
    midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_OKAY);
  }

  xn::Context context;
  XnVSessionGenerator* pSessionGenerator;

  XnStatus rc = context.InitFromXmlFile(SAMPLE_XML_FILE);
  midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_TEXT,
                           MIDNIGHTCODE_STATUS_STYLE_NORMAL,
                           "Initialise NITE context");
  if(rc != XN_STATUS_OK) {
    midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_FAIL);
    midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_BLURB,
      MIDNIGHTCODE_STATUS_STYLE_UNKNOWN, "Error: %s\n",
      xnGetStatusString(rc));
    fflush(stdout);
    exit(1);
  } else {
    midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_OKAY);
  }

  pSessionGenerator = new XnVSessionManager();
  rc = ((XnVSessionManager*)pSessionGenerator)->Initialize(&context,
    "Click", "RaiseHand");
  midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_TEXT,
                           MIDNIGHTCODE_STATUS_STYLE_NORMAL,
                           "Initialise NITE session manager");
  if(rc != XN_STATUS_OK) {
    midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_FAIL);
    midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_BLURB,
      MIDNIGHTCODE_STATUS_STYLE_UNKNOWN, "Error: %s\n",
      xnGetStatusString(rc));
    delete pSessionGenerator;
    fflush(stdout);
    exit(1);
  } else {
    midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_OKAY);
  }
  context.StartGeneratingAll();

  /* Register session */
  pSessionGenerator->RegisterSession(NULL, &kinect_SessionStart,
    &kinect_SessionEnd, &kinect_SessionProgress);

  init_signals(&g_bQuit);

  /* Register wave */
  XnVWaveDetector wc;
  wc.RegisterWave(NULL, kinect_OnWaveCB);
  wc.RegisterPointUpdate(NULL, kinect_OnPointUpdate);
  pSessionGenerator->AddListener(&wc);

  midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_H1,
    MIDNIGHTCODE_STATUS_STYLE_NORMAL, "Awaiting gesture");

  while(!g_bQuit) {
    context.WaitAndUpdateAll();
    ((XnVSessionManager*)pSessionGenerator)->Update(&context);
  }

  delete pSessionGenerator;
  context.Shutdown();

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

  exit(0);
}
