#define _XOPEN_SOURCE /* glibc2 needs this */

#include "config.h"

#include "epg.hpp"

#include "input.hpp"
#include "common.hpp"
#include "graphics.hpp"
#include "updater.hpp"

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

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

#include <time.h>

#include "gettext.hpp"

#include <iostream>
#include <list>
#include <sstream>
#include <algorithm>

// time of last change
#include <sys/stat.h>

#include "tinyxml.h"

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

time_t Programme::str_to_time(const char *s) {
  struct tm tm;
  char * datetimetz;
  int hours=0;
  int minutes=0;
  int factor=1;
        
  //fix unset value       
  tm.tm_isdst=0;
  //get timezone
  tzset();  
        
  datetimetz = strptime(s, "%Y%m%d%H%M%S", &tm);
  //check if data reliable
  if (datetimetz==NULL || strlen(datetimetz)!=6)
    return-1;
  //move up space
  datetimetz++;
  //get factor    
  if (datetimetz[0]=='-') {
    factor=-1;
  }
  //move up sign
  datetimetz++;
  //copy XXXX to extract hours
  char tmptz[5];
  strcpy(tmptz,datetimetz);
  //we only need hours
  tmptz[2]=0;
  //get hours
  hours = atoi(tmptz);
  //get minutes
  minutes= atoi(&datetimetz[2]);
  //correction using local timezone and xml timezone to obtain the
  //right date time
  tm.tm_sec+=timezone + factor*((minutes * 60)+(hours * 3600));
  return mktime(&tm);
}

bool Programme::operator<(const Programme& rhs) const
{
  return (this->start < rhs.start);
}

Programme::Programme(TiXmlElement *element)
  : start(0), stop(0)
{
	TiXmlNode *node;
	TiXmlText *text;

	if (element->Attribute("start")) {
		start = str_to_time(element->Attribute("start"));
	}

	if (element->Attribute("stop")) {
		stop = str_to_time(element->Attribute("stop"));
	} 

	if (element->FirstChild("title")) {
		node = element->FirstChild("title");
		node = node->FirstChild();
		text = node->ToText();
		title = text->Value();
	}

	if (element->FirstChild("desc")) {
		node = element->FirstChild("desc");
		node = node->FirstChild();
		text = node->ToText();
		desc = text->Value();
	}
}

Channel::Channel(TiXmlElement *element)
{
	TiXmlNode *node = 0;
	TiXmlText *text = 0;

	if (element->Attribute("id")) {
		id = element->Attribute("id");
	}

	if (element->FirstChild("display-name")) {
		node = element->FirstChild("display-name");
		if (node != 0) {
		  node = node->FirstChild();
		  if (node != 0) {
		    text = node->ToText();
		    display_name = text->Value();
		  }
		}
	}
}

Epg* Epg::instance = 0;

Epg* Epg::get_instance() 
{
  if (instance == 0) {
    instance = new Epg;
  }
  return instance;
}

bool Epg::epg(list<Channel> *chan)
{
  TiXmlDocument doc(conf->p_epg_data().c_str());
  TiXmlElement* root_element;
  TiXmlElement* element;

  doc.LoadFile();
  root_element = doc.RootElement();

  if (root_element == 0)
    return false;

  for (element = root_element->FirstChildElement("channel"); element != 0;
       element = element->NextSiblingElement("channel")) {
    chan->push_back(Channel(element));
  }

  list<Channel>::iterator channel_iterator = chan->begin();

  string last_id = "";

  for (element = root_element->FirstChildElement("programme"); element != 0;
       element = element->NextSiblingElement("programme")) {

    if (last_id != element->Attribute("channel")) // find a new channel
      for (channel_iterator = chan->begin();
	   channel_iterator != chan->end(); ++channel_iterator)
	if (channel_iterator->id == element->Attribute("channel")) {
	  last_id = channel_iterator->id;
	  break;
	}

    Programme p = Programme(element);

    if (channel_iterator->programme.size() > 0)
      if (channel_iterator->programme.back().stop == 0)
	channel_iterator->programme.back().stop = p.start;
    
    channel_iterator->programme.push_back(p);
  }

  for (list<Channel>::iterator chan_iter = chan->begin(), end = chan->end(); 
       chan_iter != end;) {
    if (chan_iter->programme.size() > 0) {
      std::sort(chan_iter->programme.begin(), chan_iter->programme.end());
      ++chan_iter;
    } else
      chan_iter = chan->erase(chan_iter);
  }

  return true;
}

Epg::EpgOpts::EpgOpts() 
{
  // header
  header = gettext("EPG Options");

  // values

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

Epg::EpgOpts::~EpgOpts() 
{
  delete reload_p;
}

void Epg::update_time()
{
  struct tm *tm;
  
  cur_time = time(0);
  tm = localtime(&cur_time);
  if (tm->tm_min < 30) {
    tm->tm_min = 0;
  } else {
    tm->tm_min = 30;
  }
  tm->tm_sec = 0;
  cur_time = mktime(tm);
  sel_time = cur_time;
}

void Epg::read_db()
{
  if (!file_exists(conf->p_epg_data()))
    return;

  channels.clear();

  if (!Epg::epg(&channels))
    return;

  cur_channel = channels.begin();
  sel_channel = cur_channel;
  sel_channel_num = 0;

  update_time();

  for (program_iter = sel_channel->programme.begin(), 
	 program_iter_end = sel_channel->programme.end();
       program_iter != program_iter_end; ++program_iter)
    if (program_iter->stop >= sel_time)
      break;

  cur_time = sel_time = program_iter->start;
}

Epg::Epg()
  : exit_loop(false), epg_script_found(true)
{
  if (conf->p_v_res() == 720)
    y_start = 130;
  else if (conf->p_v_res() == 800)
    y_start = 140;
  else if (conf->p_v_res() == 1024)
    y_start = 170;
  else if (conf->p_v_res() == 1280)
    y_start = 170;
  else
    y_start = 100; // default value

#ifdef epg_pic
  x_start = 78;
#else
  x_start = int(78+30*60/10.25-25);
#endif

  rows = (conf->p_h_res()-(y_start+10+90+25))/40;
  
  if (conf->p_h_res() == 405)
    rows = (conf->p_h_res()-(y_start+10+80+25))/40;

  timeslots = int((conf->p_v_res()-x_start-15)/(30*60/10.25));

  scale_factor = (30*60)/(double(conf->p_v_res()-x_start-25)/timeslots);

  e = new EpgUpdate;
}

Epg::~Epg()
{
  Startmenu &startmenu = Startmenu::get_instance();

  if (startmenu.epg_update_running)
    run::my_system_no_kill(("killall -9 " + conf->p_epg_update_script()).c_str());
}

bool Epg::valid_program()
{
  bool found_match = false;

  time_t sel_start_time = program_iter->start;

  vector<Programme>::iterator piter;
  for (piter = sel_channel->programme.begin(), 
	 program_iter_end = sel_channel->programme.end();
       piter != program_iter_end; ++piter)
    if (piter->stop > sel_start_time && 
	piter->stop > cur_time) {
      found_match = true;
      break;
    } 

  program_iter = piter;

  return found_match;
}

void Epg::find_next_program()
{
  vector<Programme>::iterator end = sel_channel->programme.end();
  --end;

  vector<Programme>::iterator piter;
  for (piter = sel_channel->programme.begin(), 
	 program_iter_end = sel_channel->programme.end();
       piter != program_iter_end; ++piter)
    if (piter->start >= sel_time) {
      if (piter->start == sel_time && piter != end) {

	vector<Programme>::iterator last_program_iter = piter;
	++piter;

	// fix for strange programs with no length
	if (last_program_iter->start == last_program_iter->stop || 
	    piter->start == piter->stop && piter != end) {
	  ++piter;
	} 
      }
      break;
    } 

  program_iter = piter;

  if (program_iter == end)
    return;

  sel_time = program_iter->start;
  
  int time = timeslots*30*60;

  while (sel_time >= cur_time + time)
    cur_time += 30*60;
}

void Epg::find_valid_program()
{
  if (!valid_program()) {
    find_next_program();
    return;
  }
  
  sel_time = program_iter->start;
  
  int time = timeslots*30*60;

  while (sel_time >= cur_time + time)
    cur_time += 30*60;
}

bool Epg::check_db()
{
#ifdef epg_pic
  // check if the icons can be found
  bool icons = true;

  for (list<Channel>::iterator chan = channels.begin(), end = channels.end(); 
       chan != end; ++chan) {

    string::size_type dot_pos = chan->id.find(".");

    if (dot_pos == string::npos) {
      dot_pos = chan->id.size();
      DebugPrint perror(gettext("Error in channel id, please report this problem"), 
			Print::DEBUGGING, DebugPrint::CRITICAL, "EPG");
    }
      
    chan->logo = conf->p_homedir() + "epg_icons/" 
      + chan->id.substr(0, chan->id.find(".")) + ".gif";
    if (!file_exists(chan->logo)) {
      DebugPrint perror(gettext("Failed to find icon ") + chan->logo, 
			Print::DEBUGGING, DebugPrint::CRITICAL, "EPG");
      icons = false;
      break;
    }
  }

  if (!icons) {
    Print pdialog(Print::SCREEN);
    pdialog.add_line(gettext("Could not find icons for epg"));
    pdialog.add_line("");
    pdialog.add_line(gettext("Please install the icons and try again"));
    pdialog.print();

    return false;
  } 
#endif

  // check if the TV.xml file is new enough
  time_t max = 0;

  for (list<Channel>::iterator chan = channels.begin(), end = channels.end(); 
       chan != end; ++chan) {

    if (chan->programme.size() > 0) {
      vector<Programme>::iterator cha = chan->programme.end();
      --cha; 
      if (cha != chan->programme.begin()) 
	--cha;
      
      max = std::max(cha->stop, max);
    }
  }

  if (max < time(0)) {
    Print pdialog(gettext("Your TV listings file is too old or inaccessible"),
		  Print::SCREEN);

    return false;
  } else
    return true;
}

// commands
void Epg::print_description()
{
  BusyIndicator::get_instance().idle();

  Updater &updater = Updater::get_instance();

  if (program_iter->desc.size() != 0) {
    updater.timer.deactivate("epg");
    print_description(*program_iter);
    updater.timer.activate("epg");
  } else
    DialogWaitPrint pdialog(gettext("No description available"), 1500);
}

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

  Updater &updater = Updater::get_instance();
  
  updater.timer.deactivate("epg");
  opts.mainloop();
  updater.timer.activate("epg");
}

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

bool Epg::move_element_up()
{
  if (sel_channel != channels.begin()) {
    --sel_channel;
    
    if (sel_channel_num > 0) {
      --sel_channel_num;
    } else {
      --cur_channel;
    }

    return true;
  } else
    return false;
}

bool Epg::move_element_down()
{
  if (sel_channel != (--channels.end())) {
    ++sel_channel;

    if (sel_channel_num < (rows - 1)) {
      ++sel_channel_num;
    } else {
      ++cur_channel;
    }

    return true;
  } else
    return false;
}

string Epg::mainloop()
{
  input_master->cur_map = &input_master->epg;

  Startmenu &startmenu = Startmenu::get_instance();
  startmenu.module = "epg";

  Input input;
  
  Updater &updater = Updater::get_instance();

  // disable screensaver
  updater.timer.deactivate("audio_fullscreen");

  TimeFunctori<Epg> time_func(this, &Epg::check_epg_print);
  TimeFunctorv<Epg> call_func(this, &Epg::epg_print);
  updater.timer.add(TimeElement("epg", &time_func, &call_func));

  bool update_needed = true;

  BusyIndicator &busy_indicator = BusyIndicator::get_instance();
  Shutdown &sd = Shutdown::get_instance();
  
  while (!exit_loop) {

    if (update_needed)
      print();
    
    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 == "back" && input.mode == "general")
      {
	exit();
      }
    else if (input.command == "next") {

      if (move_element_down())
	find_valid_program();

    } else if (input.command == "prev") {

      if (move_element_up())
	find_valid_program();

    } else if (input.command == "page_up") {

      bool changed_channel = false;

      for (int i = 0; i < conf->p_jump(); ++i) {
	if (!move_element_up())
	  break;
	else
	  changed_channel = true;
      }

      if (changed_channel)
	find_valid_program();

    } else if (input.command == "page_down") {

      bool changed_channel = false;

      for (int i = 0; i < conf->p_jump(); ++i) {
	if (!move_element_down())
	  break;
	else
	  changed_channel = true;
      }

      if (changed_channel)
	find_valid_program();

    } else if (input.command == "action") {

      print_description();

    } else if (input.command == "right") {

      find_next_program();

    } else if (input.command == "left") {

      vector<Programme>::iterator old_program_iter = program_iter;
      vector<Programme>::iterator piter;
      
      for (piter = sel_channel->programme.begin();
	   piter != sel_channel->programme.end(); ++piter) {
	if (piter->start > sel_time ) {
	  if (piter != sel_channel->programme.begin())
	    --piter;
	  if (piter != sel_channel->programme.begin())
	    --piter;
	  if (old_program_iter->stop == piter->stop &&
	      old_program_iter->start == piter->start)
	    if (piter != sel_channel->programme.begin())
	      --piter;

	  break;
	}
      }

      program_iter = piter;
    
      if (program_iter == sel_channel->programme.end()) {
	--program_iter;
	--program_iter;
      }
      
      sel_time = program_iter->start;

      while (sel_time < cur_time)
	cur_time -= 30*60;
    } 
    else if (input.command == "options")
      {
	options();
      }
    else if (input.command == "second_action")
      {
	ExtraMenu em;

	TimeFunctorv<Epg> t_a = TimeFunctorv<Epg>(this, &Epg::print_description);
	em.add_item(ExtraMenuItem(gettext("Print description"), input_master->find_shortcut("action"), 
				  &t_a));
	TimeFunctorv<Epg> t_o = TimeFunctorv<Epg>(this, &Epg::options);
	em.add_item(ExtraMenuItem(gettext("Options"), input_master->find_shortcut("options"), 
				  &t_o));
	TimeFunctorv<Epg> t_back = TimeFunctorv<Epg>(this, &Epg::exit);
	em.add_item(ExtraMenuItem(gettext("Return to startmenu"), 
				  input_master->find_shortcut("back"), 
				  &t_back));

#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
      update_needed = !general_loop_check(input);
  }

  updater.timer.del("epg");

  exit_loop = false;

  updater.timer.activate("audio_fullscreen");

  return "";
}

void Epg::print()
{
  render->prepare_new_image();

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

  render->current.add(new PObj(themes->startmenu_epg, 25, 10, 1, NOSCALING));

  if (themes->show_header) {
    render->current.add(new TObj(gettext("EPG"), "Vera/28", 100, 10, 
				 themes->epg_header_font1, themes->epg_header_font2, 
				 themes->epg_header_font3, 1));
  }

  char time_str[30];
  strftime(time_str, 30, "%A %d-%m-%Y", localtime(&cur_time));

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

  render->current.add(new TObj(time_str, "Vera/17", (conf->p_v_res()-x_size)/2, y_start-65,
			       themes->epg_font1, themes->epg_font2, 
			       themes->epg_font3, 2));

  time_t tmp_time = time(0);
  strftime(time_str, 30, "%H:%M %d-%m-%Y", localtime(&tmp_time));

  string_format::calculate_string_size(time_str, "Vera/17", &x_size, &y_size);

  int time_pos_from_right = 15;

  if (conf->p_busy_indicator())
    time_pos_from_right = 32;

  render->current.add(new TObj(time_str, "Vera/17", conf->p_v_res()-(x_size+time_pos_from_right), 22, 
			       themes->epg_font1, themes->epg_font2, 
			       themes->epg_font3, 2));

  time_t temp_time = time(0);

  double diff = 0;
  diff += (temp_time-cur_time)/(30*60);
  diff += ((temp_time-cur_time)/60-diff*30)/30.0;

#ifdef epg_pic

  if (diff > 0 && diff < timeslots)
    render->current.add(new RObj(int(x_start + (diff*30*60)/scale_factor), y_start, 2, rows*40+10,
				 themes->epg_time_bar1, themes->epg_time_bar2, 
				 themes->epg_time_bar3, themes->epg_time_bar4, 2));
  
  render->current.add(new RObj(x_start, y_start, 2, rows*40+10, 
			       themes->epg_bar1, themes->epg_bar2, 
			       themes->epg_bar3, themes->epg_bar4, 1));

  for (int x = 0; x < timeslots; ++x) {
    tmp_time = cur_time + x*30*60;
    strftime(time_str, 30, "%H:%M", localtime(&tmp_time));
    render->current.add(new TObj(time_str, "Vera/17", 
				 static_cast<int>(x_start-28 + (x*30*60)/scale_factor), y_start-30, 
				 themes->epg_font1, themes->epg_font2, 
				 themes->epg_font3, 2));
    render->current.add(new RObj(static_cast<int>(x_start + ((x+1)*30*60)/scale_factor), y_start, 2, rows*40+10,
				 themes->epg_bar1, themes->epg_bar2, themes->epg_bar3, 
				 themes->epg_bar4, 1));
  }
#else

  if (diff > 0 && diff < timeslots)
    render->current.add(new RObj(int(x_start + diff*30*60/scale_factor), y_start-10, 2, rows*40+18,
				 themes->epg_time_bar1, themes->epg_time_bar2, 
				 themes->epg_time_bar3, themes->epg_time_bar4, 2));

  render->current.add(new RObj(x_start, y_start-10, 2, rows*40+18, 
			       themes->epg_bar1, themes->epg_bar2, 
			       themes->epg_bar3, themes->epg_bar4, 1));

  for (int x = 0; x < timeslots; ++x) {
    tmp_time = cur_time + x*30*60;
    strftime(time_str, 30, "%H:%M", localtime(&tmp_time));
    render->current.add(new TObj(time_str, "Vera/17", 
				 static_cast<int>(x_start-28 + (x*30*60)/scale_factor), y_start-40, 
				 themes->epg_font1, themes->epg_font2, 
				 themes->epg_font3, 2));
    render->current.add(new RObj(static_cast<int>(x_start + ((x+1)*30*60)/scale_factor), y_start-10, 2, rows*40+18,
				 themes->epg_bar1, themes->epg_bar2, themes->epg_bar3, 
				 themes->epg_bar4, 1));
  }
#endif

  if ((x_start + timeslots*30*60/scale_factor + 28) < conf->p_v_res()) {
    tmp_time = cur_time + timeslots*30*60;
    strftime(time_str, 30, "%H:%M", localtime(&tmp_time));
    render->current.add(new TObj(time_str, "Vera/17", int(x_start-28 + timeslots*30*60/scale_factor), y_start-40, 
				 themes->epg_font1, themes->epg_font2, themes->epg_font3, 2));
  }
  
  std::ostringstream footer;
  int x, w;
  bool found_match = false;

  list<Channel>::iterator cur_chan = cur_channel;

  for (int y = 0; y < std::min(rows, int(channels.size())); ++y) {

#ifdef epg_pic
    render->current.add(new PObj(cur_chan->logo, 15, y*40+y_start+10, 2, NOSCALING, false));
#else
    string channel_name = cur_chan->display_name;
    string_format::format_to_size(channel_name, "Vera/16", x_start-10, false);
    render->current.add(new TObj(channel_name, "Vera/16", 15, y*40+y_start, 
				 themes->epg_font1, themes->epg_font2, 
				 themes->epg_font3, 2));
#endif

    for (vector<Programme>::iterator p_iter = cur_chan->programme.begin(),
	   end = cur_chan->programme.end();
	 p_iter != end; ++p_iter) {
      if (p_iter->stop > cur_time &&
	  p_iter->start < (cur_time + timeslots*30*60)) {

	// x = 80 + constant
	if (p_iter->start < cur_time) {
	  x = x_start+2;
	  w = int((p_iter->stop - cur_time)/scale_factor);
	} else {
	  x = int(x_start+2 + (p_iter->start - cur_time)/scale_factor);
	  w = int((p_iter->stop - p_iter->start)/scale_factor);
	}

	if (w < 14) // very short shows
	  w = 14;

	if (x + w > x_start+(timeslots*30*60)/scale_factor) {
	  // programs ending at the end of screen should just be cut off, others
	  // should cross the line a little to indicate that they end later.
	  w -= (x + w) - int(x_start+(timeslots*30*60)/scale_factor);
	  if (p_iter->stop > cur_time+timeslots*30*60)
	    w += 15;
	}
	
	if (cur_chan == sel_channel && p_iter->start <= sel_time && 
	    p_iter->stop > sel_time) {
	  if (!found_match) {
	    
	    found_match = true;

	    string name = p_iter->title;
	    string_format::format_to_size(name, "Vera/17", w - 10, false);
	    
	    render->current.add(new RObj(x, y*40+y_start+5, w - 5, 35, 
					 themes->epg_marked1, themes->epg_marked2, 
					 themes->epg_marked3, themes->epg_marked4, 3));
	    render->current.add(new TObj(name, "Vera/17", 
					 x+2, y*40+y_start+10, themes->epg_marked_font1, 
					 themes->epg_marked_font2, themes->epg_marked_font3, 4));

	    footer.str("");
	    strftime(time_str, 30, "%H:%M", localtime(&p_iter->start));
	    footer << time_str << " - ";
	    strftime(time_str, 30, "%H:%M", localtime(&p_iter->stop));
	    footer << time_str << gettext(" on ") << cur_chan->display_name;

	    string f = footer.str() + "   " + p_iter->title;

	    int f_size = f.size();
	    string_format::format_to_size(f, "Vera/17", conf->p_v_res()-40, false);

	    if (f.size() != f_size)
	      f += "...";
	    
	    render->current.add(new TObj(f, "Vera/17", 15, rows*40+y_start+20, themes->epg_font1, 
					 themes->epg_font2, themes->epg_font3, 4));
	  } 
	} else {

	  string name = p_iter->title;
	  string_format::format_to_size(name, "Vera/17", w - 10, false);

	  render->current.add(new RObj(x, y*40+y_start+5, w - 5, 35, 
				       themes->epg_boxes1, themes->epg_boxes2, 
				       themes->epg_boxes3, themes->epg_boxes4, 3));
	  render->current.add(new TObj(name, "Vera/17", x+2, 
				       y*40+y_start+10, themes->epg_boxes_font1, 
				       themes->epg_boxes_font2, themes->epg_boxes_font3, 4));
	}
      }
    }
    ++cur_chan;
  }

  render->draw("epg");

  render->image_mut.leaveMutex();
}

void Epg::print_description(Programme p)
{
  char time_str[20];
  std::ostringstream time;

  int i = 0;
  int round = 1;
  string::iterator it = p.desc.begin();
  std::ostringstream next_word;
  
  while (i < p.desc.size()) {
    
    render->prepare_new_image();

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

    render->current.add(new PObj(themes->startmenu_epg, 25, 10, 1, NOSCALING));

    render->current.add(new TObj("EPG", "Vera/28", 100, 10,
				 themes->epg_header_font1, themes->epg_header_font2, 
				 themes->epg_header_font3, 1));

    render->current.add(new RObj(55, 90, conf->p_v_res()-2*55, conf->p_h_res()-2*93, 
				 themes->general_rectangle_color1, themes->general_rectangle_color2,
				 themes->general_rectangle_color3, themes->general_rectangle_color4, 1));

    int title_start = 75;

#ifdef epg_pic
    render->current.add(new PObj(sel_channel->logo, 75, 100, 2, NOSCALING, false));

    title_start = 140;

#endif

    int x_size, y_size;

    string title = p.title;
    string_format::format_to_size(title, "Vera/22", conf->p_v_res()-(2*65), true);

    string_format::calculate_string_size(title, "Vera/22", &x_size, &y_size);
    render->current.add(new TObj(title, "Vera/22", title_start, 95,
				 themes->epg_font1, themes->epg_font2, themes->epg_font3, 2));

    strftime(time_str, 20, "%H:%M", localtime(&p.start));
    time << time_str <<  " - ";
    strftime(time_str, 20, "%H:%M", localtime(&p.stop));
    time << time_str;

    render->current.add(new TObj(time.str(), "Vera/18", 75, 130, 
				 themes->epg_font1, themes->epg_font2, themes->epg_font3, 2));
    time.str("");

    int start = 170;
    if (conf->p_h_res() == 405)
      start = 150;

    int y = start;

    int box_width = conf->p_v_res()-2*75;

    int nr_rows = 12; // 800x600
    if (conf->p_v_res() == 720) {
      if (conf->p_h_res() == 540)
	nr_rows = 10;
      else
	nr_rows = 5;
    } else if (conf->p_v_res() == 1024) {
      nr_rows = 18;
    }
    
    while (i != p.desc.size()) {
      string desc = string_format::get_line(p.desc, i, box_width, "Vera/16");

      render->current.add(new TObj(desc, "Vera/16", 75, y, 
				   themes->epg_font1, themes->epg_font2, 
				   themes->epg_font3, 2));
      y += 27;
      
      if (y == start+nr_rows*27)
	break;
    }

    render->draw("EPG description");

    render->image_mut.leaveMutex();

    // wait for keypress
    input_master->device->get_input();

    ++round;
  }
}

void Epg::epg_print()
{
  if (conf->p_idle_time() != 0) {
    // update the idle timer
    int idle = time(0)-conf->p_last_key();
    if (idle > 60*conf->p_idle_time() && 
	(program_iter->stop > time(0) &&
	 program_iter->start < (time(0) + timeslots*30*60)))
      update_time();
  }
  print();
}

int Epg::check_epg_print()
{
  return 60*1000; // ms
}

void Epg::update_epg_datafile()
{
  Startmenu &startmenu = Startmenu::get_instance();

  if (!conf->p_epg_update_script().empty() 
      && file_exists(conf->p_epg_update_script())
      && !startmenu.epg_update_running) {
    startmenu.epg_update_running = true;
    e->start();
  } else {
    epg_script_found = false;
    DebugPrint perror(gettext("Could not find epg update program"), 
		      Print::DEBUGGING, DebugPrint::WARNING, "EPG");
  }
}

void EpgUpdate::run()
{
  Config *conf = Config::get_instance();

  run::my_system_no_kill(conf->p_epg_update_script().c_str());

  Print pdialog(Print::SCREEN);
  pdialog.add_line(gettext("EPG TV listings file reloaded"));
  pdialog.add_line("");
  pdialog.add_line(gettext("The new data file will be used"));
  pdialog.add_line(gettext("the next time you enter EPG"));
  pdialog.print();

  // tell the startmenu to reload db
  Startmenu &startmenu = Startmenu::get_instance();
  startmenu.epg_loaded = false;
  startmenu.epg_update_running = false;
}

int Epg::check_epg_datafile()
{
  if (!conf->p_epg_data().empty()) {

    struct stat s;

    stat(conf->p_epg_data().c_str(), &s);

    time_t diff = time(0) - s.st_mtime;

    int hours = diff / 60 / 60;

    if (!epg_script_found)
      return 60*1000; /* wait one minutes so that the user has time to copy
			 the script to the correct place */
    
    Startmenu &startmenu = Startmenu::get_instance();
    if (hours > 24 && !startmenu.epg_update_running)
      return 0; // update now
    else
      return 12*60*60*1000; // ms
  }

  return 24*60*60*1000; // just wait a long time
}

void Epg::startup_updater()
{
  Updater &updater = Updater::get_instance();
  TimeFunctori<Epg> *time_func = new TimeFunctori<Epg>(this, &Epg::check_epg_datafile);
  TimeFunctorv<Epg> *call_func = new TimeFunctorv<Epg>(this, &Epg::update_epg_datafile);
  updater.timer.add(TimeElement("epg_data_update", time_func, call_func));
}
