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

#include "libfspp.hpp"

#include "movie.hpp"

//Input
#include "input.hpp"

#include "common.hpp"
#include "gettext.hpp"
#include "graphics.hpp"

#include "startmenu.hpp"
#include "extra_menu.hpp"
#include "updater.hpp"
#include "busy_indicator.hpp"
#include "shutdown.hpp"

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

// ostringstream
#include <sstream>
#include <fstream>

#include <iostream>

#include <cassert>

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

Movie::MovieOpts::MovieOpts() 
{
  // header
  header = gettext("Movie Options");

  // values

  // reload
  vector<string> none;
  reload_p = new Option(false, gettext("reload"), "reload", 0, none, none);
  val.push_back(reload_p);

  // sorting order
  val.push_back(go->dir_order);

  vector<string> audio_mode_values;

  audio_mode_values.push_back(gettext("stereo"));
  audio_mode_values.push_back(gettext("5.1 digital"));
  audio_mode_values.push_back(gettext("5.1 analog"));

  vector<string> english_audio_mode_values;

  english_audio_mode_values.push_back("stereo");
  english_audio_mode_values.push_back("5.1 digital");
  english_audio_mode_values.push_back("5.1 analog");

  audio_mode_p = new Option(false, gettext("audio mode"), "audio mode", 0, 
			    audio_mode_values, english_audio_mode_values);
  val.push_back(audio_mode_p);

  vector<string> dvd_audio_lang_values;

  dvd_audio_lang_values.push_back("en");
  dvd_audio_lang_values.push_back("de");
  dvd_audio_lang_values.push_back("es");
  dvd_audio_lang_values.push_back("nl");
  dvd_audio_lang_values.push_back("fr");
  dvd_audio_lang_values.push_back("pt");
  dvd_audio_lang_values.push_back("hu");
  dvd_audio_lang_values.push_back("da");
  dvd_audio_lang_values.push_back("sv");
  dvd_audio_lang_values.push_back("no");
  dvd_audio_lang_values.push_back("fi");
  dvd_audio_lang_values.push_back("it");
  dvd_audio_lang_values.push_back("pl");

  if (conf->p_dvd_player() == MPLAYER) {
    dvd_audio_lang_p = new Option(false, gettext("dvd audio language"), "dvd audio language", 
				  0, dvd_audio_lang_values, dvd_audio_lang_values);
    val.push_back(dvd_audio_lang_p);

    base = 0;
  } else 
    base = -1;
  
  vector<string> sub_lang_values;

  sub_lang_values = dvd_audio_lang_values;
  sub_lang_values.push_back(gettext("none"));

  vector<string> english_sub_lang_values;

  english_sub_lang_values = dvd_audio_lang_values; // this is english
  english_sub_lang_values.push_back("none");

  sub_lang_p = new Option(false, gettext("subtitle language"), "subtitle language", 0, 
			  sub_lang_values, english_sub_lang_values);
  val.push_back(sub_lang_p);

  if (conf->p_movie_player() == MPLAYER) {

    vector<string> vcdtitle_values;
    
    for (int i = 1; i < 100; ++i)
      vcdtitle_values.push_back(conv::itos(i));
    
    vcdtitle_p = new Option(false, gettext("vcd title"), "vcd title", 0, 
			    vcdtitle_values, vcdtitle_values);
    val.push_back(vcdtitle_p);
  }
  
  if (conf->p_dvd_player() == MPLAYER) {

    vector<string> dvdtitle_values;
    
    for (int i = 1; i < 100; ++i)
      dvdtitle_values.push_back(conv::itos(i));
    
    dvdtitle_p = new Option(false, gettext("dvd title"), "dvd title", 0, 
			    dvdtitle_values, dvdtitle_values);
    val.push_back(dvdtitle_p);
  }
}

Movie::MovieOpts::~MovieOpts() 
{
  delete reload_p;
  delete audio_mode_p;

  delete sub_lang_p;

  if (conf->p_movie_player() == MPLAYER)
    delete vcdtitle_p;

  if (conf->p_dvd_player() == MPLAYER) {
    delete dvdtitle_p;
    delete dvd_audio_lang_p;
  }
}

void Movie::MovieOpts::read_dirs() 
{ 
  Movie &mov = Movie::get_instance(); 
  mov.read_dirs(); 
  mov.reset();
}

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

Movie::Movie() 
  : exit_loop(false)
{
#ifdef use_graphical_movie
  if (conf->p_v_res() == 720)
    y_start = 85;
  else if (conf->p_v_res() == 800)
    y_start = 90;
  else if (conf->p_v_res() == 1024)
    y_start = 110;
  else if (conf->p_v_res() == 1280)
    y_start = 110;
  else
    y_start = 70; // default value

  if (conf->p_h_res() == 405)
    y_start = 70;

  image_width = 115;
  image_height = 120;

  if (conf->p_v_res() == 720)
    image_height = 105;

  if (conf->p_h_res() == 405) {
    image_width = 65;
    image_height = 67;
  }

  image_width_all = image_width+25;
  image_height_all = image_height+55;
  image_height_all_eks_text = image_height + 6;

  images_per_row = (conf->p_v_res()-2*25)/image_width_all;

  // lets make some more room for text 
  image_width_all += ((conf->p_v_res()-2*25)%image_width_all)/images_per_row;

  // + 20 is for the last row, since we don't need to add extra space
  rows = (conf->p_h_res()-2*y_start+20)/image_height_all;
  rows_search = (conf->p_h_res()-2*y_start+20-40)/image_height_all;

  image_height_all = (conf->p_h_res()-2*y_start+20)/rows;
  image_height_all_search = (conf->p_h_res()-2*y_start+20-40)/rows_search;

  imdb_dir = "/var/lib/mms/movies/";
  if (conf->p_homedir() != "/etc/mms/")
    imdb_dir = conf->p_homedir() + "movies/";

  if (!file_exists(imdb_dir)) {
    if (mkdir(imdb_dir.c_str(), 0777) == -1)
      DebugPrint perror(gettext("Could not create directory ") + imdb_dir,
			Print::DEBUGGING, DebugPrint::CRITICAL, "MOVIE");
  }
#endif

  load_options();

  reset();
}

void Movie::load_options()
{
  Config *conf = Config::get_instance();

  imdb_warning = true;

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

  std::ifstream in;
  in.open(path.c_str(), std::ios::in);
  
  if (!in.is_open()) {
    DebugPrint perror(gettext("Could not open options file ") + path,
		      Print::DEBUGGING, DebugPrint::CRITICAL, "THEME");
    ::exit(1);
  } else {
   
    string name, value, str;
    
    while (getline(in, str)) {

      string::size_type comma = str.find(",");

      if (comma == string::npos)
	continue;
      
      name = str.substr(0,comma);
      value = str.substr(comma+1);

      if (name == "imdb_warning") {
	imdb_warning = conv::stob(value);
	break;
      }
    }
    in.close();
  }
}

void Movie::reset()
{
  for (int i = 0, size = folders.size(); i < size; ++i)
    folders.pop();

  list<string> dirs = conf->p_movie_dirs();

  folders.push(std::make_pair(dirs, 0));
}

bool Movie::file_sort::operator()(const Entry& lhs, const Entry& rhs)
{
  return sort_order(lhs.lowercase_name, rhs.lowercase_name, !lhs.path.empty(), !rhs.path.empty());
}

// commands
void Movie::action_play()
{
  Entry e = vector_lookup(files, folders.top().second);
  int nfiles = e.filenames.size();
  if (nfiles > 0)
    playmovie(e);
  else
    DialogWaitPrint pdialog(gettext("Folder is empty"), 1000);
}

#ifdef use_graphical_movie
void Movie::go_back()
{
  if (!(folders.size() == 1)) {
    folders.pop();
    load_current_dirs();
  }
}

void Movie::enter_dir()
{
  if (vector_lookup(files, folders.top().second).type != "file")
    {
      string temp_cur_dir = vector_lookup(files ,folders.top().second).path;
      vector<CIMDBMovie> templist = rdir(temp_cur_dir);

      if (templist.size() != 0) {
	list<string> templs; templs.push_back(temp_cur_dir + "/");
	folders.push(std::make_pair(templs, 0));
	files = templist;
      } else {
	DialogWaitPrint pdialog(gettext("Folder is empty"), 1000);
      }
    }
}

void Movie::reget_movie_information()
{
  CIMDBMovie m = vector_lookup(files, folders.top().second);
  imdb_search_mainloop((m.m_strTitle != "") ? m.m_strTitle : m.name, m);
  files[folders.top().second] = m;
}

void Movie::print_info()
{
  if (vector_lookup(files, folders.top().second).m_strTitle == "") {
    DialogWaitPrint pdialog(2000);
    pdialog.add_line(gettext("No IMDb information found about movie"));
    pdialog.add_line(gettext("Please update information"));
    pdialog.print();
  } else
    print_information();
}

#else

void Movie::go_back()
{
  if (!(folders.size() == 1)) {
    folders.pop();
    load_current_dirs();
  }
}

void Movie::enter_dir()
{
  if (vector_lookup(files, folders.top().second).type != "file")
    {
      string temp_cur_dir = vector_lookup(files ,folders.top().second).path;
      vector<Entry> templist = rdir(temp_cur_dir);

      if (templist.size() != 0) {
	list<string> templs; templs.push_back(temp_cur_dir + "/");
	folders.push(std::make_pair(templs, 0));
	files = templist;
      }
    }
}
#endif

void Movie::load_current_dirs()
{
  files = parse_dir(folders.top().first);

  if (folders.top().first.size() > 1 && folders.size() == 1)
    std::sort(files.begin(), files.end(), file_sort());
}

void Movie::options()
{
  BusyIndicator::get_instance().idle();

  if (opts.mainloop()) {
    // reload
    folders.top().second = 0;
  }
}

void Movie::search_func()
{
  BusyIndicator::get_instance().idle();

#ifdef use_graphical_movie
  TSpecificFunctor<Movie, CIMDBMovie> print_func(this, &Movie::graphical_print);
#else
  TSpecificFunctor<Movie, Entry> print_func(this, &Movie::print);
#endif
  CompareFunctor<Movie, Entry> compare_func(this, &Movie::search_compare);
  RepFunctor<Movie, Entry> rep_func(this, &Movie::get_name_from_file);

  search(files, &folders.top().second, &print_func, &compare_func, &rep_func);
}

void Movie::exit()
{
  exit_loop = true;
}

string Movie::mainloop()
{
  Startmenu &startmenu = Startmenu::get_instance();
  startmenu.module = "movie";

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

  bool update_needed = true;

  while (!exit_loop)
    {
      if (update_needed)
#ifdef use_graphical_movie
	graphical_print(files);
#else
        print(files);
#endif

      busy_indicator.idle();

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

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

#ifdef use_graphical_movie
      if (input.command == "prev")
	{
	  if (files.size() > images_per_row) {
	    for (int i = 0; i < images_per_row; ++i) {
	      if (folders.top().second == 0) {
		folders.top().second = files.size()-1;
		i += images_per_row-files.size()%images_per_row;
	      }
	      else 
		folders.top().second -= 1;
	    }
	  }
 	}
      else if (input.command == "next")
 	{
	  if (files.size() > images_per_row) {
	    for (int i = 0; i < images_per_row; ++i) {
	      if (folders.top().second == files.size()-1 && i == 0) {
		folders.top().second = 0;
		break;
	      } else if (folders.top().second == files.size()-1) {
		break;
	      } else {
		folders.top().second += 1;
	      }
	    }
	  }
 	}
      else if (input.command == "left")
	{
	  if (folders.top().second != 0)
	    folders.top().second -= 1;
	  else
	    folders.top().second = files.size() - 1;
	}
      else if (input.command == "right")
 	{
	  folders.top().second = (folders.top().second+1)%files.size();
 	}
      else if (input.command == "back" && folders.size() > 1)
	{
	  go_back();
	}
      else if (input.command == "back" && folders.size() == 1)
	{
	  exit();
	}
      else if (input.command == "play_movie")
	{
	  action_play();
	}
      else if (input.command == "reget")
	{
	  reget_movie_information();
	}
      else if (input.command == "info" && input.mode == "graphical") 
	{
	  print_info();
	}
      else if (input.command == "action")
	{
	  if (vector_lookup(files, folders.top().second).type != "file")
	    enter_dir();
	  else
	    action_play();
	}
#else
      if (input.command == "prev")
 	{
	  if (folders.top().second != 0)
	    folders.top().second -= 1;
	  else
	    folders.top().second = files.size() - 1;
 	}
      else if (input.command == "next")
 	{
	  folders.top().second = (folders.top().second+1)%files.size();
 	}
      else if (input.command == "left")
	{
	  go_back();
	}
      else if (input.command == "right")
	{
	  enter_dir();
	}
      else if (input.command == "back" && input.mode == "general")
	{
	  exit();
	}
      else if (input.command == "action")
	{
	  action_play();
	}
#endif
      else if (input.command == "second_action")
	{
	  ExtraMenu em;

#ifdef use_graphical_movie
	  TimeFunctorv<Movie> t_e = TimeFunctorv<Movie>(this, &Movie::enter_dir);
	  TimeFunctorv<Movie> t_ap = TimeFunctorv<Movie>(this, &Movie::action_play);

	  if (vector_lookup(files, folders.top().second).type != "file") {

	    em.add_item(ExtraMenuItem(gettext("Enter directory"), input_master->find_shortcut("action"), &t_e));

	    em.add_item(ExtraMenuItem(gettext("Play all files in dir"), input_master->find_shortcut("play_movie"), &t_ap));

	  } else {
	    em.add_item(ExtraMenuItem(gettext("Play movie"), input_master->find_shortcut("action"), &t_ap));
	  }
	  
	  TimeFunctorv<Movie> t_rmi = TimeFunctorv<Movie>(this, &Movie::reget_movie_information);
	  em.add_item(ExtraMenuItem(gettext("Update IMDb information"), input_master->find_shortcut("reget"), &t_rmi));

	  TimeFunctorv<Movie> t_pi = TimeFunctorv<Movie>(this, &Movie::print_info);
	  em.add_item(ExtraMenuItem(gettext("Print information"), input_master->find_shortcut("info"), &t_pi));
	  
	  TimeFunctorv<Movie> t_gb = TimeFunctorv<Movie>(this, &Movie::go_back);
	  TimeFunctorv<Movie> t_back = TimeFunctorv<Movie>(this, &Movie::exit);

#else
	  TimeFunctorv<Movie> t_ap = TimeFunctorv<Movie>(this, &Movie::action_play);
	  em.add_item(ExtraMenuItem(gettext("Play movie"), input_master->find_shortcut("action"), 
				    &t_ap));

	  TimeFunctorv<Movie> t_e = TimeFunctorv<Movie>(this, &Movie::enter_dir);
	  if (vector_lookup(files, folders.top().second).type != "file")
	    em.add_item(ExtraMenuItem(gettext("Enter directory"), input_master->find_shortcut("right"), &t_e));
	  
	  TimeFunctorv<Movie> t_gb = TimeFunctorv<Movie>(this, &Movie::go_back);
	  if (folders.size() > 1)
	    em.add_item(ExtraMenuItem(gettext("Go up one directory"), input_master->find_shortcut("left"), &t_gb));
#endif

	  TimeFunctorv<Movie> t_sf = TimeFunctorv<Movie>(this, &Movie::search_func);
	  em.add_item(ExtraMenuItem(gettext("Search"), input_master->find_shortcut("search"), 
				    &t_sf));
	  TimeFunctorv<Movie> t_o = TimeFunctorv<Movie>(this, &Movie::options);
	  em.add_item(ExtraMenuItem(gettext("Options"), input_master->find_shortcut("options"), 
				    &t_o));

#ifdef use_graphical_movie
	  if (folders.size() > 1)
	    em.add_item(ExtraMenuItem(gettext("Go up one directory"), input_master->find_shortcut("back"), &t_gb));
	  else {
	    em.add_item(ExtraMenuItem(gettext("Return to startmenu"), 
				      input_master->find_shortcut("back"), 
				      &t_back));
	  }
#else
	  TimeFunctorv<Movie> t_back = TimeFunctorv<Movie>(this, &Movie::exit);
	  em.add_item(ExtraMenuItem(gettext("Return to startmenu"), 
				    input_master->find_shortcut("back"), 
				    &t_back));
#endif

#ifdef c_music
	  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));
#endif
	  em.mainloop();
	} 
      else if (input.command == "page_up")
 	{
	  int jump = conf->p_jump();

#ifdef use_graphical_movie
	  if (search_mode)
	    jump = images_per_row*rows_search;
	  else
	    jump = images_per_row*rows;
#endif

	  if (files.size() > jump) {
	    int diff = folders.top().second-jump;
	    if (folders.top().second == 0)
	      folders.top().second = (files.size()-1)+diff;
	    else if (diff < 0)
	      folders.top().second = 0;
	    else
	      folders.top().second = diff;
	  }
 	}
      else if (input.command == "page_down")
 	{
	  int jump = conf->p_jump();

#ifdef use_graphical_movie
	  if (search_mode)
	    jump = images_per_row*rows_search;
	  else
	    jump = images_per_row*rows;
#endif

	  if (files.size() > jump) {
	    if (folders.top().second > (files.size() - jump) && folders.top().second != (files.size()-1))
	      folders.top().second = files.size()-1;
	    else
	      folders.top().second = (folders.top().second+jump)%files.size();
	  }
 	}
      else if (input.command == "options")
	{
	  options();
	}
      else if (input.command == "search")
	{
	  search_func();
        }
      else
	update_needed = !general_loop_check(input);
    }

  exit_loop = false;

  return "";
}

string Movie::get_name_from_file(const Entry& e)
{
  return e.name;
}

bool Movie::search_compare(const Entry& s)
{
  return (s.lowercase_name.substr(0, search_str.size()) == lowercase_search_str);
}

// print a video element
void Movie::print_movie_element(const Entry& r, const Entry& position, int y)
{
  string name = r.name;

  if (r.type != "file")
    name += "/";

  string_format::format_to_size(name, "Vera/16", conf->p_v_res()-75-155, true);
  
  if (r == position) {
    render->current.add(new PObj(themes->movie_marked_left, 70, y-1, 2, NOSCALING));
    render->current.add(new PObj(themes->movie_marked, 70+25, y-1, conf->p_v_res()-(2*70)-50, 2));
    render->current.add(new PObj(themes->movie_marked_right, 70+25+conf->p_v_res()-(2*70)-50, y-1, 2, NOSCALING));
  }
  
  std::ostringstream out; out << r.filenames.size();

  render->current.add(new TObj(name, "Vera/16", 75, y, 
			       themes->movie_font1, themes->movie_font2, 
			       themes->movie_font3, 3));
  render->current.add(new TObj(r.filetype, "Vera/16", conf->p_v_res()-145, y, 
			       themes->movie_font1, themes->movie_font2, 
			       themes->movie_font3, 3));
  render->current.add(new TObj(out.str(), "Vera/16", conf->p_v_res()-85, y, 
			       themes->movie_font1, themes->movie_font2, 
			       themes->movie_font3, 3));
}

#ifdef use_graphical_movie
void Movie::print_information()
{
  BusyIndicator::get_instance().idle();

  if (vector_lookup(files, folders.top().second).type == "multi-dir")
    return;

  render->prepare_new_image();

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

  CIMDBMovie m = vector_lookup(files, folders.top().second);

  // determine if the title already contains year, this is often the case with
  // movies produced several times
  bool show_year = true;
  string::size_type first_paran = m.m_strTitle.find('(');
  if (first_paran != string::npos) {
    string::size_type second_paran = m.m_strTitle.find(')');
    if (second_paran != string::npos)
      if (second_paran - first_paran == 5)
	show_year = false;
  }

  string title = m.m_strTitle;
  if (show_year)
    title += "(" + conv::itos(m.m_iYear) + ")";
	
  string_format::format_to_size(title, "Vera/25", conf->p_v_res()-2*45, true);
  render->current.add(new TObj(title, "Vera/25", 45, 15, 
			       themes->movie_header_font1, themes->movie_header_font2, 
			       themes->movie_header_font3, 3));

  int y = 80;
  int x = 45;

  PObj *p;

  int w = 100, h = 140;

  if (conf->p_h_res() == 405) {
    y = 70;
    w = 70;
    h = 100;
  }

  if (!file_exists(render->image_get_fullpath(m.cover_path(), w, h))) {
    render->create_scaled_image_wrapper(m.cover_path(), w, h);
    // to insert the image into the transmap so that it won't fail when
    // we draw the screen again
    bool tmp;
    render->image_dimensions(render->image_get_fullpath(m.cover_path(), w, h), &tmp);
  }

  if (file_exists(m.cover_path()))
    p = new PFObj(m.cover_path(), x, y, w, h, false, 3);
  else {
    if (conf->p_h_res() == 405)
      p = new PObj(themes->movie_mcover_small, x, y, 3, NOSCALING);
    else
      p = new PObj(themes->movie_mcover, x, y, 3, NOSCALING);
  }

  render->current.add(p);
  
  render->current.add(new TObj(gettext("Director: ") + m.m_strDirector, "Vera/17", x + p->w + 10, y, 
			       themes->movie_font1, themes->movie_font2, themes->movie_font3, 3));
  y+=25;

  render->current.add(new TObj(gettext("Writing Credits: ") + m.m_strWritingCredits, "Vera/17", x + p->w + 10, y, 
			       themes->movie_font1, themes->movie_font2, themes->movie_font3, 3));
  y+=50;

  string runtime = gettext("Runtime: ") + string_format::trim(m.m_strRuntime);
  string_format::format_to_size(runtime, "Vera/17", conf->p_v_res() - (x + p->w + 10 + 45), false);
  render->current.add(new TObj(runtime, 
			       "Vera/17", x + p->w + 10, y, 
			       themes->movie_font1, themes->movie_font2, themes->movie_font3, 3));

  y+=(p->h+15-50-25);

  string genre = gettext("Genre: ") + m.m_strGenre;
  string_format::format_to_size(genre, "Vera/17", conf->p_v_res()-2*45, true);
  render->current.add(new TObj(genre, "Vera/17", x, y, 
			       themes->movie_font1, themes->movie_font2, 
			       themes->movie_font3, 3));
  y+=2*25;

  if (conf->p_h_res() == 405)
    y -= 7;

  int max_lines = 8;

  if (conf->p_v_res() == 800)
    max_lines = 6;
  else if (conf->p_v_res() == 720) {
    if (conf->p_h_res() == 540)
      max_lines = 4;
    else // widescreen
      max_lines = 2;
  }

  int pos = 0, lines = 0;

  string tagline = string_format::trim(m.m_strTagLine);

  if (!tagline.empty()) {
    tagline = gettext("Tagline: ") + tagline;

    while (pos != tagline.size()) {

      if (lines == max_lines)
	break;

      string t = string_format::get_line(tagline, pos, conf->p_v_res()-100, "Vera/17");

      if (lines == max_lines - 1 && pos != tagline.size()) {
	t = t.substr(0, t.size()-3);
	t += "...";
      }
      
      render->current.add(new TObj(t, "Vera/17", x, y + lines*25, 
				   themes->movie_font1, themes->movie_font2, 
				   themes->movie_font3, 3));
      ++lines;
    }
    y+=(lines+1)*25;
  }

  if (conf->p_h_res() == 405)
    y -= 7;

  string plot = gettext("Plot: ") + string_format::trim(m.m_strPlotOutline);

  int tag_lines = lines;
  pos = 0, lines = 0;

  while (pos != plot.size()) {

    if (tag_lines + lines == max_lines)
      break;

    string t = string_format::get_line(plot, pos, conf->p_v_res()-100, "Vera/17");

    if (tag_lines + lines == max_lines - 1 && pos != plot.size()) {
      t = t.substr(0, t.size()-3);
      t += "...";
    }

    render->current.add(new TObj(t, "Vera/17", x, y + lines*25, 
				 themes->movie_font1, themes->movie_font2, 
				 themes->movie_font3, 3));
    ++lines;
  }

  y+=(lines+1)*25;

  if (conf->p_h_res() == 405)
    y -= 7;

  render->current.add(new TObj(gettext("Rating:"), 
			       "Vera/17", x, y, themes->movie_font1, themes->movie_font2, 
			       themes->movie_font3, 3));

  int x_size, y_size;
  string_format::calculate_string_size(gettext("Rating:"), "Vera/17", &x_size, &y_size);

  int nr_stars = int(m.m_fRating);
  bool half_star = true;
  if (m.m_fRating - nr_stars == 0)
    half_star = false;

  for (int t = 0; t < nr_stars; ++t)
    render->current.add(new PObj(themes->movie_rating_icon, x+x_size+t*30, y, 3, NOSCALING));

  float extra_space = 0.5;
  if (half_star) {
    render->current.add(new PObj(themes->movie_rating_icon_half, x+x_size+nr_stars*30, y, 3, NOSCALING));
    extra_space += 0.7;
  }

  render->current.add(new TObj(conv::ftos(m.m_fRating) + " (" + m.m_strVotes + " votes)", 
			       "Vera/17", int(x+x_size+(nr_stars+extra_space)*30), y, 
			       themes->movie_font1, themes->movie_font2, themes->movie_font3, 3));

  

  y+=25;

  render->draw("Movie information 1");

  render->image_mut.leaveMutex();

  input_master->device->get_input();

  render->prepare_new_image();

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

  render->current.add(new TObj(title, "Vera/25", 45, 15, 
			       themes->movie_header_font1, themes->movie_header_font2, 
			       themes->movie_header_font3, 3));

  y = 90;
  x = 45;

  if (conf->p_h_res() == 405)
    y = 70;

  vector<string> cast;
  string::iterator last_iter = m.m_strCast.begin();
  bool cast_string_found = false;

  for (string::iterator i = m.m_strCast.begin(), end = m.m_strCast.end(); i != end;)
    if (*i == '\n' || ++i == end) {
      if (*i == '\n') ++i;
      if (cast_string_found) {
	cast.push_back(string(last_iter, --i));
	last_iter = ++i;
      } else {
	cast_string_found = true;
	last_iter = i;
      }
    }


  render->current.add(new TObj(gettext("Cast: "), "Vera/17", x, y, 
			       themes->movie_font1, themes->movie_font2, 
			       themes->movie_font3, 3));
  y += 25;

  int actors = 0;
  int max_actors = 20;
  if (conf->p_v_res() == 800)
    max_actors = 16;
  else if (conf->p_v_res() == 720) {
    if (conf->p_v_res() == 540)
      max_actors = 14;
    else
      max_actors = 9;
  }

  for (vector<string>::iterator i = cast.begin(), end = cast.end(); i != end && actors < max_actors; ++i, ++actors) {
    string cast_member = *i;
    string_format::format_to_size(cast_member, "Vera/17", conf->p_v_res()-(2*45+30), true);
    render->current.add(new TObj(cast_member, "Vera/17", x+30, y, 
				 themes->movie_font1, themes->movie_font2, 
				 themes->movie_font3, 3));
    y += 25;
  }

  render->draw("Movie information 2");

  render->image_mut.leaveMutex();

  input_master->device->get_input();
}

bool Movie::imdb_search_mainloop(const string& search_word, CIMDBMovie &m)
{
  Updater &updater = Updater::get_instance();
  updater.timer.deactivate("audio_fullscreen");
  
  input_master->cur_map = &input_master->movie_imdb;

  Input input;

  Overlay overlay = Overlay("dialog");

  DialogStepPrint pdialog(gettext("Looking up movie in IMDb database"), overlay);
  
  IMDB_MOVIELIST results;
  imdb.FindMovie(search_word, results);
  int pos = -1;
  if (results.size() > 0)
    pos = 0;

  pdialog.print_step2();

  Overlay o = Overlay("imdb search");

  bool found_match = false;

  string search_string = search_word;

  BusyIndicator &busy_indicator = BusyIndicator::get_instance();

  busy_indicator.idle();

  while (!(input.command == "back" && input.mode == "general"))
    {
      if (pos == -1) {
	MovieIMDbFunctor<Movie> print_func(this, &Movie::imdb_search_screen, results, pos, o);

	string gen = search_string;

	GenerateReturn r = generate_string(gen, &print_func);

	if (r == OK) {
	  search_string = gen;
	} else if (r == NEXT && results.size() > 0) {
	  pos = 0;
	  continue;
	} else {
	  input.command = "back";
	  input.mode = "general";
	  continue;
	}

	Overlay overlay_lookup = Overlay("dialog");
	DialogStepPrint pdialog2(gettext("Looking up movie in IMDb database"), 
				 overlay_lookup);

	busy_indicator.busy();

	imdb.FindMovie(search_string, results);
	if (results.size() > 0)
	  pos = 0;

	busy_indicator.idle();

	pdialog2.print_step2();

	continue;

      } else
	input = imdb_search_screen(search_string, results, pos, o);

      if (input.command == "prev")
	{
	  if (pos != -1)
	    --pos;
 	}
      else if (input.command == "next")
 	{
	  if (pos != results.size()-1)
	    ++pos;
 	}
      else if (input.command == "action")
	{
	  busy_indicator.busy();

	  CIMDBUrl url = vector_lookup(results, pos);

	  Overlay overlay_details = Overlay("dialog");
	  DialogStepPrint pdialog2(gettext("Getting details about movie"), 
				   overlay_details);

	  imdb.GetDetails(url, m);

	  // fixme: gif?
	  string cover = imdb_dir + m.name + "_cover.jpg";
	  // delete any previous cover, the new movie might not have a cover
	  if (file_exists(cover))
	    run::my_system_no_kill(("rm \"" + cover + "\"").c_str());

	  if (imdb.Download(m.m_strPictureURL, cover)) {
	    render->image_mut.enterMutex();
	    render->device->wait_for_not_drawing();
	    render->create_scaled_image_wrapper(m.cover_path(), image_width, image_height);
	    // to insert the image into the transmap so that it won't fail when
	    // we draw the screen again
	    bool tmp;
	    render->image_dimensions(render->image_get_fullpath(m.cover_path(), image_width, image_height), &tmp);
	    render->image_mut.leaveMutex();
	  }

	  pdialog2.print_step2();

	  m.Save(imdb_dir + m.name);

	  found_match = true;
	  break;
	}
      else
	general_loop_check(input, true);
    }

  render->image_mut.enterMutex();
  render->device->wait_for_output_done();
  o.cleanup();
  render->image_mut.leaveMutex();

  // if we didn't find anything we save anyway to save an empty imdb movie to
  // signal that we have searched this for this file and we don't want to do it
  // again unless the user tells us to.
  if (!found_match)
    m.Save(imdb_dir + m.name);

  input_master->cur_map = &input_master->movies;
  
  updater.timer.activate("audio_fullscreen");

  return found_match;
}

Input Movie::imdb_search_screen(string search_word, IMDB_MOVIELIST results, int pos, Overlay& o)
{
  // pos ranges from [-1, results.size()-1], -1 denotes the enter search word box

  render->image_mut.enterMutex();
  render->device->wait_for_output_done();

  if (o.elements.size() > 0)
    o.partial_cleanup();

  int x, y, max_x = conf->p_v_res()-250, max_y = 30;

  int max = std::min(10, int(results.size()));
  if (max == 0)
    max = 1; // for the no results found mesage

  int box_height = max_y*max+30+35+30+30;

  x = (conf->p_v_res()-(max_x+30))/2;
  y = (conf->p_h_res()-(box_height))/2;

  o.add(new RObj(x-15+2, y-15+2, max_x+30+1, box_height+1, 0, 0, 0, 200, 0));
  o.add(new RObj(x-15, y-15, max_x+30, box_height,  
		 themes->dialog_background1, themes->dialog_background2, 
		 themes->dialog_background3, themes->dialog_background4, 1));

  string header = gettext("IMDb lookup");

  int x_size, y_size;
  string_format::calculate_string_size(header, "Vera/20", &x_size, &y_size);

  o.add(new TObj(header, "Vera/20", (conf->p_v_res()-x_size)/2, y-5, 
		 themes->dialog_font1, themes->dialog_font2, themes->dialog_font3, 3));
  y += 35;

  if (pos == -1) 
    o.add(new RObj(x-5, y-3, max_x+10, 25+6, 0, 0, 0, 255, 2));

  string s = gettext("Search: ");
  string_format::calculate_string_size(s, "Vera/17", &x_size, &y_size);
  string_format::format_to_size(search_word, "Vera/17", max_x-x_size, true);

  o.add(new TObj(s + search_word, "Vera/17", x, y, themes->dialog_font1, 
		 themes->dialog_font2, themes->dialog_font3, 3));
  y += 40;

  if (results.size() == 0)
    o.add(new TObj(gettext("No results found..."), "Vera/17", x, y, 
		   themes->dialog_font1, themes->dialog_font2, themes->dialog_font3, 3));
  else {

    int nr_elements = 10;
    int i, start;
    i = std::max(0, pos-nr_elements+1);
    start = i;

    for (IMDB_MOVIELIST::const_iterator iter = results.begin()+i;
	 iter != results.end() && i < (start + nr_elements); ++iter, ++i) {
      if (i == pos)
	o.add(new RObj(x-5, y-3, max_x+10, 25+6, themes->dialog_marked1, 
		       themes->dialog_marked2, themes->dialog_marked3, 
		       themes->dialog_marked4, 2));

      string title = string_format::trim(iter->m_strTitle);
      string_format::format_to_size(title, "Vera/17", max_x, true);
      o.add(new TObj(title, "Vera/17", x, y, themes->dialog_font1, 
		     themes->dialog_font2, themes->dialog_font3, 3));
      y += 30;
    }

    string result_string = conv::itos(results.size());

    if (results.size() == 1)
      result_string += gettext(" result found");
    else
      result_string += gettext(" results found");

    int x_size2, y_size2;
    string_format::calculate_string_size(result_string, "Vera/17", &x_size2, &y_size2);

    o.add(new TObj(result_string, "Vera/17", x+max_x-x_size2, y+5, themes->dialog_font1, 
		   themes->dialog_font2, themes->dialog_font3, 3));
  }
  
  render->draw("imdb search screen", true);
  render->image_mut.leaveMutex();

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

  return input;
}

void Movie::graphical_print(const vector<CIMDBMovie>& cur_files)
{
  render->prepare_new_image();

  render->current.add(new PObj(themes->movie_background, 0, 0, 0, SCALE_FULL));
  
  render->current.add(new PObj(themes->startmenu_movie_dir, 25, 10, 2, NOSCALING));
  
  if (themes->show_header) {
    render->current.add(new TObj(gettext("Movies"), "Vera/28", 100, 10, 
				 themes->movie_header_font1, themes->movie_header_font2, 
				 themes->movie_header_font3, 2));
  }
  
  if (search_mode) {

    int y = 60;
    if (conf->p_v_res() == 720) {
      y -= 3;
    }

    render->current.add(new RObj(0, y, conf->p_v_res(), 45, 
				 themes->general_search_rectangle_color1, 
				 themes->general_search_rectangle_color2, 
				 themes->general_search_rectangle_color3, 
				 themes->general_search_rectangle_color4, 2));

    render->current.add(new PObj(themes->general_search, 47, y, 3, NOSCALING));

    render->current.add(new TObj(gettext("Search: ") + search_str, "Vera/22", 100, y+5, 
				 themes->movie_font1, themes->movie_font2, 
				 themes->movie_font3, 3));
  }

  // print the range
  int pos, start, pos_in_list, cur_rows, cur_image_height_all;

  if (search_mode) {
    pos_in_list = offset;
    cur_rows = rows_search;
    cur_image_height_all = image_height_all_search;
  } else {
    pos_in_list = folders.top().second;
    cur_rows = rows;
    cur_image_height_all = image_height_all;
  }

  int nr_in_last_row = cur_files.size()%images_per_row;
  if (nr_in_last_row == 0)
    nr_in_last_row = images_per_row;

  if (cur_files.size() > cur_rows*images_per_row) {
    if (pos_in_list < std::ceil(double(cur_rows)/2.0)*images_per_row) {
      pos = pos_in_list;
      start = 0;
    } else if (pos_in_list >= cur_files.size() - (images_per_row+nr_in_last_row)) { // last two rows
      start = cur_files.size()-((cur_rows-1)*images_per_row+nr_in_last_row);
      pos = pos_in_list-start;
    } else {
      pos = pos_in_list%images_per_row+images_per_row;
      if (pos == images_per_row-1)
	pos = 2*images_per_row-1;
      start = pos_in_list-pos;
    }
  } else {
    pos = pos_in_list;
    start = 0;
  }

  int x_start, x = (conf->p_v_res()-images_per_row*image_width_all)/2, y = y_start;
  x_start = x;
  
  if (search_mode)
    y = y_start + 40;

  for (int j = 0; j < cur_rows*images_per_row && j < cur_files.size(); ++j) {

    CIMDBMovie s = vector_lookup(cur_files, start);

    if (file_exists(s.cover_path())) {
      bool border = false;
	
      if (pos == j)
	border = true;

      if (!file_exists(render->image_get_fullpath(s.cover_path(), image_width, image_height))) {
	render->create_scaled_image_wrapper(s.cover_path(), image_width, image_height);
	// to insert the image into the transmap so that it won't fail when
	// we draw the screen again
	bool tmp;
	render->image_dimensions(render->image_get_fullpath(s.cover_path(), image_width, image_height), &tmp);
      }

      PFObj *p = new PFObj(s.cover_path(), x, y, image_width, image_height, border, 3);
	
      p->x = p->real_x = x + (image_width_all - (p->w + 10))/2;
      p->y = p->real_y = y + (image_height_all_eks_text - (p->h + 10))/2;

      if (p->border) {
	p->real_x -= 5;
	p->real_y -= 5;
      } else {
	p->real_x -= 1;
	p->real_y -= 1;
      }

      render->current.add(p);

    } else {
      if (s.type == "dir-multi" || s.type == "dir") {

	PObj *p;
      
	if (pos == j)
	  p = new PObj(themes->movie_dir_marked, x, y, 3, NOSCALING);
	else
	  p = new PObj(themes->movie_dir, x, y, 3, NOSCALING);

	p->x = p->real_x = x + (image_width_all - p->w)/2;
	p->y = p->real_y = y + (image_height_all_eks_text - p->h)/2;
      
	render->current.add(p);

      } else {

	PObj *p;
	if (conf->p_h_res() == 405)
	  p = new PObj(themes->movie_mcover_small, x, y, 3, NOSCALING);
	else
	  p = new PObj(themes->movie_mcover, x-5, y-5, 3, NOSCALING);
	p->x = p->real_x = x + (image_width_all - (p->w + 10))/2;
	p->y = p->real_y = y + (image_height_all_eks_text - (p->h + 10))/2;
	render->current.add(p);

      }
    }

    string name = s.name;
    if (!s.real_name.empty())
      name = s.real_name;
    else if (s.m_strTitle != "")
      name = s.m_strTitle;

    string old_name = name;
    
    string_format::format_to_size(name, "Vera/13", image_width_all-10, false);

    string name_second_part = old_name.substr(name.size());

    if (name_second_part.size() > 0 && name_second_part[0] != ' ') {
      // splitting words
      string::size_type last_whole_word = name.rfind(' ');
      if (last_whole_word != string::npos) {
	name = name.substr(0, last_whole_word);
	name_second_part = old_name.substr(name.size());
      }
    }

    int x_size, y_size;
    string_format::calculate_string_size(name, "Vera/13", &x_size, &y_size);


    string_format::format_to_size(name_second_part, "Vera/13", image_width_all-10, false);

    int x_size2;
    string_format::calculate_string_size(name_second_part, "Vera/13", &x_size2, &y_size);

    if (pos == j) {
      render->current.add(new TObj(name, "Vera/13", x + (image_width_all-x_size)/2, y+image_height_all_eks_text, 
				   themes->movie_marked_font1, themes->movie_marked_font2, 
				   themes->movie_marked_font3, 3));
      render->current.add(new TObj(name_second_part, "Vera/13", x + (image_width_all-x_size2)/2, y+image_height_all_eks_text+20, 
				   themes->movie_marked_font1, themes->movie_marked_font2, 
				   themes->movie_marked_font3, 3));
    } else {
      render->current.add(new TObj(name, "Vera/13", x + (image_width_all-x_size)/2, y+image_height_all_eks_text, 
				   themes->movie_font1, themes->movie_font2, 
				   themes->movie_font3, 3));
      render->current.add(new TObj(name_second_part, "Vera/13", x + (image_width_all-x_size2)/2, y+image_height_all_eks_text+20, 
				   themes->movie_font1, themes->movie_font2, 
				   themes->movie_font3, 3));
    }
    
    x += image_width_all;

    if (start == cur_files.size()-1) {
      if ((j+1)%images_per_row != 0)
	j += (images_per_row-(j+1)%images_per_row);
      
      x = x_start;
      y += cur_image_height_all;

      start = 0;
      continue;
      
    } else
      ++start;
    
    if ((j+1)%images_per_row == 0) {
      y += image_height_all;
      x = x_start;
    }
  }

  std::ostringstream position;

  if (search_mode)
    if (cur_files.size() > 0)
      position << offset + 1 << "/" << cur_files.size();
    else
      position << "";
  else
    position << folders.top().second+1 << "/" << cur_files.size();
  
  int x_size, y_size;
  string_format::calculate_string_size(position.str(), "Vera/18", &x_size, &y_size);
    
  render->current.add(new TObj(position.str(), "Vera/18", conf->p_v_res()-(60+x_size), 20, 
			       themes->movie_font1, themes->movie_font2, 
			       themes->movie_font3, 3));

  render->draw("movie-graphical");

  render->image_mut.leaveMutex();
}
#endif

void Movie::print(const vector<Entry>& cur_files)
{
  render->prepare_new_image();

  render->current.add(new PObj(themes->movie_background, 0, 0, 0, SCALE_FULL));
  
  render->current.add(new PObj(themes->startmenu_movie_dir, 25, 10, 2, NOSCALING));
  
  if (themes->show_header) {
    render->current.add(new TObj(gettext("Movies"), "Vera/28", 100, 10, 
				 themes->movie_header_font1, themes->movie_header_font2, 
				 themes->movie_header_font3, 2));
  }
  
  if (search_mode) {
    render->current.add(new RObj(0, 60, conf->p_v_res(), 45, 
				 themes->general_search_rectangle_color1, 
				 themes->general_search_rectangle_color2, 
				 themes->general_search_rectangle_color3, 
				 themes->general_search_rectangle_color4, 2));

    render->current.add(new PObj(themes->general_search, 47, 60, 3, NOSCALING));

    render->current.add(new TObj(gettext("Search: ") + search_str, "Vera/22", 100, 65, 
				 themes->movie_font1, themes->movie_font2, 
				 themes->movie_font3, 3));
  }

  if (cur_files.size() > 0) {
    TPRMovieFunctor<Movie> print_func(this, &Movie::print_movie_element);
    if (search_mode && cur_files.size() > 0)
      print_range(cur_files, vector_lookup(cur_files, offset), 
		  offset, &print_func);
    else
      print_range(cur_files, vector_lookup(cur_files, folders.top().second), 
		  folders.top().second, &print_func);
  }

  std::ostringstream pos;

  if (search_mode)
    if (cur_files.size() > 0)
      pos << offset + 1 << "/" << cur_files.size();
    else
      pos << "";
  else
    pos << folders.top().second+1 << "/" << cur_files.size();
  
  int x, y;
  string_format::calculate_string_size(pos.str(), "Vera/18", &x, &y);
    
  render->current.add(new TObj(pos.str(), "Vera/18", conf->p_v_res()-(60+x), 20, 
			       themes->movie_font1, themes->movie_font2, 
			       themes->movie_font3, 3));

  render->draw("movie");

  render->image_mut.leaveMutex();
}

std::pair<Movie::SUBTYPE, vector<string> > Movie::find_subtitles(const string& dir)
{
  SUBTYPE type = NOSUB;

  vector<string> subtitles;

  for (file_iterator<file_t, default_order> i (dir); i != i.end (); ++i) {
    string name = i->getName();
    if (name.substr(name.size()-3) == "srt") {
      if (type == NOSUB)
	type = SRT;
      subtitles.push_back(name);
    } else if (name.substr(name.size()-3) == "idx") {
      if (type == NOSUB)
	type = IDX;
      subtitles.push_back(name.substr(0, name.size()-4));
    }
  }

  return std::make_pair(type, subtitles);
}

void Movie::playmovie(const Entry file)
{
  DialogWaitPrint pdialog(gettext("Starting Playback..."), 1000);

  if (file.filetype == "dvd") {
    play_dvd_helper(file.filenames.front());
    return;
  } else {

#if 0 // FIXME: this doesn't work, it's not escaped and only handles one movie file
    vector<string> empty_path;
    std::pair<SUBTYPE, vector<string> > subtitles;
    subtitles = std::make_pair(NOSUB, empty_path);

    if (file.path.empty()) {
      vector<string> subpath;
      SUBTYPE type = NOSUB;
      for (list<string>::const_iterator i = file.filenames.begin(), iend = file.filenames.end();
	   i != iend; ++i) {

	string pos_subpath = i->substr(0, i->size()-3) + "srt";
	if (file_exists(pos_subpath)) {
	  type = SRT;
	  subpath.push_back(pos_subpath);
	}

	string::size_type pos = i->rfind('/');
	if (pos != string::npos) {
	  string pos_subpath = i->substr(0, pos+1) + "subs/" + i->substr(pos+1, (i->size()-3)-(pos+1)) + "srt";
	  if (file_exists(pos_subpath)) {
	    type = SRT;
	    subpath.push_back(pos_subpath);
	  }
	  pos_subpath = i->substr(0, pos+1) + "Subs/" + i->substr(pos+1, (i->size()-3)-(pos+1)) + "srt";
	  if (file_exists(pos_subpath)) {
	    type = SRT;
	    subpath.push_back(pos_subpath);
	  }
	}

	pos_subpath = i->substr(0, i->size()-3) + "idx";
	if (file_exists(pos_subpath)) {
	  type = IDX;
	  subpath.push_back(pos_subpath);
	}

	if (pos != string::npos) {
	  string pos_subpath = i->substr(0, pos+1) + "subs/" + i->substr(pos+1, (i->size()-3)-(pos+1)) + "idx";
	  if (file_exists(pos_subpath)) {
	    type = IDX;
	    subpath.push_back(pos_subpath.substr(0, pos_subpath.size()-4));
	  }
	  pos_subpath = i->substr(0, pos+1) + "Subs/" + i->substr(pos+1, (i->size()-3)-(pos+1)) + "idx";
	  if (file_exists(pos_subpath)) {
	    type = IDX;
	    subpath.push_back(pos_subpath.substr(0, pos_subpath.size()-4));
	  }
	}
      }
      subtitles = std::make_pair(type, subpath);
    } else
      subtitles = find_subtitles(file.path);

    string subcommand = "";

    if (conf->p_movie_player() == MPLAYER) {
      if (subtitles.first == IDX)
	subcommand += " -vobsub";
      else if (subtitles.first == SRT) {
	// mplayer must find the subtitles itself
	subtitles.first = NOSUB;
	subtitles.second.clear();
      }
    } 

    if (subtitles.second.size() != 0) {
      // vobsubs can only supplied in ones
      subcommand += " " + subtitles.second.front();
    }
#endif

    string subcommand = "";
    vector<string> empty_path;
    std::pair<SUBTYPE, vector<string> > subtitles = std::make_pair(NOSUB, empty_path);

    string cur_files = "";

    // support playlist files
    if (file.filetype == "pls")
      cur_files += " -playlist ";

    if (conf->p_movie_player() == XINE) {
      if (subtitles.second.size() != file.filenames.size())
	for (list<string>::const_iterator iter = (file.filenames).begin(); iter != (file.filenames).end(); ++iter)
	  cur_files += " \"" + (*iter) + "\"";
      else {
	int i = 0;
	for (list<string>::const_iterator iter = (file.filenames).begin(); iter != (file.filenames).end(); ++iter, ++i)
	  cur_files += " \"" + (*iter) + "#subtitle:" + vector_lookup(subtitles.second, i) + "\"";
      }
    } else if (conf->p_movie_player() == MPLAYER)
      for (list<string>::const_iterator iter = (file.filenames).begin(); iter != (file.filenames).end(); ++iter)
	cur_files += " \"" + (*iter) + "\"";

    if (conf->p_movie_player() == MPLAYER) {
      run::my_system((conf->p_mplayer_path() + ' ' + conf->p_mplayer_opts() +
		      (opts.sub_lang().empty() ? "" : " -slang " + opts.sub_lang()) +
		      ' ' + opts.audio_mode() +
		      ((conf->p_input() != LIRC) ? "" :
		       (conf->p_lircconf().empty() ? "" :
			" -lircconf " + conf->p_lircconf())) +
		      ' ' + subcommand + cur_files).c_str());
    } else if (conf->p_movie_player() == XINE) {
      run::my_system((conf->p_cxfe_path() + ' ' + conf->p_cxfe_opts() +
		      ((conf->p_input() != LIRC) ? " -i kybd" :
		       " -i lirc") +
		      ' ' + cur_files).c_str());
    } else {
      DebugPrint perror(gettext("No movie player specified in config file"),
			Print::DEBUGGING, DebugPrint::CRITICAL, "MOVIE");
      return;
    }
  }
}

#ifdef use_media
// play everything on a cd
void Movie::playcd(int count)
{
  if (count == 1) { //play disc

    list<MyPair> filetypes = conf->p_filetypes_m();

    string player_cmd;
    
    if (conf->p_movie_player() == MPLAYER) {
      player_cmd = conf->p_mplayer_path() + ' ' + conf->p_mplayer_opts() + 
	(opts.sub_lang().empty() ? "" : " -slang " + opts.sub_lang()) +
	' ' + opts.audio_mode() +
	((conf->p_input() != LIRC) ? "" : 
	 (conf->p_lircconf().empty() ? "" : " -lircconf " + conf->p_lircconf() + ' ')) + ' ';
    } else if (conf->p_movie_player() == XINE) {
      player_cmd = conf->p_cxfe_path() + ' ' + conf->p_cxfe_opts() +
	((conf->p_input() != LIRC) ? " -i kybd" :
	 " -i lirc") + ' ';
    } else 
      DebugPrint perror(gettext("No movie player specified in config file"),
			Print::DEBUGGING, DebugPrint::CRITICAL, "MOVIE");

    string file_types;
    
    for (list<MyPair>::iterator iter2 = filetypes.begin();
	 iter2 != filetypes.end(); ++iter2)
      file_types += "-iname '*." + iter2->first + "' -o ";

    file_types = file_types.substr(0, file_types.size()-4);

    string play_cmd = "find " + cd->get_mount_point() +
      " -follow -maxdepth 8 -type f -print0 " + 
      file_types + " | xargs -0 " + player_cmd;

    run::my_system(play_cmd.c_str());

  } else {

#ifdef use_graphical_movie
    vector<CIMDBMovie> backup_files;
#else
    vector<Entry> backup_files;
#endif

    backup_files = files;

    std::stack<std::pair<list<string>, int > > backup_folders = folders;

    list<string> dirs;
    dirs.push_back(cd->get_mount_point());

    files = parse_dir(dirs);

    while (!folders.empty())
      folders.pop();

    folders.push(std::make_pair(dirs, 0));

    mainloop();

    files = backup_files;
    folders = backup_folders;
  }

  //umount the disc
  run::my_system_no_kill(("umount " + cd->get_mount_point()).c_str());
}

// play a vcd
void Movie::playvcd()
{
  string device = cd->get_device();

  //play disc
  if (conf->p_movie_player() == MPLAYER) {
    run::my_system(
		   (conf->p_mplayer_path() + ' ' + conf->p_mplayer_dvd_opts() +
		    " vcd://" + opts.vcdtitle() + "/" + device +
		    ' ' + opts.audio_mode() +
		    ((conf->p_input() != LIRC) ? "" :
		     (conf->p_lircconf().empty() ? "" :
		      " -lircconf " + conf->p_lircconf())
		     )
		    ).c_str());
  } else if (conf->p_movie_player() == XINE) {
    run::my_system(
		   (conf->p_cxfe_path() + ' ' + conf->p_cxfe_opts() +
		    ((conf->p_input() != LIRC) ? " -i kybd" :
		     " -i lirc") + " -dev " + device + ' ' +
		    "vcd://").c_str());
  } else {
    DebugPrint perror(gettext("No movie player specified in config file"),
		      Print::DEBUGGING, DebugPrint::CRITICAL, "MOVIE");
    return;
  }
}

// play a dvd
void Movie::playdvd()
{
  play_dvd_helper(cd->get_device());
}

void Movie::determine_media()
{
  bool data_disc = cd->data_disc();
  cd->close();

  if (data_disc) {
    //mount the disc
    run::my_system_no_kill(("mount " + cd->get_mount_point()).c_str());
  
    int count;
    string mediatype = testdir(&count); 

    if (mediatype == "empty")
      Print pdialog(gettext("The disc does not contain any recognizable files"),
		    Print::SCREEN);
    else if (count < 2)
      DialogWaitPrint pdialog(gettext("Starting Playback..."), 1000);

    if (mediatype != "divx")
      //umount the disc
      run::my_system_no_kill(("umount " + cd->get_mount_point()).c_str()); 

    if (mediatype == "vcd")
      playvcd();
    else if (mediatype == "svcd")
      playvcd();
    else if (mediatype == "dvd")
      playdvd();
    else if (mediatype == "divx")
      playcd(count);
  } else
    Print pdialog(gettext("The disc does not contain any recognizable files"),
		  Print::SCREEN);
}
#endif

void Movie::play_dvd_helper(const string& device)
{
  if (conf->p_dvd_player() == DXR3PLAYER) {
    // dxr3player or dxr3player-sdl
    run::my_system((conf->p_dxr3player_path() + ' ' + 
		    conf->p_dxr3player_opts() + 
		    " -d '" + device + "'").c_str());
      
  } else if (conf->p_dvd_player() == XINE) {

    run::my_system((conf->p_cxfe_path() + ' ' + conf->p_cxfe_opts() +
		    ((conf->p_input() != LIRC) ? " -i kybd" :
		     " -i lirc")  + " dvd://'" + device + "'").c_str());

  } else if (conf->p_dvd_player() == MPLAYER) {

    run::my_system(
		   (conf->p_mplayer_path() + ' ' + conf->p_mplayer_dvd_opts() +
		    " dvd://" + opts.dvdtitle() + 
		    " -slang " + opts.sub_lang() +
		    " -dvd-device '" + device +  "'"
		    " -alang " + opts.dvd_audio_lang() + 
		    ' ' + opts.audio_mode() +
		    ((conf->p_input() != LIRC) ? "" :
		     (conf->p_lircconf().empty() ? "" :
		      " -lircconf " + conf->p_lircconf())
		     )).c_str());
  } else {
    DebugPrint perror(gettext("No dvd player specified in config file"),
		      Print::DEBUGGING, DebugPrint::CRITICAL, "MOVIE");
  }
}

// ====================================================================
//
// dir
//
// ====================================================================

// Add a file to the list
Entry Movie::addfile(const string& name, const MyPair& filetype)
{
  Entry r;
  r.id = ++id;
  r.filenames.push_back(name);
  string tempname = name.substr(0, name.size()-((filetype.first).size()+1));
  string::size_type pos = tempname.rfind("/");
  if (pos != string::npos)
    r.name = tempname.substr(pos+1);
  else
    r.name = tempname;
  if (conf->p_convert())
    r.name = string_format::convert(r.name);
  r.lowercase_name = string_format::lowercase(r.name);
  r.type = "file";
  r.filetype = filetype.second;

  return r;
}

// Add a moviedir as "one file" to the movielist
Entry Movie::add_dir(const string& filename)
{
  Entry output;

  string type = "";

  vector<string> known_filetypes;

  int nr_of_elements = 0;

  for (file_iterator<file_t, default_order> i (filename); i != i.end (); i.advance(false))
    {
      string name = i->getName();

      if (string_format::lowercase(name).rfind("video_ts") != string::npos) {
	type = "dvd";
	break;
      }
      
      string short_name = name.substr(name.size()-3, 2);

      if (string_format::lowercase(short_name) == "cd") {
	type = "cd";
	break;
      } else if (check_type(name, conf->p_filetypes_m()) != emptyMyPair) {
	known_filetypes.push_back(name);
	type = "movie";
      }

      ++nr_of_elements;
    }

  if (type == "dvd") {

    output.id = ++id;
    string::size_type pos = filename.rfind("/");
    output.name = filename.substr(pos+1);
    output.type = "dir-single"; 
    output.path = filename;
    output.filetype = "dvd";

    for (file_iterator<file_t, default_order> i (filename); 
	 i != i.end (); i.advance(false)) {
      if (string_format::lowercase(i->getName()).rfind("/video_ts") != string::npos) {
	output.filenames.push_back(i->getName());
	break;
      }
    }

  } else if (type == "cd") {

    string::size_type pos = filename.rfind("/");

    output.id = ++id;
    output.name = filename.substr(pos+1);
    output.type = "dir-single"; 
    output.path = filename;

    for (file_iterator<file_t, default_order> i (filename); i != i.end (); i.advance(false)) {
      file_t cur_file = *i;
      
      string name = cur_file.getName();
      string short_name = name.substr(name.size()-3, 2);

      if (string_format::lowercase(short_name) == "cd") {
	for (file_iterator<file_t, default_order> i2 (name); i2 != i2.end(); i2.advance(false)) {
	  cur_file = *i2;

	  const MyPair filetype = check_type(cur_file.getName(), conf->p_filetypes_m());
	  if (filetype != emptyMyPair) {
	    output.filetype = filetype.second;
	    output.filenames.push_back((*i2).getName());
	  }
	}
      }
    }

  } else if (!known_filetypes.empty()) {
    output.id = ++id; 
    string::size_type pos = filename.rfind("/");
    output.name = filename.substr(pos+1);
    output.path = filename;

    output.type = "dir-single";
    if (known_filetypes.size() > 1) {
      string first = known_filetypes.front();
      vector<string>::iterator i = known_filetypes.begin();
      ++i;
      for (vector<string>::iterator end = known_filetypes.end();
	   i != end; ++i) 
	if (string_format::difference(first, *i) > 1) {
	  output.type = "dir-multi";
	  break;
	}
    }

    if (conf->p_convert())
      output.name = string_format::convert(output.name);

    const MyPair filetype = check_type(known_filetypes.front(), conf->p_filetypes_m());
    output.filetype = filetype.second; 
    for (vector<string>::iterator i = known_filetypes.begin(), end = known_filetypes.end();
	 i != end; ++i)
      output.filenames.push_back(*i);

  } else {

    output.id = ++id;
    string::size_type pos = filename.rfind("/");
    output.name = filename.substr(pos+1);
    if (nr_of_elements > 1)
      output.type = "dir-multi"; 
    else
      output.type = "dir"; 
    output.path = filename;
    output.filetype = "dir";
  }

  output.lowercase_name = string_format::lowercase(output.name);

  return output;
}

#ifdef use_graphical_movie
// read a dir and spit out a list of files and dirs. For movies.
vector<CIMDBMovie> Movie::rdir(const string& argv)
{
  vector<CIMDBMovie> cur_files;

  bool db_load_succes = false;

  for (file_iterator<file_t, default_order> i (argv); i != i.end (); i.advance(false))
    {
      string original_name = filesystem::FExpand(i->getName());
      string filename = original_name;

      bool inside_movie_directory = false;
      list<string> filenames;

      // if the directory where the files reside is known to be a movie, use it
      // instead of trying to fetch information. But use the names of the files
      // to differentiate files like CD1.avi CD2.avi etc.

      string small_name = "";

      if (filename[filename.size()-1] == '/')
	filename = filename.substr(0, filename.size()-1);
      
      string::size_type pos_parent = filename.rfind("/");
      if (pos_parent != string::npos) {
	small_name = filename.substr(pos_parent+1);
	filename = filename.substr(0, pos_parent);
      }

#if 0
      string small_parent_name = "";

      if (filename[filename.size()-1] == '/')
	filename = filename.substr(0, filename.size()-1);
      
      pos_parent = filename.rfind("/");
      if (pos_parent != string::npos)
	small_parent_name = filename.substr(pos_parent+1);

      bool root_dir = false;

      list<string> root_dirs = conf->p_movie_dirs();
      for (list<string>::const_iterator i_root = root_dirs.begin(), 
	     end = root_dirs.end(); i_root != end; ++i_root)
	if ((*i_root) == filename) {
	  root_dir = true;
	  break;
	}

      bool inside_movie_directory = false;

      list<string> filenames;

      // lets see if the dir is recognized as a movie
      if (!root_dir && file_exists(imdb_dir + small_parent_name)) {

	inside_movie_directory = true;
	
	// skip not recognizable files
	if (!isDirectory (original_name)) {
	  MyPair filetype = check_type(i->getName(), conf->p_filetypes_m());
	  if (filetype == emptyMyPair)
	    continue;
	  else
	    filenames.push_back(original_name);
	} else {
	  bool good_dir = false;
	  for (file_iterator<file_t, default_order> i2 (original_name); i2 != i2.end (); ++i2) {
	    MyPair filetype = check_type(i2->getName(), conf->p_filetypes_m());
	    if (filetype != emptyMyPair) {
	      filenames.push_back(i2->getName());
	      good_dir = true;
	      break;
	    }
	  }
	  if (!good_dir)
	    continue;
	}
      } else
#endif
	filename = original_name;
      
      if (isDirectory (filename)) { // dir
	Entry dir = add_dir(filename);
	dir.path += "/";
	if (dir.filenames.size() != 0 || dir.filetype == "dir") {
	  CIMDBMovie m = CIMDBMovie(dir);
	  if (m.Load(imdb_dir + m.name))
	    db_load_succes = true;
	  if (inside_movie_directory) {
	    m.real_name = small_name;
	    if (conf->p_convert())
	      m.real_name = string_format::convert(m.real_name);
	    m.type = "file"; // we don't want to enter anyting inside a
	                     // movie directory
	    m.filenames = filenames;
	  }
	  cur_files.push_back(m);
	}
      } else { // file
	MyPair filetype = check_type(filename, conf->p_filetypes_m());
	if (filetype != emptyMyPair) {
	  Entry e = addfile(filename, filetype);
	  CIMDBMovie m = CIMDBMovie(e);
	  if (m.Load(imdb_dir + m.name))
	    db_load_succes = true;
	  cur_files.push_back(m);
	}
      }
    }

  // check if it's a root dir, we don't want to display the message for all
  // folders
  bool root_dir = false;

  list<string> root_dirs = conf->p_movie_dirs();
  for (list<string>::const_iterator i_root = root_dirs.begin(), 
	 end = root_dirs.end(); i_root != end; ++i_root)
    if ((*i_root) == argv) {
      root_dir = true;
      break;
    }

  if (!db_load_succes && root_dir && imdb_warning) {
    DialogWaitPrint pdialog(5000);
    pdialog.add_line(gettext("No IMDb information found about movies"));
    pdialog.add_line(gettext("Use the reget movie information action"));
    pdialog.add_line(gettext("to download information about a movie"));
    pdialog.print();
    imdb_warning = false;
  }

#if 0
  // this means that we tried to enter a dir and we just can't go any further,
  // happens if you are inside a movie dir and you try to enter another
  // directory.
  if (cur_files == files) {
    vector<CIMDBMovie> cur_files_empty;
    cur_files = cur_files_empty;
  }
#endif

  return cur_files;
}
#else
// read a dir and spit out a list of files and dirs. For movies.
vector<Entry> Movie::rdir(const string& argv)
{
  vector<Entry> cur_files;
  
  for (file_iterator<file_t, default_order> i (argv); i != i.end (); i.advance(false))
    {
      string filename = filesystem::FExpand(i->getName());

      if (isDirectory (filename)) { // dir
	Entry dir = add_dir(filename);
	if ((dir.filenames).size() != 0 || dir.filetype == "dir")
	  cur_files.push_back(dir);
      } else { // file
	MyPair filetype = check_type(filename, conf->p_filetypes_m());
	if (filetype != emptyMyPair)
	  cur_files.push_back(addfile(filename, filetype));
      }
    }
  return cur_files;
}
#endif

#ifdef use_graphical_movie
vector<CIMDBMovie> Movie::parse_dir(const list<string>& dirs)
{
  vector<CIMDBMovie> vec_files;

  for (list<string>::const_iterator iter = dirs.begin(); iter != dirs.end(); ++iter)
    {
      vector<CIMDBMovie> tempfiles = rdir(*iter);

      for (vector<CIMDBMovie>::const_iterator iter2 = tempfiles.begin(); iter2 != tempfiles.end(); ++iter2)
	vec_files.push_back(*iter2);
    }

  return vec_files;
}
#else
vector<Entry> Movie::parse_dir(const list<string>& dirs)
{
  vector<Entry> vec_files;

  for (list<string>::const_iterator iter = dirs.begin(); iter != dirs.end(); ++iter)
    {
      vector<Entry> tempfiles = rdir(*iter);

      for (vector<Entry>::const_iterator iter2 = tempfiles.begin(); iter2 != tempfiles.end(); ++iter2)
	vec_files.push_back(*iter2);
    }

  return vec_files;
}
#endif

void Movie::read_dirs() 
{
  if (conf->p_movie_mode()) {

    // process movie dir 
    files = parse_dir(conf->p_movie_dirs());

    if (conf->p_movie_dirs().size() > 1)
      std::sort(files.begin(), files.end(), file_sort());

  }
}

#ifdef use_media
// help function for determine_media
string Movie::testdir(int *count)
{
  *count = 0;

  for (file_iterator<file_t, default_order> i (cd->get_mount_point()); i != i.end (); ++i) 
    {
      string filename = filesystem::FExpand(i->getName());

      if (isDirectory (filename)) {

	string cur_dir = string_format::lowercase(filename.substr(filename.rfind("/")+1));
	if (cur_dir == "svcd")
	  return "svcd";

	if (cur_dir == "vcd")
	  return "vcd";

	if (cur_dir == "video_ts")
	  return "dvd";
      } else {
	MyPair filetype = check_type(filename, conf->p_filetypes_m());
	if (filetype != emptyMyPair)
	  ++(*count);
      }
    }

  if (*count == 0)
    return "empty";
  else
    // none of the above found, it must be a divx
    return "divx";
}
#endif
