/* bluebox distributor
 * version 0.3.2 date 2007-07-18
 * Copyright (C) 2006-2007 Stefan Schuermans <stefan@blinkenarea.org>
 * Copyleft: GNU public license V2.0 - http://www.gnu.org/copyleft/gpl.html
 * a BlinkenArea project - http://www.blinkenarea.org/
 */

#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
     
#include "tools.h"
#include "config.h"
#include "bd_config.h"

//parsed settings from troia_dist configuration file
int bd_verbose;
in_addr_t bd_in_listen_addr;
in_port_t bd_in_listen_port;
unsigned int bd_in_timeout;
unsigned int bd_in_timeout_interval;
double bd_map_base, bd_map_spread, bd_map_gamma;
in_addr_t bd_out_bind_addr;
in_port_t bd_out_bind_port;
in_addr_t bd_out_ip_base, bd_out_ip_step;
in_port_t bd_out_dest_port;
unsigned short bd_out_dev_cnt, bd_out_ser_cnt, bd_out_pix_cnt;
unsigned char bd_out_command;
unsigned char bd_out_turn_off;
unsigned char bd_out_suffix;
unsigned short bd_out_suffix_cnt;
char bd_fmt_file[256];

//buffers used to parse settings
char bd_verbose_str[8];
char bd_in_listen_str[32], bd_in_timeout_str[16], bd_in_timeout_interval_str[16];
char bd_map_base_str[16], bd_map_spread_str[16], bd_map_gamma_str[16];
char bd_out_bind_str[32];
char bd_out_ip_base_str[16], bd_out_ip_step_str[16], bd_out_dest_port_str[16];
char bd_out_dev_cnt_str[16], bd_out_ser_cnt_str[16], bd_out_pix_cnt_str[16];
char bd_out_command_str[16], bd_out_turn_off_str[16], bd_out_suffix_str[16];
char bd_out_suffix_cnt_str[16];

//settings in troia_dist configuration file
st_config_setting bd_settings[] = 
{
  { "", "verbose", bd_verbose_str, sizeof( bd_verbose_str ), 0 },
  { "input", "listen", bd_in_listen_str, sizeof( bd_in_listen_str ), 0 },
  { "input", "timeout", bd_in_timeout_str, sizeof( bd_in_timeout_str ), 0 },
  { "input", "timeout_interval", bd_in_timeout_interval_str, sizeof( bd_in_timeout_interval_str ), 0 },
  { "mapping", "base", bd_map_base_str, sizeof( bd_map_base_str ), 0 },
  { "mapping", "spread", bd_map_spread_str, sizeof( bd_map_spread_str ), 0 },
  { "mapping", "gamma", bd_map_gamma_str, sizeof( bd_map_gamma_str ), 0 },
  { "output", "bind", bd_out_bind_str, sizeof( bd_out_bind_str ), 0 },
  { "output", "ip_base", bd_out_ip_base_str, sizeof( bd_out_ip_base_str ), 0 },
  { "output", "ip_step", bd_out_ip_step_str, sizeof( bd_out_ip_step_str ), 0 },
  { "output", "dest_port", bd_out_dest_port_str, sizeof( bd_out_dest_port_str ), 0 },
  { "output", "dev_cnt", bd_out_dev_cnt_str, sizeof( bd_out_dev_cnt_str ), 0 },
  { "output", "ser_cnt", bd_out_ser_cnt_str, sizeof( bd_out_ser_cnt_str ), 0 },
  { "output", "pix_cnt", bd_out_pix_cnt_str, sizeof( bd_out_pix_cnt_str ), 0 },
  { "output", "command", bd_out_command_str, sizeof( bd_out_command_str ), 0 },
  { "output", "turn_off", bd_out_turn_off_str, sizeof( bd_out_turn_off_str ), 0 },
  { "output", "suffix", bd_out_suffix_str, sizeof( bd_out_suffix_str ), 0 },
  { "output", "suffix_cnt", bd_out_suffix_cnt_str, sizeof( bd_out_suffix_cnt_str ), 0 },
  { "format", "file", bd_fmt_file, sizeof( bd_fmt_file ), 0 },
};

//get troia_dist configuration from configuration file
void bd_config_get( char * filename )
{
  char * p_colon, * p_addr_str, * p_port_str;
  int i, port;

  //read config file
  config_parse( filename, bd_settings, count( bd_settings ) );

  //get verbose flag
  bd_verbose = 0;
  for( i = 0; bd_verbose_str[i] != 0; i++ )
  {
    if( strchr( "+123456789yY", bd_verbose_str[i] ) )
    {
      bd_verbose = 1;
      break;
    }
    if( strchr( "-0nN", bd_verbose_str[i] ) )
    {
      bd_verbose = 0;
      break;
    }
  }

  //input

  //divide listen string into IP and port
  p_colon = strchr( bd_in_listen_str, ':' );
  if( p_colon == NULL )
  {
    printf( "config: input: no port found in listen address (\"%s\"), using 2323\n", bd_in_listen_str );
    p_addr_str = bd_in_listen_str;
    p_port_str = "2323";
  }
  else
  {
    p_addr_str = bd_in_listen_str;
    *p_colon = 0;
    p_port_str = p_colon + 1;
  }

  //parse listen IP
  bd_in_listen_addr = inet_addr( p_addr_str );
  if( bd_in_listen_addr == INADDR_NONE )
  {
    printf( "config: input: invalid listen IP (\"%s\"), using \"0.0.0.0\"\n", p_addr_str );
    bd_in_listen_addr = INADDR_ANY;
  }

  //convert listen port into number
  if( sscanf( p_port_str, "%i", &port ) != 1 )
  {
    printf( "config: input: error in format of listen port (\"%s\"), using 2323\n", p_port_str );
    port = 2323;
  }
  if( port < 0 || port > 65535 )
  {
    printf( "config: input: invalid listen port number (%d), using 2323\n", port );
    port = 2323;
  }
  bd_in_listen_port = (in_port_t)port;

  //convert timeout into number
  if( sscanf( bd_in_timeout_str, "%i", &bd_in_timeout ) != 1 )
  {
    printf( "config: input: error in format of timeout (\"%s\"), using 3 seconds\n", bd_in_timeout_str );
    bd_in_timeout = 3;
  }
  if( bd_in_timeout < 1 || bd_in_timeout > 300 )
  {
    printf( "config: input: invalid timeout (%d seconds), using 3 seconds\n", bd_in_timeout );
    bd_in_timeout = 3;
  }

  //convert timeout interval into number
  if( sscanf( bd_in_timeout_interval_str, "%i", &bd_in_timeout_interval ) != 1 )
  {
    printf( "config: input: error in format of timeout interval (\"%s\"), using 1 second1\n", bd_in_timeout_interval_str );
    bd_in_timeout_interval = 1;
  }
  if( bd_in_timeout_interval < 1 || bd_in_timeout_interval > bd_in_timeout )
  {
    printf( "config: input: invalid timeout interval (%d seconds), using 1 seconds\n", bd_in_timeout_interval );
    bd_in_timeout_interval = 1;
  }

  //mapping

  //parse base brighness value
  if( sscanf( bd_map_base_str, "%lf", &bd_map_base ) != 1 ||
      bd_map_base < 0.0 || bd_map_base > 1.0 )
  {
    printf( "config: mapping: error in format of base brightness value (\"%s\"), using 0.0\n", bd_map_base_str );
    bd_map_base = 0.0;
  }

  //parse brighness spread value
  if( sscanf( bd_map_spread_str, "%lf", &bd_map_spread ) != 1 ||
      bd_map_spread < 0.01 || bd_map_spread > 100.0 )
  {
    printf( "config: mapping: error in format of brighness spread value (\"%s\"), using 1.0\n", bd_map_spread_str );
    bd_map_spread = 1.0;
  }

  //parse gamma correction value
  if( sscanf( bd_map_gamma_str, "%lf", &bd_map_gamma ) != 1 ||
      bd_map_gamma < 0.01 || bd_map_gamma > 100.0 )
  {
    printf( "config: mapping: error in format of gamma correction value (\"%s\"), using 1.0\n", bd_map_gamma_str );
    bd_map_gamma = 1.0;
  }

  //output

  //divide bind string into IP and port
  p_colon = strchr( bd_out_bind_str, ':' );
  if( p_colon == NULL )
  {
    printf( "config: output: no port found in bind address (\"%s\"), using 2342\n", bd_out_bind_str );
    p_addr_str = bd_out_bind_str;
    p_port_str = "2342";
  }
  else
  {
    p_addr_str = bd_out_bind_str;
    *p_colon = 0;
    p_port_str = p_colon + 1;
  }

  //parse bind IP
  bd_out_bind_addr = inet_addr( p_addr_str );
  if( bd_out_bind_addr == INADDR_NONE )
  {
    printf( "config: output: invalid bind IP (\"%s\"), using \"0.0.0.0\"\n", p_addr_str );
    bd_out_bind_addr = INADDR_ANY;
  }

  //convert bind port into number
  if( sscanf( p_port_str, "%i", &port ) != 1 )
  {
    printf( "config: output: error in format of bind port (\"%s\"), using 2342\n", p_port_str );
    port = 2342;
  }
  if( port < 0 || port > 65535 )
  {
    printf( "config: output: invalid bind port number (%d), using 2342\n", port );
    port = 2342;
  }
  bd_out_bind_port = (in_port_t)port;

  //parse IP base
  bd_out_ip_base = inet_addr( bd_out_ip_base_str );
  if( bd_out_ip_base == INADDR_NONE )
  {
    printf( "config: output: invalid IP base (\"%s\"), using \"10.23.42.0\"\n", bd_out_ip_base_str );
    bd_out_ip_base = (in_addr_t)htonl( 0x0A172A00 );
  }

  //parse IP step
  bd_out_ip_step = inet_addr( bd_out_ip_step_str );
  if( bd_out_ip_step == INADDR_NONE )
  {
    printf( "config: output: invalid IP step (\"%s\"), using \"0.0.0.1\"\n", bd_out_ip_step_str );
    bd_out_ip_step = (in_addr_t)htonl( 0x00000001 );
  }

  //convert destination port into number
  if( sscanf( bd_out_dest_port_str, "%i", &port ) != 1 )
  {
    printf( "config: output: error in format of destination port (\"%s\"), using 2342\n", bd_out_dest_port_str );
    port = 2342;
  }
  if( port < 1 || port > 65535 )
  {
    printf( "config: output: invalid destination port number (%d), using 2342\n", port );
    port = 2342;
  }
  bd_out_dest_port = (in_port_t)port;

  //convert device count into number
  if( sscanf( bd_out_dev_cnt_str, "%hi", &bd_out_dev_cnt ) != 1 )
  {
    printf( "config: output: error in format of device count (\"%s\"), using 7\n", bd_out_dev_cnt_str );
    bd_out_dev_cnt = 7;
  }
  if( bd_out_dev_cnt <= 0 || bd_out_dev_cnt > 256 )
  {
    printf( "config: output: invalid device count (%d), using 7\n", bd_out_dev_cnt );
    bd_out_dev_cnt = 7;
  }

  //convert serial port count into number
  if( sscanf( bd_out_ser_cnt_str, "%hi", &bd_out_ser_cnt ) != 1 )
  {
    printf( "config: output: error in format of serial port count (\"%s\"), using 32\n", bd_out_ser_cnt_str );
    bd_out_ser_cnt = 32;
  }
  if( bd_out_ser_cnt <= 0 || bd_out_ser_cnt > 64 )
  {
    printf( "config: output: invalid serial port count (%d), using 32\n", bd_out_ser_cnt );
    bd_out_ser_cnt = 32;
  }

  //convert pixel count into number
  if( sscanf( bd_out_pix_cnt_str, "%hi", &bd_out_pix_cnt ) != 1 )
  {
    printf( "config: output: error in format of pixel count (\"%s\"), using 4\n", bd_out_pix_cnt_str );
    bd_out_pix_cnt = 4;
  }
  if( bd_out_pix_cnt <= 0 || bd_out_pix_cnt > 256 )
  {
    printf( "config: output: invalid pixel count (%d), using 4\n", bd_out_pix_cnt );
    bd_out_pix_cnt = 4;
  }

  //convert command byte into number
  if( sscanf( bd_out_command_str, "%hhi", &bd_out_command ) != 1 )
  {
    printf( "config: output: error in format of command byte (\"%s\"), using 0x82\n", bd_out_command_str );
    bd_out_command = 0x82;
  }
  if( bd_out_command < 0x80 )
  {
    printf( "config: output: invalid command byte (0x%02X), using 0x82\n", bd_out_command );
    bd_out_command = 0x82;
  }

  //convert turn_off byte into number
  if( sscanf( bd_out_turn_off_str, "%hhi", &bd_out_turn_off ) != 1 )
  {
    printf( "config: output: error in format of turn_off byte (\"%s\"), using 0x80\n", bd_out_turn_off_str );
    bd_out_turn_off = 0x80;
  }
  if( bd_out_turn_off < 0x80 )
  {
    printf( "config: output: invalid turn_off byte (0x%02X), using 0x80\n", bd_out_turn_off );
    bd_out_turn_off = 0x80;
  }

  //convert suffix byte into number
  if( sscanf( bd_out_suffix_str, "%hhi", &bd_out_suffix ) != 1 )
  {
    printf( "config: output: error in format of suffix byte (\"%s\"), using 0x55\n", bd_out_suffix_str );
    bd_out_suffix = 0x55;
  }
  if( bd_out_suffix >= 0x80 )
  {
    printf( "config: output: invalid suffix byte (0x%02X), using 0x55\n", bd_out_suffix );
    bd_out_suffix = 0x55;
  }

  //convert suffix count into number
  if( sscanf( bd_out_suffix_cnt_str, "%hi", &bd_out_suffix_cnt ) != 1 )
  {
    printf( "config: output: error in format of suffix count (\"%s\"), using 1\n", bd_out_suffix_cnt_str );
    bd_out_suffix_cnt = 1;
  }
  if( bd_out_suffix_cnt > 16 )
  {
    printf( "config: output: invalid suffix count (%d), using 1\n", bd_out_suffix_cnt );
    bd_out_suffix_cnt = 1;
  }

  //format

  //no format file
  if( bd_fmt_file[0] == 0 )
  {
    printf( "config: format: no format file, using \"troia_dist.bd_fmt\"\n" );
    strcpy( bd_fmt_file, "troia_dist.bd_fmt" );
  }
}
