// used for #ifdef
#include "config.h"

#include "startmenu.hpp"

//Input
#include "input.hpp"

#include "gettext.hpp"
#include "renderer.hpp"
#include "config.hpp"
#include "theme.hpp"
#include "graphics.hpp"
#include "updater.hpp"
#include "busy_indicator.hpp"
#include "shutdown.hpp"
#include "extra_menu.hpp"

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

#ifdef c_movie
#include "movie.hpp"
#endif

#ifdef c_picture
#include "pictures.hpp"
#endif

#ifdef c_game
#include "game.hpp"
#endif

#ifdef use_epg
#include "epg.hpp"
#endif

// Tv
#ifdef use_tv
#include "tv.hpp"
#endif

// ostringstream
#include <sstream>

#include <fstream>
#include <iostream>

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

startmenu_item::startmenu_item(const string& n, const string& a, const string& p) 
  : name(n), action(a), pic(p)
{}

Startmenu& Startmenu::get_instance() 
{
  static Startmenu s;
  return s;
}

Startmenu::StartmenuOpts::StartmenuOpts() 
{
  // header
  header = gettext("Options");

  // values
  vector<string> empty_values;

  // global options
  g_o = new Option(true, gettext("Global Options"), "Global Options", 0, 
		   empty_values, empty_values);
  val.push_back(g_o);

  val.push_back(go->dir_order);

  if (conf->p_cdpath().size() > 1) 
    val.push_back(go->cd_device);

#ifdef c_music

  a_o = new Option(true, gettext("Music Options"), "Music Options", 0, 
		   empty_values, empty_values);
  val.push_back(a_o);

  Audio *audio = Audio::get_instance();

  for (int i = 0; i < audio->opts.values().size(); ++i)
    if (((audio->opts.values()[i])->name != gettext("reload")) &&
	((audio->opts.values()[i])->name != gettext("sorting")))
      val.push_back(vector_lookup(audio->opts.values(), i));

#endif

#ifdef c_movie

  m_o = new Option(true, gettext("Movie Options"), "Movie Options", 0, 
		   empty_values, empty_values);
  val.push_back(m_o);

  Movie &mov = Movie::get_instance();

  for (int i = 0; i < mov.opts.values().size(); ++i)
    if ((mov.opts.values()[i]->name != gettext("reload")) &&
	((mov.opts.values()[i])->name != gettext("sorting")))
      val.push_back(vector_lookup(mov.opts.values(), i));

#endif

#ifdef c_picture

  p_o = new Option(true, gettext("Picture Options"), "Picture Options", 0, 
		   empty_values, empty_values);
  val.push_back(p_o);

  Pictures &pic = Pictures::get_instance();

  for (int i = 0; i < pic.opts.values().size(); ++i)
    if (((pic.opts.values()[i])->name != gettext("reload")) &&
	((pic.opts.values()[i])->name != gettext("sorting")))
      val.push_back(vector_lookup(pic.opts.values(), i));

#endif

#if 0 // not needed yet, comment in when needed
  //#ifdef c_game

  game_o = new Option(true, gettext("Game Options"), "Game Options", 0, 
		   empty_values, empty_values);
  val.push_back(game_o);

  Game &gam = Game::get_instance();

  for (int i = 0; i < gam.opts.values().size(); ++i)
    if (((gam.opts.values()[i])->name != gettext("reload")) &&
	((gam.opts.values()[i])->name != gettext("sorting")))
      val.push_back(vector_lookup(gam.opts.values(), i));

#endif

  load();
}

Startmenu::StartmenuOpts::~StartmenuOpts() 
{
#ifdef c_music
  delete a_o;
#endif

#ifdef c_movie
  delete m_o;
#endif

#ifdef c_picture
  delete p_o;
#endif

#if 0 // not needed yet, comment in when needed
  //#ifdef c_game
  delete game_o;
#endif

  // global
  delete g_o;
}

void Startmenu::StartmenuOpts::save() 
{
  Theme &theme = Theme::get_instance();
#ifdef c_music
  Audio_s *audio_s = Audio_s::get_instance();
  Audio *audio = Audio::get_instance();
#endif
#ifdef c_movie
  Movie &movie = Movie::get_instance();
#endif

  std::ofstream file;

  string path = conf->p_var_data_dir() + "options";

  file.open(path.c_str());
    
  if (!file) {
    DebugPrint perror(gettext("Could not write options to file ") + path,
		      Print::DEBUGGING, DebugPrint::CRITICAL, "STARTMENU");
  } else {
    for (vector<Option*>::const_iterator iter = val.begin(); iter != val.end(); ++iter)
      if (!(*iter)->type && (*iter)->name != gettext("reload") && (*iter)->name != gettext("shutdown on finish") ) 
	file << (*iter)->english_name << "," << vector_lookup((*iter)->english_values, (*iter)->pos) << std::endl;

    file << "cur_theme," << theme.cur_theme << std::endl;
#ifdef c_music
    file << "volume," << audio_s->p->getvol() << std::endl;
    file << "playlist_pos," << audio->get_real_playlist_pos() << std::endl;
    file << "play_now_warning," << audio->play_now_warning << std::endl;
#endif
#ifdef c_movie
    file << "imdb_warning," << movie.imdb_warning << std::endl;
#endif
  }

  file.close();
}

void Startmenu::StartmenuOpts::load()
{
  string path = conf->p_var_data_dir() + "options";

  std::ifstream In(path.c_str());

  if(!In){
    DebugPrint perror(gettext("Could not open options file ") + path,
		      Print::DEBUGGING, DebugPrint::CRITICAL, "STARTMENU");
  } else {

    string name, value, str;
    int i;
    
    while (getline(In, str)) {

      string::size_type comma = str.find(",");
      
      name = str.substr(0,comma);
      value = str.substr(comma+1);
      
      for (vector<Option*>::const_iterator iter = val.begin(); 
	   iter != val.end(); ++iter)
	if (!(*iter)->type && (*iter)->english_name == name) {
	  i = 0;
	  for (vector<string>::const_iterator iter2 = (*iter)->english_values.begin(); 
	       iter2 != (*iter)->english_values.end(); ++iter2, ++i)
	    if (value == (*iter2)) {
	      (*iter)->pos = i;
	      break;
	    }
	}
    }
  }
}

string Startmenu::mainloop()
{
  Input input;

  input_master->cur_map = &input_master->startmenu;
  module = "startmenu";

  list<startmenu_row>::iterator iter = startmenu.begin();
  list<startmenu_item>::iterator iter_row = iter->items.begin();

  print((*iter), (*iter_row));

  // updater
  Updater &updater = Updater::get_instance();

  BusyIndicator &busy_indicator = BusyIndicator::get_instance();
  Shutdown &sd = Shutdown::get_instance();

#ifdef c_music 
  Audio* audio = Audio::get_instance();
  audio->startup_updater();
#endif

#ifdef c_picture
  Pictures &pictures = Pictures::get_instance();
  pictures.startup_updater();
#endif

#ifdef use_epg
  Epg *epg = Epg::get_instance();
  epg->startup_updater();
#endif

  updater.start();
  updater.activate();

  bool update_needed = true;

  while (!(input.command == "action" && iter_row->action == "quit"))
    {
      if (update_needed)
	print((*iter), (*iter_row));

      busy_indicator.idle();

      input = input_master->device->get_input();

      busy_indicator.busy();
      
      if (sd.is_enabled()) {
      	sd.cancel();
      	continue;
      }

      update_needed = true;

      if (input.command == "prev")
 	{
 	  if (iter != startmenu.begin()) {
	    --iter;
	    iter_row = iter->items.begin();
	  } 
 	}
      else if (input.command == "next")
 	{
 	  list<startmenu_row>::iterator j = startmenu.end(); --j;
 	  if (iter != j) {
 	    ++iter;
	    iter_row = iter->items.begin();
	  } 
 	}
      else if (input.command == "left")
 	{
 	  if (iter_row != iter->items.begin())
 	    --iter_row;
 	}
      else if (input.command == "right")
 	{
 	  list<startmenu_item>::iterator j = iter->items.end(); --j;
 	  if (iter_row != j)
 	    ++iter_row;
 	}
      else if (input.command == "second_action")
	{
#ifdef c_music
	  // make back work for playlist commands
	  input_master->cur_map = &input_master->standard;

	  ExtraMenu em;

	  TimeFunctorv<Audio> t_player = TimeFunctorv<Audio>(Audio::get_instance(), &Audio::playlist_commands);
	  em.add_item(ExtraMenuItem(gettext("Playlist commands"), input_master->find_shortcut("playlist_commands"), &t_player));

	  em.mainloop();

	  // reset
	  input_master->cur_map = &input_master->startmenu;
#endif
	}
      else if (input.command == "action" && iter_row->action != "quit")
 	{
#ifdef music_hd
 	  if (iter_row->action == "audio_hd") {

	    if (!audio_loaded) {
	      audio->read_dirs();
	    } 

	    if (reload_audio) {
	      DialogWaitPrint pdialog(gettext("Reloading directories"), 1000);

	      audio->read_dirs();
	    }

	    bool error = false;

	    if (conf->p_audio_mode() == PLAYLIST) {
	      if (audio->playlist_size() == 0)
		error = true;
	    } else if (audio->audiolist_size() == 0)
	      error = true;
	    
	    if (error) {
	      
	      Print pdialog(Print::SCREEN);
	      pdialog.add_line(gettext("Could not find any music"));
	      pdialog.add_line("");
	      pdialog.add_line(gettext("Please specify a correct path "));
	      pdialog.add_line(gettext("in the configuration file"));
	      pdialog.print();

	      reload_audio = true;
	    
	    } else {

	      reload_audio = false;
	      audio_loaded = true;

	      if (conf->p_audio_mode() == PLAYLIST)
		audio->mainloop(Audio::PLAY);
	      else
		audio->mainloop(Audio::ADD);
	    }
	  }
#endif
#ifdef music_disc
	  if (iter_row->action == "audio_disc") {

	    if (cd->open()) {

	      Cd::cdstatus cdstatus = cd->check_cddrive();

	      if (cdstatus == Cd::OK)
		audio->detect_cdformat();

	      cd->close();
	    }
	  }
#endif
#ifdef music_radio
 	  if (iter_row->action == "audio_radio") {

	    if (conf->p_internet_radio().size() == 0) {
	      
	      Print pdialog(Print::SCREEN);
	      pdialog.add_line(gettext("Could not find any internet radio stations"));
	      pdialog.add_line(gettext("Please define some in the configuration file"));
	      pdialog.print();

	    } else
	      audio->choose_radio();
	  }
#endif
#ifdef movie_hd
	  if (iter_row->action == "movie_hd") {

	    Movie &mov = Movie::get_instance();

	    if (!movie_loaded) {
	      mov.read_dirs();
	      movie_loaded = true;
	    } 

	    if (reload_movie) {
	      DialogWaitPrint pdialog(gettext("Reloading directories"), 1000);

	      mov.read_dirs();
	    }
	    
	    if (mov.files_size() == 0) {
	      
	      Print pdialog(Print::SCREEN);
	      pdialog.add_line(gettext("Could not find any movies"));
	      pdialog.add_line("");
	      pdialog.add_line(gettext("Please specify a correct path "));
	      pdialog.add_line(gettext("in the configuration file"));
	      pdialog.print();

	      reload_movie = true;

	    } else {
	      reload_movie = false;

	      input_master->cur_map = &input_master->movies;
	      mov.mainloop();
	    }
	  }
#endif
#ifdef movie_disc
	  if (iter_row->action == "movie_disc") {

	    Movie &mov = Movie::get_instance();

	    if (cd->open()) {

	      Cd::cdstatus cdstatus = cd->check_cddrive();

	      if (cdstatus == Cd::OK) {
		input_master->cur_map = &input_master->movies;
		mov.determine_media();
	      } else {
		Print pdialog(gettext("The disc does not contain any recognizable files"),
			      Print::SCREEN);
	      }
	    }
	  }
#endif
#ifdef picture_hd
	  if (iter_row->action == "picture_hd") {

	    Pictures &pictures = Pictures::get_instance();

	    if (!pictures_loaded) {
	      pictures.read_dirs();
	      pictures_loaded = true;
	    } 

	    if (reload_pictures) {
	      DialogWaitPrint pdialog(gettext("Reloading directories"), 1000);

	      pictures.read_dirs();
	    }
	    
	    if (pictures.pic_list.size() == 0) {
	      
	      Print pdialog(Print::SCREEN);
	      pdialog.add_line(gettext("Could not find any pictures"));
	      pdialog.add_line("");
	      pdialog.add_line(gettext("Please specify a correct path "));
	      pdialog.add_line(gettext("in the configuration file"));
	      pdialog.print();

	      reload_pictures = true;

	    } else {
	      reload_pictures = false;
	      pictures.mainloop();
	    }
	  }
#endif
#ifdef picture_disc
	  if (iter_row->action == "picture_disc") {

	    Pictures &pictures = Pictures::get_instance();

	    if (cd->open()) {
	      Cd::cdstatus cdstatus = cd->check_cddrive();

	      if (cdstatus == Cd::OK)
		pictures.play_pictures_cd();

	      cd->close();
	    }
	  }
#endif
#ifdef game_hd
	  if (iter_row->action == "game_hd") {

	    Game &gam = Game::get_instance();

	    if (!game_loaded) {
	      gam.read_dirs();
	      game_loaded = true;
	    } 

	    if (reload_game) {
	      DialogWaitPrint pdialog(gettext("Reloading directories"), 1000);

	      gam.read_dirs();
	    }
	    
	    if (gam.files_size() == 0) {
	      
	      Print pdialog(Print::SCREEN);
	      pdialog.add_line(gettext("Could not find any games"));
	      pdialog.add_line("");
	      pdialog.add_line(gettext("Please specify a correct path "));
	      pdialog.add_line(gettext("in the configuration file"));
	      pdialog.print();

	      reload_game = true;

	    } else {
	      reload_game = false;

	      input_master->cur_map = &input_master->movies;
	      gam.mainloop();
	    }
	  }
#endif
#ifdef game_disc
	  if (iter_row->action == "game_disc") {

	    Game &gam = Game::get_instance();

	    if (cd->open()) {

	      Cd::cdstatus cdstatus = cd->check_cddrive();

	      if (cdstatus == Cd::OK) {
		input_master->cur_map = &input_master->movies;
		gam.determine_media();
	      }
	    }
	  }
#endif
#ifdef use_epg
	  if (iter_row->action == "epg") {
	    if (!epg_loaded) {
	      epg->read_db();
	      epg_loaded = true;
	    } 

	    if (reload_epg) {
	      DialogWaitPrint pdialog(gettext("Rereading TV listings file"), 1000);

	      epg->read_db();
	    }
	    
	    if (!epg->check_db())
	      reload_epg = true;
	    else {
	      reload_epg = false;
	      epg->mainloop();
	    }
	  }
#endif
#ifdef use_media
#ifdef openclose
	  if (iter_row->action == "openclose") 
	    cd->openclose_cddrive();
#endif
#endif

	  if (iter_row->action == "themes") {
	    busy_indicator.idle();

	    Theme &theme = Theme::get_instance();
	    theme.mainloop();
	  } else if (iter_row->action == "options") {
	    busy_indicator.idle();

	    opts.mainloop();
	  }

#ifdef use_tv
	  if (iter_row->action == "tv") {
	    Tv::get_instance().mainloop();
	  }
#endif          

	  // set the input map back to startmenu
	  input_master->cur_map = &input_master->startmenu;
	  module = "startmenu";
	}
      // make back work for playlist commands
      input_master->cur_map = &input_master->standard;
      update_needed = !general_loop_check(input);
      // reset
      input_master->cur_map = &input_master->startmenu;
    }
  return "quit";
}

void Startmenu::print(const startmenu_row& row, const startmenu_item& current_item)
{
  render->prepare_new_image();

  render->current.add(new PObj(themes->startmenu_background, 0, 0, 0, SCALE_FULL));

  int y = 120;
  int x = 0;

  if (conf->p_h_res() == 405)
    y = 40;
  
  for (list<startmenu_row>::const_iterator iter = startmenu.begin(); iter != startmenu.end(); ++iter)
    {
      if (iter->name == row.name) {
	for (list<startmenu_item>::const_iterator iter2 = iter->items.begin(); iter2 != iter->items.end(); ++iter2) {
	  if (current_item.name == iter2->name) {
	    
	    render->current.add(new PObj("startmenu/marked-left.png", 65, (y-2), 1, NOSCALING));

	    if (x == 70)
	      render->current.add(new PObj("startmenu/marked2.png", 90, (y-2), conf->p_v_res()-220+x-50, 1));
	    else if (x == 140)
	      render->current.add(new PObj("startmenu/marked3.png", 90, (y-2), conf->p_v_res()-220+x-50, 1));
	    else
	      render->current.add(new PObj("startmenu/marked.png", 90, (y-2), conf->p_v_res()-220+x-50, 1));
	    
	    render->current.add(new PObj("startmenu/marked-right.png", 90+conf->p_v_res()-220+x-50, (y-2), 1, NOSCALING));

	    render->current.add(new TObj(iter2->name, "Vera/22", 75, y+5, 
					 themes->startmenu_font1, themes->startmenu_font2, 
					 themes->startmenu_font3, 2));
	  }
	  render->current.add(new PObj(iter2->pic, conf->p_v_res()-220+x, y, 2, NOSCALING));
	  x += 70;
	}
      } else {
	render->current.add(new TObj(iter->name, "Vera/22", 75, y+5, 
				     themes->startmenu_font1, themes->startmenu_font2, 
				     themes->startmenu_font3, 2));
	for (list<startmenu_item>::const_iterator iter2 = iter->items.begin(); iter2 != iter->items.end(); ++iter2) {
	  render->current.add(new PObj(iter2->pic, conf->p_v_res()-220+x, y, 2, NOSCALING));
	  x += 70;
	}
      }
      x = 0;
      y += 51;
    }

  // render the logo in the startmenu
  render->current.add(new PObj("logo.png", conf->p_v_res()-85, conf->p_h_res()-41, 2, NOSCALING));

  render->draw("startmenu");

  render->image_mut.leaveMutex();
}

Startmenu::Startmenu()
  : movie_loaded(false), pictures_loaded(false), reload_audio(false), reload_movie(false), 
    reload_pictures(false), reload_epg(false), audio_loaded(false), epg_loaded(false), 
    game_loaded(false), reload_game(false),
    epg_update_running(false), module(""), last_module("")
{
  generate_startmenu();
}

void Startmenu::generate_startmenu()
{
  // generate menues.
  list<startmenu_row> start_menu;
  startmenu_row row;

#ifdef music_hd
  if (conf->p_audio_mode() != NONE)
    // add audio_hd button
#if !defined(music_disc) && !defined(music_radio)
    row.items.push_back(startmenu_item(gettext("Play music"), "audio_hd", 
				       themes->startmenu_music_dir));
#else
    row.items.push_back(startmenu_item(gettext("Play music from harddrive"), 
				       "audio_hd", themes->startmenu_music_dir));
#endif
#endif

#ifdef music_disc
  if (conf->p_cdpath().size() > 0)
    // add cd button
#if !defined(music_hd) && !defined(music_radio)
    row.items.push_back(startmenu_item(gettext("Play music"), "audio_disc", 
				       themes->startmenu_music_cd));
#else
    row.items.push_back(startmenu_item(gettext("Play music from disc"), 
				       "audio_disc", 
				       themes->startmenu_music_cd));
#endif
#endif

#ifdef music_radio
  // add cd button
  row.items.push_back(startmenu_item(gettext("Play internet radio"), 
				     "audio_radio", themes->startmenu_radio));
#endif

  if (row.items.size() != 0) {
    row.name = gettext("Music");
    start_menu.push_back(row);

    row.items.clear();
  }

#ifdef movie_hd
  if (conf->p_movie_mode())
    // add movie button
#ifdef movie_disc
    row.items.push_back(startmenu_item(gettext("Play movies from harddrive"), 
				       "movie_hd", themes->startmenu_movie_dir));
#else
    row.items.push_back(startmenu_item(gettext("Play movies"), "movie_hd", 
				       themes->startmenu_movie_dir));
#endif
#endif

#ifdef movie_disc
  if (conf->p_cdpath().size() > 0)
    // add dvd/vcd/svcd button
#ifdef movie_hd
    row.items.push_back(startmenu_item(gettext("Play a dvd/vcd/svcd"), 
				       "movie_disc", 
				       themes->startmenu_movie_cd));
#else
    row.items.push_back(startmenu_item(gettext("Play movies"), "movie_disc", 
				       themes->startmenu_movie_cd));
#endif
#endif

  if (row.items.size() != 0) {
    row.name = gettext("Movies");
    start_menu.push_back(row);

    row.items.clear();
  }

#ifdef picture_hd
#ifdef picture_disc
  row.items.push_back(startmenu_item(gettext("View pictures from harddrive"), 
				     "picture_hd", 
				     themes->startmenu_pictures_dir));
#else
  row.items.push_back(startmenu_item(gettext("View pictures"), "picture_hd", 
				     themes->startmenu_pictures_dir));
#endif
#endif

#ifdef picture_disc
  if (conf->p_cdpath().size() > 0)
    // add cd button
#ifdef picture_hd
    row.items.push_back(startmenu_item(gettext("View pictures from cd"), 
				       "picture_disc", 
				       themes->startmenu_pictures_cd));
#else
    row.items.push_back(startmenu_item(gettext("View pictures"), 
				       "picture_disc", 
				       themes->startmenu_pictures_cd));
#endif
#endif

  if (row.items.size() != 0) {
    row.name = gettext("Pictures");
    start_menu.push_back(row);

    row.items.clear();
  }

#ifdef game_hd
     // add game button
#ifdef game_disc
    row.items.push_back(startmenu_item(gettext("Play games from harddrive"), 
				       "game_hd", themes->startmenu_game_dir));
#else
    row.items.push_back(startmenu_item(gettext("Play games"), "game_hd", 
				       themes->startmenu_game_dir));
#endif
#endif

#ifdef game_disc
  if (conf->p_cdpath().size() > 0)
    // add PSX cd/dvd rooms button
#ifdef game_hd
  if (conf->p_psxemu_enable())
    row.items.push_back(startmenu_item(gettext("Play PSX or roms from cd/dvd"), 
				       "game_disc", 
				       themes->startmenu_game_cd));
  else
	row.items.push_back(startmenu_item(gettext("Play roms from cd/dvd"), 
				       "game_disc", 
				       themes->startmenu_game_cd));				      
#else
    row.items.push_back(startmenu_item(gettext("Play games"), "game_disc", 
				       themes->startmenu_game_cd));
#endif
#endif

 if (row.items.size() != 0) {
    row.name = gettext("Games");
    start_menu.push_back(row);

    row.items.clear();
  }

#ifdef use_epg
  // add epg button
  row.items.push_back(startmenu_item(gettext("View TV Program Guide"), "epg", 
				     themes->startmenu_epg));
#endif

#ifdef use_tv  
  row.items.push_back(startmenu_item(gettext("Watch TV"), "tv", 
                                       themes->startmenu_tv));
#endif
          
 if (row.items.size() != 0) {
   row.name = gettext("TV");
   start_menu.push_back(row);

   row.items.clear();
 }

  if (start_menu.size() == 0) {
    DebugPrint perror(gettext("You disabled every feature, mms is useless."),
		      Print::INFO, DebugPrint::INFO, "STARTMENU");
    exit(0);
  }
  
  // options button
  row.items.push_back(startmenu_item(gettext("Options"), "options", 
				     themes->startmenu_options));

  // themes button
  row.items.push_back(startmenu_item(gettext("Themes"), "themes", 
				     themes->startmenu_themes));


#ifdef use_media
#ifdef openclose
  if (conf->p_cdpath().size() > 0)
    // Open/Close Tray button
    row.items.push_back(startmenu_item(gettext("Open/Close Tray"), "openclose",
				       themes->startmenu_open_close));
#endif
#endif

  if (row.items.size() != 0) {
    row.name = gettext("Options");
    start_menu.push_back(row);  

    row.items.clear();
  }

  row.name = gettext("Quit");
  // quit button
  row.items.push_back(startmenu_item(gettext("Quit"), "quit", 
				     themes->startmenu_quit));
  start_menu.push_back(row);
  row.items.clear();

  startmenu = start_menu;
}
