#include "config.h"

#include "renderer.hpp"
#include "config.hpp"
#include "common.hpp"
#include "startmenu.hpp"
#include "updater.hpp"
#include "print.hpp"

#ifdef c_music 
#include "audio.hpp"
#endif

// lcd
#include "display.hpp"

#include "replay.hpp"

// Input
#include "input.hpp"
#include "keyboard.hpp"
#include "replay_device.hpp"
#ifdef use_lirc
#include "lirc.hpp"
#endif
#ifdef use_evdev
#include "evdev.hpp"
#endif
#include "config.hpp"

// Output
#ifdef use_dxr3
#include "dxr3out.hpp"
#endif
#ifdef use_dvb
#include "dvbout.hpp"
#endif
#ifdef use_mpeg_write
#include "mpeg_out.hpp"
#endif
#ifdef use_fbdev
#include "fbdevout.hpp"
#endif
#ifdef use_vga
#include "vgaglout.hpp"
#endif
#ifdef use_sdl
#include "sdlout.hpp"
#endif

/* SIGHUP */
#include <signal.h>

#include <unistd.h>
#include <sstream>

#include <iostream>

// --version
#include <getopt.h>

#include "gettext.hpp"

#ifdef use_nls
#include <langinfo.h>
#include <locale.h>
#endif

using std::list;
using std::string;

void start()
{
  Startmenu &startmenu = Startmenu::get_instance();

#ifdef c_music 
  // open the autosaved playlist
  Audio *audio = Audio::get_instance();
  audio->load_last_playlist();
#endif

  //Print the info and wait for input
  for (string output = ""; output != "quit";)
    output = startmenu.mainloop();
}

void print_options_header() 
{
  Print print(gettext("mms options:") + string("\n"), Print::INFO);
}

void print_options_footer() 
{
  Print print(Print::INFO);
  print.add_line("");
  print.add_line(gettext("<relative> : filename relative to config directory"));
  print.add_line(gettext("<absolute> : full path filename"));
  print.print();
}

void print_output_options() 
{
  string output = gettext(" -o : output device [");
  list<string> options;

#ifdef use_dxr3
  options.push_back("dxr3");
#endif
#ifdef use_dvb
  options.push_back("dvb");
#endif
#ifdef use_mpeg_write
  options.push_back("mpeg");
#endif
#ifdef use_fbdev
  options.push_back("fbdev");
#endif
#ifdef use_vga
  options.push_back("vga");
#endif
#ifdef use_sdl
  options.push_back("sdl");
#endif

  for (list<string>::iterator i = options.begin(), end = options.end();
       i != end; ++i) {
    output += *i;
    if (*i != options.back())
      output += ",";
  }
  
  output += "]";
  
  Print print(output, Print::INFO);
}

void print_input_options() 
{
  string output = gettext(" -i : input device [");
  list<string> options;

#ifdef use_lirc
  options.push_back("lirc");
#endif
#ifdef use_evdev
  options.push_back("evdev");
#endif
  options.push_back("keyboard");
  options.push_back("replay");

  for (list<string>::iterator i = options.begin(), end = options.end();
       i != end; ++i) {
    output += *i;
    if (*i != options.back())
      output += ",";
  }
  
  output += "]";
  
  Print print(output, Print::INFO);
}

void print_misc_options()
{
  string output = gettext(" -d : daemonize (run in background)") + string("\n") + gettext(" -c <absolute> : use alternate config directory");
  Print print (output, Print::INFO);
}

void print_replay_options()
{
  string output = gettext(" -r <relative> : use replay file as input (requires replay as input device)") + string("\n") + 
    gettext(" -b : benchmark mms by replaying as fast as possible (requires replay as input device) ") + string("\n") + 
    gettext(" -w <relative> : write replay to specified file");
    
  Print print (output, Print::INFO);
}

static struct option longopts[] =
{
  { "conf", 1, 0, 'c' },
  { "daemon", 0, 0, 'd' },
  { "replay-read", 1, 0, 'r' },
  { "benchmark", 0, 0, 'b' },
  { "replay-write", 1, 0, 'w' },
  { "version", 0, 0, 'v' },
  { "help", 0, 0, 'h' },
};

int main(int argc,char* argv[])
{
  signal(SIGINT, clean_up);
  signal(SIGTERM, clean_up);
  signal(SIGHUP, clean_up);
  signal(SIGKILL, clean_up);

  Config *conf = Config::get_instance();

  // overwriting variables
  string homedir = "";
  bool benchmark = false;
  string replay_read = "", replay_write = "", outdev = "";
  int indev = -1;

#ifdef use_nls
  // gettext
  setlocale(LC_ALL, ""); /* set from the environment variables */
  bindtextdomain("mms", prefix"/share/locale");
  bind_textdomain_codeset("mms", nl_langinfo(CODESET));
  textdomain("mms");
#endif

  int c;
  
  while ((c = getopt_long(argc, argv, "abc:dfhlno:w:r:i:t:O:vV",
			  longopts, NULL)) != -1)
    switch (c) {

    case 'b':
      benchmark = true;
      break;
    case 'c': // config file
      homedir = optarg;
      break;
    case 'd': // daemonize
      switch (fork()) {
      case 0: break; // my child will go on
      case -1:
	fprintf(stderr,"Error forking\n");
	return -1;
      default:
	_exit(0);
      }
      if (setsid() < 0) // we won't get SIGHUP after this
	return -1;
      switch (fork()) {
      case 0: break; // my child will go on
      case -1: // error
	fprintf(stderr,"Error forking\n");
	return -1;
      default: _exit(0); // the parent process exits
      }
      break;
    case 'h': 
      print_options_header();
      print_misc_options();
      print_replay_options();
      print_input_options();
      print_output_options();
      print_options_footer();
      exit(0);
      break;
    case 'r':
      replay_read = optarg;
      break;
    case 'w':
      replay_write = optarg;
      break;
    case 'o':
      if (!strcmp(optarg,"h") || !strcmp(optarg,"help")) {
	print_options_header();
	print_output_options();
	exit(0);
      }
      outdev = optarg;
      break;
    case 'i':
      if (!strcmp(optarg,"h") || !strcmp(optarg,"help")) {
	print_options_header();
	print_input_options();
	exit(0);
      }

      int opt;
      
      if (!strcmp(optarg,"lirc"))
	opt = LIRC;
      else if (!strcmp(optarg,"evdev"))
	opt = EVDEV;
      else if (!strcmp(optarg,"keyboard"))
	opt = KEYBOARD;
      else if (!strcmp(optarg,"replay"))
	opt = REPLAY;

      indev = opt;

      break;
    case 'v':		/* version */
    case 'V':		/* version */
      {
	DebugPrint perror("My Media System: 1.0.8.5",
			  Print::INFO, DebugPrint::INFO);
      }
      exit (0);
    }

  // check if ~/.mms/ is available, if so use it when possible
  if (homedir.empty()) {
    homedir = getenv("HOME");
    if (file_exists(homedir + "/.mms/")) {
      conf->s_homedir(homedir + "/.mms/");
      conf->s_var_data_dir(homedir + "/.mms/");
    } else {
      conf->s_homedir("/etc/mms/");
      conf->s_var_data_dir("/var/lib/mms/");
    }
  }
  else {
    if (homedir[homedir.size()-1] != '/')
      homedir += '/';
    
    conf->s_homedir(homedir);
    conf->s_var_data_dir(homedir);
  }

  // now pass the configuration file using the correct path
  conf->parse_configuration_file();

  // set options from configuration line to overwrite config
  if (benchmark)
    conf->s_benchmark(true);

  if (!replay_read.empty())
      conf->s_replay_read(replay_read);

  if (!replay_write.empty())
      conf->s_replay_write(replay_write);

  if (!outdev.empty())
      conf->s_outdev(outdev);

  if (indev != -1)
      conf->s_input(indev);
  
  Print print(gettext("Using ") + conf->p_homedir() + gettext(" as configuration directory"), Print::INFO);

  if (conf->p_outdev() == "dxr3" || conf->p_outdev() == "dvb"||
      conf->p_outdev() == "mpeg") {
    if (conf->p_v_res() != 720 || (conf->p_h_res() != 540 && conf->p_h_res() != 405)) {
      DebugPrint perror(gettext("DVB and DXR3 output only supports the resolution 720x540 (4:3) or 720x405 (16:9)"), Print::DEBUGGING, DebugPrint::INFO, "MMS");
      exit(1);
    }
  }

#ifdef player_xine
  // check if we can change priority
  if (nice(-2) == -1) {
    conf->s_priority_change(false);
    std::cerr << gettext("WARNING: unable to set nice level") << std::endl;
    std::cerr << gettext("you might experience skipping sound") << std::endl;
  } else {
    nice(2);
    conf->s_priority_change(true);
  }
#endif

  // make sure the temporary directory is available
  string temp_loc = conf->p_temp_image_loc();
  if (temp_loc[temp_loc.size()-1] != '/')
    temp_loc += '/';
  
  if (!file_exists(temp_loc))
    create_if_not_exists(temp_loc);

  Render *render = Render::get_instance();
  render->set_theme();

  // Output
  if (conf->p_outdev() == "dxr3") {
#ifdef use_dxr3
    render->device = new Dxr3;
#else
    DebugPrint perror(Print::DEBUGGING, DebugPrint::INFO, "MMS");
    perror.add_line(gettext("mms was compiled without dxr3 support."));
    perror.add_line(gettext("Please choose another output device."));
    perror.print();
    exit(0);
#endif
  } else if (conf->p_outdev() == "dvb") {
#ifdef use_dvb
    render->device = new Dvb;
#else
    DebugPrint perror(Print::DEBUGGING, DebugPrint::INFO, "MMS");
    perror.add_line(gettext("mms was compiled without dvb support."));
    perror.add_line(gettext("Please choose another output device."));
    perror.print();
    exit(0);
#endif
  } else if (conf->p_outdev() == "mpeg") {
#ifdef use_mpeg_write
    render->device = new MpegOut;
#else
    DebugPrint perror(Print::DEBUGGING, DebugPrint::INFO, "MMS");
    perror.add_line(gettext("mms was compiled without mpeg support."));
    perror.add_line(gettext("Please choose another output device."));
    perror.print();
    exit(0);
#endif
  } else if (conf->p_outdev() == "fbdev") {
#ifdef use_fbdev
    render->device = new Fbdev;
#else
    DebugPrint perror(Print::DEBUGGING, DebugPrint::INFO, "MMS");
    perror.add_line(gettext("mms was compiled without framebuffer support."));
    perror.add_line(gettext("Please choose another output device."));
    perror.print();
    exit(0);
#endif
  } else if (conf->p_outdev() == "sdl") {
#ifdef use_sdl
    render->device = new SDLdev;
#else
    DebugPrint perror(Print::DEBUGGING, DebugPrint::INFO, "MMS");
    perror.add_line(gettext("mms was compiled without SDL support."));
    perror.add_line(gettext("Please choose another output device."));
    perror.print();
    exit(0);
#endif
  } else if (conf->p_outdev() == "vga") {
#ifdef use_vga
    render->device = new VGAGLdev;
#else
    DebugPrint perror(Print::DEBUGGING, DebugPrint::INFO, "MMS");
    perror.add_line(gettext("mms was compiled without vgagl support."));
    perror.add_line(gettext("Please choose another output device."));
    perror.print();
    exit(0);
#endif
  }
  else {
    DebugPrint perror(gettext("No output device specified! Use the -o option."),
		      Print::DEBUGGING, DebugPrint::INFO, "MMS");
    exit(1);
  }

  // First init render device, so that SDL may initialize itself.
  // Because input device need it too.

  render->device->init();

  Config *config = Config::get_instance();

  // lcd
  if (config->p_lcd()) {

    display_init();
    lcdprint("widget_set MMS line0 1 1 {   My Media System  }\n");
    lcdprint("widget_set MMS line1 1 2 {                    }\n");

  }

  // start replaysaver and replayer on the same time
  ReplaySaver::get_instance();

  Input_master *Input_master = Input_master::get_instance();

  // Input
  if (conf->p_input() == LIRC) {
#ifdef use_lirc
    Input_master->device = new Lirc;
#else
    DebugPrint perror(Print::DEBUGGING, DebugPrint::INFO, "MMS");
    perror.add_line(gettext("mms was compiled without lirc support."));
    perror.add_line(gettext("Please choose another input device."));
    perror.print();
    exit(0);
#endif
  } else if (conf->p_input() == EVDEV) {
#ifdef use_evdev
    Input_master->device = new Evdev;
#else
    DebugPrint perror(Print::DEBUGGING, DebugPrint::INFO, "MMS");
    perror.add_line(gettext("mms was compiled without evdev support."));
    perror.add_line(gettext("Please choose another input device."));
    perror.print();
    exit(0);
#endif
  } else if (conf->p_input() == KEYBOARD) {
    Input_master->device = new Keyboard;
  } else if (conf->p_input() == REPLAY) {
    Input_master->device = new ReplayDevice;
  }
  else {
    DebugPrint perror(gettext("No input device specified! Use the -i option."),
		      Print::DEBUGGING, DebugPrint::INFO, "MMS");
    exit(1);
  }

  Input_master->device->start();
  render->device->start();

  // set the audio device after everything else has been setup correctly
  render->set_audio_state();

  start();

  deinit_mms(false);

  exit(0);
}
