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

#include "libfspp.hpp"

#include "audio.hpp"

#ifdef use_imms
#include "library-builder/fetcher.h"
#endif

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

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

#include "updater.hpp"
#include "extra_menu.hpp"

// time 
#include <time.h>

//Input
#include "input.hpp"

//lcd
#include "display.hpp"

// cd 
#ifdef use_media
#include <errno.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include "kernelheaders.hpp"
#endif

#include "shoutcast.hpp"

#include <fstream>
#include <iomanip>

// sleep
#include <unistd.h>

#include <stdio.h>

// ostringstream
#include <sstream>

#include <iostream>

#include <cassert>

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

Audio* Audio::instance = 0;

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

Audio::Audio() 
  : exit_loop(false), change_mode(false), 
#ifdef use_media
    mounted_media(false),
#endif
    playlist_id(0), files(&audio_list), display_volume(false), 
    radio_stations_downloaded(false), audio_playback_offset(0), 
    disabled_busy_indicator(false), printing_information(false),
    display_message(true)
{
#ifdef use_imms
  extracted_all_files = false;

  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_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;
#endif

  if (conf->p_audio_mode() == BOTH)
    mode = ADD;
  else
    mode = PLAY;

  reset();

  audio_state = Audio_s::get_instance();

#ifdef music_radio_bttv
  bttv = Bttv::get_instance();
#endif
}

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

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

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

  files = &audio_list;
  mode = ADD;
}

Audio::~Audio()
{
  // save the playlist
  save_playlist("last");
}

bool Audio::file_sort::operator()(const audio_type& lhs, const audio_type& rhs)
{
  return sort_order(lhs.lowercase_name, rhs.lowercase_name, lhs.type == "dir", rhs.type == "dir");
}

// commands
void Audio::exit()
{
  exit_loop = true;
}

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

  CompareFunctor<Audio, audio_type> compare_func(this, &Audio::search_compare);
#ifdef use_imms
  TSpecificFunctorNonconst<Audio, Dbaudiofile> print_func(this, &Audio::print);
#else
  TSpecificFunctor<Audio, Simplefile> print_func(this, &Audio::print);
#endif
  RepFunctor<Audio, audio_type> rep_func(this, &Audio::get_name_from_file);
  if (mode == ADD)
    search(*files, &folders.top().second, &print_func, &compare_func, &rep_func);
  else
    search(*files, &playlist_pos_int, &print_func, &compare_func, &rep_func);
}

void Audio::options()
{
  Startmenu &startmenu = Startmenu::get_instance();

  BusyIndicator::get_instance().idle();

  string random_mode_before = opts.shuffle();

  if (opts.mainloop()) {
    //reload
    if (conf->p_audio_mode() == BOTH) {
	      
      input_master->cur_map = &input_master->audio_add;
      mode = ADD;
      files = &audio_list;
      startmenu.module = "audio";
	      
    } else {
	      
      //winamp only
      files = &playlist;
    }
    set_position_int(0);
    cover_map.clear();
#ifdef use_imms
    extracted_all_files = false;
#endif
  }

  if (opts.shuffle() != random_mode_before) {
    shuffle_list.randomize();
    audio_state->empty_played_tracks();
  }
}

void Audio::add()
{
  string message;

  // add file or dir to playlist add the buttom of the list
  if (vector_lookup(*files, position_int()).type == "dir") {
	      
    vector<audio_type> addfiles = add_dir(vector_lookup(*files,position_int()).path);

    for (vector<audio_type>::const_iterator iter = addfiles.begin(); iter != addfiles.end(); ++iter)
      {
	audio_type p = *iter;
	p.id = ++playlist_id;
	playlist.push_back(p);
	shuffle_list.add_track(p);

	if (playlist.size() == 1)
	  // first element added so we need to set the pos
	  playlist_pos_int = 0;
      }

    if (display_message)
      message = gettext("Added directory to playlist");
	      
  } else {
	      
    audio_type p = vector_lookup(*files, position_int());
    p.id = ++playlist_id;
    playlist.push_back(p);
    shuffle_list.add_track(p);

    if (playlist.size() == 1)
      // first element added so we need to set the pos
      playlist_pos_int = 0;

    if (display_message)
      message = gettext("Added track to playlist");

  }
  if (display_message)
    DialogWaitPrint pdialog(message, 1000);
}

void Audio::add_all()
{
  for (vector<audio_type>::const_iterator iter = audio_list.begin(); iter != audio_list.end(); ++iter)
    {
      if (iter->type != "dir") {
	audio_type p = *iter;
	p.id = ++playlist_id;
	playlist.push_back(p);
	shuffle_list.add_track(p);
	
	if (playlist.size() == 1)
	  // first element added so we need to set the pos
	  playlist_pos_int = 0;
      }
    }

  DialogWaitPrint pdialog(gettext("Added directory to playlist"), 1000);
}

void Audio::play_now() 
{
  if (add_dir(vector_lookup(*files,position_int()).path).size() == 0) {
    DialogWaitPrint pdialog(gettext("Folder is empty"), 1000);
    return;
  }
  
  if (play_now_warning) {
    DialogWaitPrint pdialog(5000);
    pdialog.add_line(gettext("Clearing playlist to play contents of folder"));
    pdialog.add_line(gettext("The old playlist has been saved as 'autosaved'"));
    pdialog.print();
    play_now_warning = false;
  }

  save_playlist("autosaved", true);

  clear_playlist();
  // don't change mode to playlist
  change_mode = false;
  display_message = false;
  add();
  display_message = true;
  play_track();
}

void Audio::play_track_now() 
{
  add();
  playlist_pos_int = playlist.size()-1;
  play_track();
}

void Audio::enter_dir()
{
  if (vector_lookup(*files, position_int()).type == "dir")
    {
      string temp_cur_dir = vector_lookup(*files, position_int()).path;

#ifdef use_imms
      int nr_files = 0;
      for (file_iterator<file_t, default_order> i (temp_cur_dir); i != i.end (); i.advance(false))
	if (!isDirectory(i->getName()) && check_type(i->getName(), conf->p_filetypes_a()) != emptyMyPair)
	  ++nr_files;
		

      Updater &updater = Updater::get_instance();

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

      updater.status_progressbar = 0;
      updater.total_progressbar = nr_files;
      updater.header = gettext("Extracting metadata");

      TimeFunctoriNC cp(&check_progressbar);
      TimeFunctorvNC pg(&print_progressbar);

      updater.timer.add(TimeElement("progressbar", &cp, &pg));
#endif

      vector<audio_type> templist = rdir(temp_cur_dir);

#ifdef use_imms
      updater.timer.del("progressbar");
      cleanup_progressbar();

      // reenable screensaver
      updater.timer.activate("audio_fullscreen");
#endif

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

void Audio::leave_dir()
{
  if (!(folders.size() == 1)) {
    folders.pop();

    load_current_dirs();
  }
}

void Audio::load_current_dirs()
{
  audio_list = parse_dir(folders.top().first);

  if (conf->p_audio_dirs().size() > 1 && folders.size() == 1)
    std::sort(audio_list.begin(), audio_list.end(), file_sort());
}

#ifdef use_media
void Audio::pos_umount()
{
  if (mounted_media) {
    run::my_system_no_kill(("umount " + cd->get_mount_point()).c_str());
    mounted_media = false;
  }
}

void Audio::check_mount_before(const audio_type& track)
{
  if (!mounted_media && track.type == "media-file") {
    run::my_system_no_kill(("mount " + cd->get_mount_point()).c_str());
    mounted_media = true;
  } 
}

void Audio::check_mount_after(const audio_type& track)
{
  if (mounted_media && track.type != "media-file") {
    run::my_system_no_kill(("umount " + cd->get_mount_point()).c_str());
    mounted_media = false;
  }
}
#endif

void Audio::play_track()
{
  audio_type track = vector_lookup(playlist, playlist_pos_int);
  
#ifdef use_media
  check_mount_before(track);
#endif
  
  audio_state->p->set_cur_nr(track);

#ifdef music_radio_bttv
    if (audio_state->p->p_cur_nr().type == "bttvradio")
      bttv->play(audio_state->p->p_cur_nr().path);
    else
#endif
      audio_state->p->addfile(track);

#ifdef use_media
  check_mount_after(track);
#endif

  if (audio_state->played_tracks.size() == 0) {
    // notify the random track generation that we want to start with this track
    shuffle_list.track_played(track);
  }
  
  audio_state->add_track_to_played(track);
	      
  if (opts.shuffle() == gettext("real random"))
    audio_state->direction = Audio_s::ADDED;
}

void Audio::move_up()
{
  if (playlist.size() != 1) {
    if (playlist_pos_int != 0) {
      audio_type r;
      vector<audio_type>::iterator j = playlist.begin() + playlist_pos_int;

      r = (*j);
      playlist.erase(j); --j;
      playlist.insert(j++, r);
      --j;

      // move the view
      playlist_pos_int = playlist_pos_int - 1;
    } else {
      vector<audio_type>::iterator j = playlist.end(); --j;
      audio_type r_end = playlist.back();
      audio_type r_start = playlist.front();

      playlist.erase(j);
      playlist.erase(playlist.begin());
      playlist.push_back(r_start);
      playlist.insert(playlist.begin(), r_end);
      j = playlist.end(); --j;

      // move the view
      playlist_pos_int = playlist.size()-1;
    }
  }
}

void Audio::move_down()
{
  if (playlist.size() != 1) {
    if ((playlist_pos_int + 1) != playlist.size()) {
      audio_type r;
      vector<audio_type>::iterator j = playlist.begin() + playlist_pos_int + 1;

      r = (*j);
      playlist.erase(j);
      playlist.insert(--j, r);
		
      // move the view
      playlist_pos_int = playlist_pos_int + 1;
    } else {
      vector<audio_type>::iterator j = playlist.end(); --j;
      audio_type r_end = playlist.back();
      audio_type r_start = playlist.front();

      playlist.erase(j);
      playlist.erase(playlist.begin());
      playlist.push_back(r_start);
      playlist.insert((playlist.begin()), r_end);
      j = playlist.begin();

      // move the view
      playlist_pos_int = 0;
    }
  }
}

void Audio::delete_track()
{
  assert(playlist_pos_int < playlist.size());
  
  vector<audio_type>::iterator i = playlist.begin() + playlist_pos_int;

  if (files->size() == 1) {
	    
    if (conf->p_audio_stop_on_clear()) {
      audio_state->p->stop();
      Simplefile empty;
      audio_state->p->set_cur_nr(empty);
    }

    // remove track if queued
    audio_state->remove_track_from_queue(*i);
    
    audio_state->remove_track_from_played(*i);

    playlist.clear();
    shuffle_list.clear();

    if (conf->p_audio_mode() == PLAYLIST)
      exit_loop = true;
    else
      change_mode = true;

  } else {

    if (audio_state->p->p_cur_nr() == *i)
      audio_state->p->next();

    // remove track if queued
    audio_state->remove_track_from_queue(*i);

    audio_state->remove_track_from_played(*i);

    shuffle_list.remove_track(*i);
    playlist.erase(i);

    // this is playlist.size(), not -1, because we just deleted a track
    if (playlist_pos_int == playlist.size())
      // the last element of the list
      playlist_pos_int = playlist_pos_int - 1;
  }
}

void Audio::queue_track()
{
  Simplefile track = vector_lookup(*files, position_int());
  if (audio_state->queue_pos(track) == 0)
    audio_state->add_track_to_queue(track);
  else
    audio_state->remove_track_from_queue(track);
}

void Audio::clear_playlist()
{
  if (conf->p_audio_stop_on_clear()) {
    audio_state->p->stop();
    Simplefile empty;
    audio_state->p->set_cur_nr(empty);
  }
  
  audio_state->remove_queued_tracks();

  audio_state->empty_played_tracks();

  playlist.clear();
  shuffle_list.clear();

  if (conf->p_audio_mode() == PLAYLIST)
    exit_loop = true;
  else
    change_mode = true;
}

void Audio::save_playlist_func()
{
  BusyIndicator::get_instance().idle();

  string name = "";
  TSaveAudioFunctor *print_func = new TSaveAudioFunctor(&print_dialog_fixed_size);
  GenerateReturn r = generate_string(name, print_func);
  delete print_func;
  usleep(100*1000);
  if (r == OK)
    save_playlist(name);
}

int Audio::get_real_playlist_pos()
{
  Simplefile empty;
  Simplefile cur_track = audio_state->p->p_cur_nr();

  if (cur_track != empty) {
#ifdef use_imms
    vector<Dbaudiofile>::iterator j = playlist.begin();
#else
    vector<Simplefile>::iterator j = playlist.begin();
#endif  

    int pos = 0;
    while (j != playlist.end()) {
      if (j->id == cur_track.id)
	return pos;
      else {
	++j;
	++pos;
      }
    }
    
    return playlist_pos_int;

  } else {
    return playlist_pos_int;
  }
}

string Audio::format_time(int cur_time, int total_time)
{
  char buffer [512];
  snprintf(buffer, 512, "%02d:%02d / %02d:%02d", min(cur_time), 
	   sec(cur_time), min(total_time), sec(total_time));
  return buffer;
}

string Audio::format_time(int time)
{
  char buffer [512];
  snprintf(buffer, 512, "%02d:%02d", min(time), sec(time));
  return buffer;
}

const int Audio::position_int()
{
  if (mode == ADD)
    return folders.top().second;
  else
    return playlist_pos_int;
}

inline void Audio::set_position_int(int pos)
{
  if (mode == ADD)
    folders.top().second = pos;
  else
    playlist_pos_int = pos;
}

string Audio::mainloop(AudioMode new_mode)
{
  BusyIndicator &busy_indicator = BusyIndicator::get_instance();
  Shutdown &sd = Shutdown::get_instance();
  
  if (new_mode == ADD) {
    input_master->cur_map = &input_master->audio_add;
    files = &audio_list;
    mode = ADD;
  } else {
    input_master->cur_map = &input_master->playlist;
    files = &playlist;
    mode = PLAY;
  }

  Startmenu &startmenu = Startmenu::get_instance();
  startmenu.module = (mode == ADD) ? "audio" : "playlist";

  Input input;

  bool update_needed = true;

  while (!exit_loop)
    {
      if (update_needed)
	print(*files);

      busy_indicator.idle();

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

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

      update_needed = true;
     
      // general
      if (input.command == "page_up")
 	{
	  int jump = conf->p_jump();

#ifdef use_imms
	  if (mode == ADD) {
	    if (search_mode)
	      jump = images_per_row*rows_search;
	    else
	      jump = images_per_row*rows;
	  }
#endif

	  if (files->size() > jump) {
	    int pos = position_int()-jump;
	    if (position_int() == 0)
	      set_position_int(files->size()-1+pos);
	    else if (pos < 0)
	      set_position_int(0);
	    else
	      set_position_int(pos);
	  }
 	}
      else if (input.command == "page_down")
 	{
	  int jump = conf->p_jump();

#ifdef use_imms
	  if (mode == ADD) {
	    if (search_mode)
	      jump = images_per_row*rows_search;
	    else
	      jump = images_per_row*rows;
	  }
#endif

	  if (files->size() > jump) {
	    if (position_int() > (files->size() - jump) && position_int() != (files->size()-1))
	      set_position_int(files->size()-1);
	    else
	      set_position_int((position_int()+jump)%files->size());
	  }
 	}
      else if (input.command == "second_action")
	{
	  ExtraMenu em;

	  // audio add
	  TimeFunctorv<Audio> t_add = TimeFunctorv<Audio>(this, &Audio::add);
	  TimeFunctorv<Audio> t_play_now = TimeFunctorv<Audio>(this, &Audio::play_now);
	  TimeFunctorv<Audio> t_play_track_now = TimeFunctorv<Audio>(this, &Audio::play_track_now);
	  TimeFunctorv<Audio> t_ed = TimeFunctorv<Audio>(this, &Audio::enter_dir);
	  TimeFunctorv<Audio> t_gu = TimeFunctorv<Audio>(this, &Audio::leave_dir);
	  TimeFunctorv<Audio> t_aall = TimeFunctorv<Audio>(this, &Audio::add_all);
	  // playlist
	  TimeFunctorv<Audio> t_play = TimeFunctorv<Audio>(this, &Audio::play_track);
	  TimeFunctorv<Audio> t_mu = TimeFunctorv<Audio>(this, &Audio::move_up);
	  TimeFunctorv<Audio> t_md = TimeFunctorv<Audio>(this, &Audio::move_down);
	  TimeFunctorv<Audio> t_dt = TimeFunctorv<Audio>(this, &Audio::delete_track);
	  TimeFunctorv<Audio> t_qt = TimeFunctorv<Audio>(this, &Audio::queue_track);
	  TimeFunctorv<Audio> t_cl = TimeFunctorv<Audio>(this, 
							 &Audio::clear_playlist);
	  TimeFunctorv<Audio> t_cmp = TimeFunctorv<Audio>(this, 
							  &Audio::change_mode_from_playlist);
	  TimeFunctorv<Audio> t_sp = TimeFunctorv<Audio>(this, 
							 &Audio::save_playlist_func);
	    
	  if (mode == ADD) {

#ifdef use_imms
	    if (vector_lookup(*files, position_int()).type == "dir") {
	      em.add_item(ExtraMenuItem(gettext("Enter directory"), 
					input_master->find_shortcut("action"), &t_ed));
	      em.add_item(ExtraMenuItem(gettext("Add directory to playlist"), 
					input_master->find_shortcut("add_dir"), 
					&t_add));
	    } else 
	      em.add_item(ExtraMenuItem(gettext("Add track to playlist"), 
					input_master->find_shortcut("action"), &t_add));
	    
#else
	    em.add_item(ExtraMenuItem(gettext("Add directory to playlist"), 
				      input_master->find_shortcut("action"), 
				      &t_add));
	    
	    if (vector_lookup(*files, position_int()).type == "dir") {
	      em.add_item(ExtraMenuItem(gettext("Enter directory"), 
					input_master->find_shortcut("right"), 
					&t_ed));
	    }

	    if (folders.size() > 1)
	      em.add_item(ExtraMenuItem(gettext("Go up one directory"), 
					input_master->find_shortcut("left"), 
					&t_gu));
#endif

	    if (conf->p_audio_mode_playnow() && 
		vector_lookup(*files, position_int()).type == "dir")
	      em.add_item(ExtraMenuItem(gettext("Play now"), 
					input_master->find_shortcut("play_now"),
					&t_play_now));
	    else if (vector_lookup(*files, position_int()).type != "dir")
	      em.add_item(ExtraMenuItem(gettext("Play track now"), 
					input_master->find_shortcut("play_now"),
					&t_play_track_now));

	    if (vector_lookup(*files, position_int()).type != "dir") {
	      em.add_item(ExtraMenuItem(gettext("Add all tracks to playlist"), 
					input_master->find_shortcut("add_all"), 
					&t_aall));
	    }

	  } else {
	    em.add_item(ExtraMenuItem(gettext("Play track"), 
				      input_master->find_shortcut("action"), 
				      &t_play));
	    em.add_item(ExtraMenuItem(gettext("Move track up"), 
				      input_master->find_shortcut("move_up"), 
				      &t_mu));
	    em.add_item(ExtraMenuItem(gettext("Move track down"), 
				      input_master->find_shortcut("move_down"), 
				      &t_md));
	    em.add_item(ExtraMenuItem(gettext("Delete track"), 
				      input_master->find_shortcut("delete"), 
				      &t_dt));
	    em.add_item(ExtraMenuItem(gettext("Queue track"), 
				      input_master->find_shortcut("queue"), 
				      &t_qt));
	    em.add_item(ExtraMenuItem(gettext("Clear playlist"), 
				      input_master->find_shortcut("clear_list"), 
				      &t_cl));
	    em.add_item(ExtraMenuItem(gettext("Save playlist"),
				      input_master->find_shortcut("save_playlist"), 
				      &t_sp));
	  }

	  em.add_item(ExtraMenuItem(gettext("Load playlist"), 
				    input_master->find_shortcut("playlist_mode"), &t_cmp));

#ifdef use_imms
	  TimeFunctorv<Audio> t_pi = TimeFunctorv<Audio>(this, 
							 &Audio::print_information);
	  if (vector_lookup(*files, position_int()).type != "dir") {
	    em.add_item(ExtraMenuItem(gettext("Print information"), 
				      input_master->find_shortcut("info"), 
				      &t_pi));
	  }
#endif

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

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

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

	  em.mainloop();
	}
      else if (input.command == "search")
	{
	  search_func();
	}
      else if (input.command == "options")
	{
	  options();
	}

      // playlist
      if (mode == PLAY && input.command == "move_up")
	{
	  move_up();
	}
      else if (mode == PLAY && input.command == "move_down")
	{
	  move_down();
	}
      else if (mode == PLAY && input.command == "delete")
	{
	  delete_track();
	}
      else if (mode == PLAY && input.command == "queue")
	{
	  queue_track();
	}
      else if (mode == PLAY && input.command == "clear_list")
	{
	  clear_playlist();
	}
      else if (mode == PLAY && input.command == "save_playlist")
	{
	  save_playlist_func();
	}
      // play specific
      else if (mode == PLAY && input.command == "action")
	{
	  play_track();
	}
#ifdef use_imms
      else if (input.command == "info" && input.mode == "graphical") 
	{
	  print_information();
	}
      else if (mode == ADD and !no_folders_in_dir(*files)) {
	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")
	  {
	    if (folders.size() > 1)
	      leave_dir();
	    else
	      exit();
	  }
	else if (input.command == "add_dir") 
	  {
	    if (vector_lookup(*files, position_int()).type == "dir")
	      add();
	  }
  	else if (input.command == "action")
	  {
	    if (vector_lookup(*files, position_int()).type == "dir")
	      enter_dir();
	    else
	      add();
	  }
  	else if (input.command == "play_now")
	  {
	    if (conf->p_audio_mode_playnow() && 
		vector_lookup(*files, position_int()).type == "dir")
	      play_now();
	  }
      } else {
	if (input.command == "prev")
#else
	else if (input.command == "prev")
#endif
	  {
	    if (position_int() != 0)
	      set_position_int(position_int()-1);
	    else
	      set_position_int(files->size()-1);
	  }
	else if (input.command == "next")
	  {
	    set_position_int((position_int()+1)%files->size());
	  }
#ifdef use_imms
	else if (input.command == "back" && input.mode == "general")
	  {
	    if (mode == ADD && folders.size() > 1)
	      leave_dir();
	    else
	      exit();
	  }
#else
	else if (input.command == "back" && input.mode == "general")
	  {
	    exit();
	  }
#endif
	// audio-add
	else if (mode == ADD) {
	  
	  if (input.command == "action")
	    {
	      add();
	    }
	  else if (input.command == "right")
	    {
	      enter_dir();
	    }
	  else if (input.command == "add_all")
	    {
	      add_all();
	    }
	  else if (input.command == "play_now")
	    {
	      if (vector_lookup(*files, position_int()).type != "dir")
		play_track_now();
	    }
#ifndef use_imms
	  else if (input.command == "left")
	    {
	      leave_dir();
	    }
#endif
	}
#ifdef use_imms
      }
#endif

      update_needed = !general_loop_check(input);
    }

  exit_loop = false;

  return "";
}

#ifdef use_imms
void Audio::print_information()
{
  BusyIndicator::get_instance().idle();

  if (isDirectory(vector_lookup(*files, position_int()).path))
    return;

  printing_information = true;
  
  render->prepare_new_image();

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

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

  if (themes->show_header) {
    render->current.add(new TObj(gettext("Audio - Information"), "Vera/28", 100, 10, 
				 themes->audio_header_font1, themes->audio_header_font2, 
				 themes->audio_header_font3, 2));
  }

  int y = 100;

  Dbaudiofile dbaf = vector_lookup(*files, position_int());
  // lookup information each time see this information as the rating of the
  // song might have changed
  dbaf = lookup_file_in_db(dbaf.path);

  string pic = get_cover_from_filename(vector_lookup(*files, position_int()));

  int max_x_pic = 240, max_y_pic = 266;
	  
  if (conf->p_v_res() == 1024) {
    max_x_pic = 300;
    max_y_pic = 333;
  } else if (conf->p_h_res() == 405) {
    max_x_pic = 135;
    max_y_pic = 150;
  }

  int x = max_x_pic + 35 + 35;
  if (pic.empty())
    x = 50;
  
  if (!pic.empty()) {
    render->create_scaled_image_wrapper(pic, max_x_pic, max_y_pic);
    render->current.add(new PFObj(pic, 35, y+5, max_x_pic, max_y_pic, true, 3));
  }

  int w, h, first_column_width = 0;
  string_format::calculate_string_size(gettext("Artist:"), "Vera/18", &w, &h);
  first_column_width = std::max(w, first_column_width);
  string_format::calculate_string_size(gettext("Album:"), "Vera/18", &w, &h);
  first_column_width = std::max(w, first_column_width);
  string_format::calculate_string_size(gettext("Title:"), "Vera/18", &w, &h);
  first_column_width = std::max(w, first_column_width);
  string_format::calculate_string_size(gettext("Year:"), "Vera/18", &w, &h);
  first_column_width = std::max(w, first_column_width);
  string_format::calculate_string_size(gettext("Bitrate:"), "Vera/18", &w, &h);
  first_column_width = std::max(w, first_column_width);
  string_format::calculate_string_size(gettext("Length:"), "Vera/18", &w, &h);
  first_column_width = std::max(w, first_column_width);
  string_format::calculate_string_size(gettext("Rating:"), "Vera/18", &w, &h);
  first_column_width = std::max(w, first_column_width);
  string_format::calculate_string_size(gettext("Track:"), "Vera/18", &w, &h);
  first_column_width = std::max(w, first_column_width);

  first_column_width += 10;

  string artist = string_format::convert(dbaf.artist, true);
  string_format::format_to_size(artist, "Vera/18", conf->p_v_res()-(x+first_column_width)-25, true);
  render->current.add(new TObj(gettext("Artist:"), "Vera/18", x, y, themes->audio_font1,
			       themes->audio_font2, themes->audio_font3, 3));
  render->current.add(new TObj(artist, "Vera/18", 
			       x+first_column_width, y, 
			       themes->audio_font1, themes->audio_font2, 
			       themes->audio_font3, 3));
  y+=28;

  string album = string_format::convert(dbaf.album, true);
  string_format::format_to_size(album, "Vera/18", conf->p_v_res()-(x+first_column_width)-25, true);
  render->current.add(new TObj(gettext("Album:"), "Vera/18", x, y, 
			       themes->audio_font1, themes->audio_font2, 
			       themes->audio_font3, 3));
  render->current.add(new TObj(album, "Vera/18", 
			       x+first_column_width, y,  
			       themes->audio_font1, themes->audio_font2, 
			       themes->audio_font3, 3));
  y+=28;

  string title = string_format::convert(dbaf.title, true);
  string_format::format_to_size(title, "Vera/18", conf->p_v_res()-(x+first_column_width)-25, true);
  render->current.add(new TObj(gettext("Title:"), "Vera/18", x, y, 
			       themes->audio_font1, themes->audio_font2, 
			       themes->audio_font3, 3));
  render->current.add(new TObj(title, "Vera/18", 
			       x+first_column_width, y,  
			       themes->audio_font1, themes->audio_font2, 
			       themes->audio_font3, 3));
  y+=28;

  render->current.add(new TObj(gettext("Year:"), "Vera/18", x, y, 
			       themes->audio_font1, themes->audio_font2, 
			       themes->audio_font3, 3));
  render->current.add(new TObj(conv::itos(dbaf.year), "Vera/18", 
			       x+first_column_width, y, 
			       themes->audio_font1, themes->audio_font2, 
			       themes->audio_font3, 3));
  y+=28;

  render->current.add(new TObj(gettext("Bitrate:"), "Vera/18", x, y, 
			       themes->audio_font1, themes->audio_font2, 
			       themes->audio_font3, 3));
  render->current.add(new TObj(conv::itos(dbaf.bitrate), "Vera/18", 
			       x+first_column_width, y,
			       themes->audio_font1, themes->audio_font2, 
			       themes->audio_font3, 3));
  y+=28;

  std::ostringstream time;

  if (sec(dbaf.length) < 10)
    time << min(dbaf.length) << ":0" << sec(dbaf.length);
  else
    time << min(dbaf.length) << ":" << sec(dbaf.length);
	  
  render->current.add(new TObj(gettext("Length:"), "Vera/18", x, y, 
			       themes->audio_font1, themes->audio_font2, 
			       themes->audio_font3, 3));
  render->current.add(new TObj(time.str(), "Vera/18", 
			       x+first_column_width, y,  
			       themes->audio_font1, themes->audio_font2, 
			       themes->audio_font3, 3));
  y+=28;

  std::ostringstream rating;
  if (dbaf.rating != -1)
    rating << dbaf.rating;
  else
    rating << gettext("not rated yet");

  render->current.add(new TObj(gettext("Rating:"), "Vera/18", x, y, 
			       themes->audio_font1, themes->audio_font2, 
			       themes->audio_font3, 3));
  render->current.add(new TObj(rating.str(), "Vera/18", x+first_column_width, y,  
			       themes->audio_font1, themes->audio_font2, 
			       themes->audio_font3, 3));
  y+=28;

  render->current.add(new TObj(gettext("Track:"), "Vera/18", x, y, 
			       themes->audio_font1, themes->audio_font2, 
			       themes->audio_font3, 3));
  render->current.add(new TObj(conv::itos(dbaf.track), "Vera/18", 
			       x+first_column_width, y,
			       themes->audio_font1, themes->audio_font2, 
			       themes->audio_font3, 3));
  y+=28;

  render->draw("Audio information");
  render->image_mut.leaveMutex();

  input_master->device->get_input();

  printing_information = false;
}
#endif

#ifdef use_imms
Dbaudiofile Audio::lookup_file_in_db(const string& file)
{
  imms_mut.enterMutex();

  Imms *imms = audio_state->imms;

  string filename = string_format::remove_doubles(file);
  
  Dbaudiofile empty;
  Dbaudiofile dbaf = imms->get_immsdb()->get_real_data(filename);

  if (empty == dbaf) {
    InfoFetcher::SongData temp(-1, filename);
    imms->fetch_song_info(temp);
    dbaf = imms->get_immsdb()->get_real_data(filename);
  }
	  
  imms_mut.leaveMutex();

  return dbaf;
}

bool Audio::file_exists_in_db(const string& file)
{
  imms_mut.enterMutex();
  bool ret = (audio_state->imms->get_immsdb()->file_exists(string_format::remove_doubles(file)) != -1);
  imms_mut.leaveMutex();
  return ret;
}

#endif

void Audio::action_load_playlist()
{
  if (load_playlist(vector_lookup(saved_playlists, saved_playlist_pos))) {
    audio_state->p->stop();
    returned = true;
    return_value = true;
  } else
    DialogWaitPrint pdialog(gettext("Playlist is corrupt"), 1000);
}

void Audio::delete_playlist()
{
  // delete the file from drive
  std::ostringstream del;
  del << "rm \"" + conf->p_var_data_dir() + "playlists/" << vector_lookup(saved_playlists, saved_playlist_pos) << "\"" << std::endl;
  run::my_system_no_kill(del.str().c_str());
	  
  vector<string>::iterator i = saved_playlists.begin() + saved_playlist_pos;
  saved_playlists.erase(i);

  // this is saved_playlists.size(), not -1, because we just deleted a track
  if (saved_playlist_pos == saved_playlists.size())
    --saved_playlist_pos;

  // if we removed the last element then bail
  if (saved_playlists.size() == 0) {
    returned = true;
    return_value = false;
  }
}

bool Audio::choose_playlist()
{
  input_master->cur_map = &input_master->playlist_load;

  Input input;
  
  saved_playlists = list_playlists();

  saved_playlist_pos = 0;

  returned = false;
  return_value = false;

  bool update_needed = true;

  BusyIndicator &busy_indicator = BusyIndicator::get_instance();

  while (!(input.command == "back" && input.mode == "general"))
    {
      if (update_needed)
	print_list(saved_playlists, vector_lookup(saved_playlists, saved_playlist_pos), saved_playlist_pos, true);
      
      busy_indicator.idle();

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

      busy_indicator.busy();

      update_needed = true;

      if (input.command == "prev")
 	{
 	  if (saved_playlist_pos != 0)
	    --saved_playlist_pos;
	  else 
	    saved_playlist_pos = saved_playlists.size()-1;
 	}
      else if (input.command == "next")
 	{
 	  if (saved_playlist_pos != saved_playlists.size()-1)
	    ++saved_playlist_pos;
	  else
	    saved_playlist_pos = 0;
 	}
      else if (input.command == "page_up")
 	{
	  if (saved_playlists.size() > conf->p_jump()) {
	    int new_pos = saved_playlist_pos-conf->p_jump();
	    if (saved_playlist_pos == 0)
	      saved_playlist_pos = saved_playlists.size()-1+new_pos;
	    else if (new_pos < 0)
	      saved_playlist_pos = 0;
	    else
	      saved_playlist_pos = new_pos;
	  }
 	}
      else if (input.command == "page_down")
 	{
	  if (saved_playlists.size() > conf->p_jump()) {
	    if (saved_playlist_pos > (saved_playlists.size() - conf->p_jump()) && saved_playlist_pos != (saved_playlists.size()-1))
	      saved_playlist_pos = saved_playlists.size()-1;
	    else 
	      saved_playlist_pos = (saved_playlist_pos+conf->p_jump())%saved_playlists.size();
	  }
 	}
      else if (input.command == "second_action")
	{
	  ExtraMenu em;

	  TimeFunctorv<Audio> t_sf = TimeFunctorv<Audio>(this, &Audio::action_load_playlist);
	  em.add_item(ExtraMenuItem(gettext("Load playlist"), input_master->find_shortcut("action"), &t_sf));
	  
	  TimeFunctorv<Audio> t_sf2 = TimeFunctorv<Audio>(this, &Audio::delete_playlist);
	  em.add_item(ExtraMenuItem(gettext("Delete playlist"), input_master->find_shortcut("delete"), &t_sf2));

	  TimeFunctorv<Audio> t_back = TimeFunctorv<Audio>(this, &Audio::exit);
	  em.add_item(ExtraMenuItem(gettext("Playlist"), 
				    input_master->find_shortcut("back"), 
				    &t_back));

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

	  if (returned)
	    return return_value;
	  else if (exit_loop) {
	    exit_loop = false;
	    return false;
	  }
	}
      else if (input.command == "delete" && input.mode == "choose_playlist")
	{
	  delete_playlist();

	  if (returned)
	    return return_value;
	}
      else if (input.command == "action")
	{
	  action_load_playlist();

	  if (returned)
	    return return_value;
	}
      else if (input.command == "playlist_mode" && input.mode == "audio_general")
	{
	  return false;
	}
      else
	update_needed = !general_loop_check(input);
    }
  return false;
}

void Audio::choose_radio()
{
  Startmenu &startmenu = Startmenu::get_instance();
  startmenu.module = "radio";

  // init it if it's not already done
  if (!conf->p_audio_started())
    audio_state = Audio_s::get_instance();

  input_master->cur_map = &input_master->playlist;

  Input input;

  vector<std::pair<string, string> > cur_files;
  vector<string> print_files;

  std::pair<string, string> my_radio_genre("My Radio Stations", "");
  print_files.push_back(my_radio_genre.first);
  cur_files.push_back(my_radio_genre);

  Shoutcast *shoutcast = Shoutcast::get_instance();

  if (conf->p_download_radio()) {
    if (!radio_stations_downloaded) {
      DialogWaitPrint pdialog(gettext("Downloading radio stations"), 2000);
      radio_stations_downloaded = true;
    }

    list<std::pair<string, string> > shoutcast_genrelist = shoutcast->get_genrelist();  

    for (list<std::pair<string, string> >::iterator i = shoutcast_genrelist.begin(),
	   end = shoutcast_genrelist.end(); i != end; ++i) {
      print_files.push_back(i->first);
      cur_files.push_back(*i);
    }
  }
  
  int pos = 0;
  int old_pos = 0;

  bool update_needed = true;
  bool exit_radio = false;
  bool in_genre = true;

  BusyIndicator &busy_indicator = BusyIndicator::get_instance();

  while (!(input.command == "back" && input.mode == "general" && exit_radio))
    {
      if (update_needed)
	print_list(print_files, vector_lookup(cur_files, pos).first, pos, false);

      busy_indicator.idle();

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

      busy_indicator.busy();

      update_needed = true;

      if (input.command == "prev")
 	{
 	  if (pos != 0)
	    --pos;
	  else
	    pos = cur_files.size()-1;
 	}
      else if (input.command == "next")
 	{
 	  if (pos != cur_files.size()-1)
	    ++pos;
	  else
	    pos = 0;
 	}
      else if (input.command == "page_up")
 	{
	  if (cur_files.size() > conf->p_jump()) {
	    int new_pos = pos-conf->p_jump();
	    if (pos == 0)
	      pos = cur_files.size()-1+new_pos;
	    else if (new_pos < 0)
	      pos = 0;
	    else
	      pos = new_pos;
	  }
 	}
      else if (input.command == "options")
	{
	  options();
	}
      else if (input.command == "page_down")
 	{
	  if (cur_files.size() > conf->p_jump()) {
	    if (pos > (cur_files.size() - conf->p_jump()) && pos != (cur_files.size()-1))
	      pos = cur_files.size()-1;
	    else
	      pos = (pos+conf->p_jump())%cur_files.size();
	  }
	}
      // play specific
      else if (input.command == "action")
	{
	  
	  if(pos == 0 && in_genre)
          {
             print_files.clear();
             cur_files.clear();

             list<std::pair<string, string> > radio_stations = conf->p_internet_radio();

	     int counter = 0;
             for (list<std::pair<string, string> >::iterator i = radio_stations.begin(),
                end = radio_stations.end(); i != end; ++i) {
		  i->first = itostr(++counter, 3) + ". " + i->first;
                  print_files.push_back(i->first);
                  cur_files.push_back(*i);
             }

             update_needed = true;
             in_genre = false;
             old_pos = pos;
             pos = 0;
          }
	  else if(in_genre)
          {
             list<std::pair<string, string> > shoutcast_toplist = 
				shoutcast->get_toplist(vector_lookup(cur_files, pos).second);

             print_files.clear();
             cur_files.clear();

             for (list<std::pair<string, string> >::iterator i = shoutcast_toplist.begin(),
                end = shoutcast_toplist.end(); i != end; ++i) {
                  print_files.push_back(i->first);
                  cur_files.push_back(*i);
             }

             update_needed = true;
             in_genre = false;
             old_pos = pos;
             pos = 0;
          }
	  else
          {
 	     if(vector_lookup(cur_files, pos).second.length() > 0)
	     {
	        Simplefile s;
	        std::pair<string, string> station;

                if(old_pos == 0)
                   station = vector_lookup(cur_files, pos);
                else
                   station = shoutcast->get_playlist(vector_lookup(cur_files, pos).second);

	        s.id = ++playlist_id;
	        s.name = station.first;
	        s.lowercase_name = string_format::lowercase(station.first);
	        s.path = station.second;
	        s.type = "";

                if (s.path.find("rtsp://") == 0 || s.path.find("rtp://") == 0) {
#ifdef player_alsaplayer
	          DialogWaitPrint pdialog(gettext("Alsaplayer does not support rtsp"), 2000);
#else
	          s.type = "web";
#endif
	        } else if (s.path.find("http://") == 0)
	            s.type = "web";
#ifdef music_radio_bttv
                else if (s.path.find("bttvradio://") == 0) {
	            s.type = "bttvradio";
	            if (s.path.find("bttvradio://") != string::npos)
	               s.path=s.path.substr(12);
	        }
#else
                else if (s.path.find("bttvradio://") == 0)
	           DialogWaitPrint pdialog(gettext("Please recompile mms with bttvradio enabled"), 2000);
#endif

	        if (!s.type.empty()) {
	           DialogWaitPrint pdialog(gettext("Added track to playlist"), 1000);

#ifdef use_imms
	           playlist.push_back(Dbaudiofile(s));
#else
	           playlist.push_back(s);
#endif
	           shuffle_list.add_track(s);
	        }

	        if (playlist.size() == 1)
	           // first element added so we need to set the pos
	           playlist_pos_int = 0;

	        if (!audio_state->p_playing()) {
	           // setup
	           AudioMode old_mode = mode;
	           mode = PLAY;
	           files = &playlist;
	           int old_playlist_pos = playlist_pos_int;
	           playlist_pos_int = playlist.size() - 1;

	           play_track();

	           // reset
	           playlist_pos_int = old_playlist_pos;
	           files = &audio_list;
	           mode = old_mode;
	        }
	      }
            }
          }
      else if(input.command == "back")
      {
         if(in_genre)
            exit_radio = true;
         else
         {
            print_files.clear();
            cur_files.clear();

            print_files.push_back(my_radio_genre.first);
            cur_files.push_back(my_radio_genre);
	    
	    if (conf->p_download_radio()) {
               list<std::pair<string, string> > shoutcast_genrelist = shoutcast->get_genrelist();

               for (list<std::pair<string, string> >::iterator i = shoutcast_genrelist.begin(),
                 end = shoutcast_genrelist.end(); i != end; ++i) {
                   print_files.push_back(i->first);
                   cur_files.push_back(*i);
               }
	    }

            pos = old_pos;
            update_needed = true;
            in_genre = true;
         }
       }
      else
	update_needed = !general_loop_check(input);
    }
}

#ifdef use_imms
string Audio::get_name_from_file(const Dbaudiofile& dbaf)
{
  string name;

  string title = string_format::convert(string_format::trim(dbaf.title), true);
  string artist = string_format::convert(string_format::trim(dbaf.artist), true);
  string album = string_format::convert(string_format::trim(dbaf.album), true);

  if (artist.empty() || title.empty())
    name = dbaf.name;
  else {

    if (!artist.empty())
      name += artist;
    if (!album.empty())
      name += " - " + album;
    if (!title.empty())
      name += " - " + title;
  }

  return name;
}

string Audio::get_name_from_file_in_picture_dir(const Dbaudiofile& dbaf)
{
  if (dbaf.title.empty())
    return dbaf.name;
  else
    return string_format::convert(string_format::trim(dbaf.title), true);
}
#else

string Audio::get_name_from_file(const Simplefile& s)
{
  return s.name;
}
#endif

#ifdef use_imms
void Audio::print_graphical_element(Dbaudiofile& dbaf, const Dbaudiofile& position, int y, bool picture_dir)
{
  string name;

  if (!dbaf.fetched) {
    Dbaudiofile tmp_dbaf = lookup_file_in_db(dbaf.path);
    tmp_dbaf.fetched = true;
    tmp_dbaf.set(dbaf);
    dbaf = tmp_dbaf;
  }

  if (mode == ADD && picture_dir)
    name = get_name_from_file_in_picture_dir(dbaf);
  else
    name = get_name_from_file(dbaf); 

  if (picture_dir && mode == ADD) {

    int x_start = 358;

    if (conf->p_v_res() == 1024)
      x_start = 418;

    if (conf->p_h_res() == 405)
      x_start = 250;

    string_format::format_to_size(name, "Vera/16", conf->p_v_res()-x_start-85, true);
    
    // shorter
    if (dbaf == position) {
      render->current.add(new PObj(themes->audio_marked_small_left, x_start, y-1, 2, NOSCALING));
      render->current.add(new PObj(themes->audio_marked_small, x_start+25, y-1, conf->p_v_res()-x_start-75 - 50, 2));
      render->current.add(new PObj(themes->audio_marked_small_right, x_start+25+conf->p_v_res()-x_start-75 - 50, y-1, 2, NOSCALING));
    }
    
    if (isDirectory(dbaf.path))
      name.append("/");

    render->current.add(new TObj(name, "Vera/16", x_start+7, y, 
				 themes->audio_font1, themes->audio_font2, 
				 themes->audio_font3, 3));

  } else {

    int width = conf->p_v_res()-250;
    if (mode == ADD)
      width += 15;

    string_format::format_to_size(name, "Vera/16", width, true);

    if (dbaf == position) {
      render->current.add(new PObj(themes->audio_marked_left, 70, y-1, 2, NOSCALING));
      render->current.add(new PObj(themes->audio_marked, 70+25, y-1, conf->p_v_res()-75-70-50, 2));
      render->current.add(new PObj(themes->audio_marked_right, 70+25+conf->p_v_res()-75-70-50, y-1, 2, NOSCALING));
    }
    
    bool printed = false;
    if (mode == PLAYLIST)
      if (dbaf.id == audio_state->p->p_cur_nr().id && audio_state->p_playing()) {
	render->current.add(new TObj(name, "Vera/16", 75, y, 
				     themes->audio_marked_font1, themes->audio_marked_font2, 
				     themes->audio_marked_font3, 3));
	printed = true;
      }
    
    if (!printed) {
    
      if (isDirectory(dbaf.path))
	name.append("/");

      render->current.add(new TObj(name, "Vera/16", 75, y, 
				   themes->audio_font1, themes->audio_font2, 
				   themes->audio_font3, 3));
    }

    if (dbaf.length != 0) {
      char temp[512];
      snprintf(temp, 512, "%d:%02d", dbaf.length/60, dbaf.length%60);
      int x_size, y_size;
      string_format::calculate_string_size(temp, "Vera/16", &x_size, &y_size);
      render->current.add(new TObj(temp, "Vera/16", conf->p_v_res()-75-x_size, y, 
				   themes->audio_font1, themes->audio_font2, 
				   themes->audio_font3, 3));
    }

    if (mode == PLAYLIST) {
      // FIXME: inefficient
      int qpos = audio_state->queue_pos(dbaf);

      if (qpos != 0)
	render->current.add(new TObj(conv::itos(qpos), "Vera/16", conf->p_v_res()-165, y, 
				     themes->audio_font1, themes->audio_font2, 
				     themes->audio_font3, 3));
    }
  }
}
#else
void Audio::print_normal_element(const Simplefile& r, const Simplefile& position, int y)
{
  string name = r.name;
  string_format::format_to_size(name, "Vera/16", conf->p_v_res()-(75+85+10), true);

  if (r == position) {
    render->current.add(new PObj(themes->audio_marked_left, 67, y-1, 2, NOSCALING));
    render->current.add(new PObj(themes->audio_marked, 67+25, y-1, conf->p_v_res()-135-50, 2));
    render->current.add(new PObj(themes->audio_marked_right, 67+25+conf->p_v_res()-135-50, y-1, 2, NOSCALING));
  }
  
  if (mode == PLAY && r == audio_state->p->p_cur_nr() && audio_state->p_playing()) 
    render->current.add(new TObj(name, "Vera/16", 75, y, 
				 themes->audio_marked_font1, themes->audio_marked_font2, 
				 themes->audio_marked_font3, 3));
  else {
    
    if (isDirectory(r.path))
      name.append("/");

    render->current.add(new TObj(name, "Vera/16", 75, y, 
				 themes->audio_font1, themes->audio_font2, 
				 themes->audio_font3, 3));
  }


  // FIXME: inefficient
  int qpos = audio_state->queue_pos(r);

  if (qpos != 0)
    render->current.add(new TObj(conv::itos(qpos), "Vera/16", conf->p_v_res()-85, y, 
				 themes->audio_font1, themes->audio_font2, 
				 themes->audio_font3, 3));
}
#endif

void Audio::print_string_element(const string& r, const string& position, int y)
{
  string name = r;
  string_format::format_to_size(name, "Vera/16", conf->p_v_res()-160, true);

  if (r == position) {
    int width = conf->p_v_res()-(2*67);
    if (conf->p_v_res() == 720)
      width -= 5;

    render->current.add(new PObj(themes->audio_marked_left, 67, y-2, 2, NOSCALING));
    render->current.add(new PObj(themes->audio_marked, 67+25, y-2, width-50, 2));
    render->current.add(new PObj(themes->audio_marked_right, 67+25+width-50, y-2, 2, NOSCALING));
  }

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

  render->current.add(new TObj(name, "Vera/16", 75, y, 
			       themes->audio_font1, themes->audio_font2, 
			       themes->audio_font3, 3));
}

bool Audio::search_compare(const audio_type& t)
{
  // optimization
  if (search_str.empty())
    return true;

  string name = get_name_from_file(t);

  return (string_format::lowercase(name.substr(0, search_str.size())) == lowercase_search_str);
}

void Audio::print_top()
{
  render->prepare_new_image();

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

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

  string header = gettext("Audio - Add files");
  if (mode == PLAY)
    header = gettext("Audio - Playlist");

  if (themes->show_header) {
    render->current.add(new TObj(header, "Vera/28", 100, 10, 
				 themes->audio_header_font1, themes->audio_header_font2, 
				 themes->audio_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->audio_font1, themes->audio_font2, 
				 themes->audio_font3, 3));
  }
}

void Audio::print_buttom(const vector<audio_type>& cur_files)
{
  std::ostringstream pos;

  if (search_mode)
    if (cur_files.size() > 0)
      pos << offset + 1 << "/" << cur_files.size();
    else
      pos << "";
  else
    pos << position_int()+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->audio_font1, themes->audio_font2, 
			       themes->audio_font3, 3));

  int opts_pos=46;

  // check if repeat is on then display
  if (opts.repeat()== gettext("yes") && opts.shutdown()== gettext("no")) {
    render->current.add(new PObj(themes->audio_repeat, conf->p_v_res()-opts_pos ,conf->p_h_res()-52, 3, NOSCALING));
    opts_pos = opts_pos + 50; 
  }
  
  // check if shutdown is on then display
  if (opts.shutdown()== gettext("yes")) {
    render->current.add(new PObj(themes->audio_shutdown, conf->p_v_res()-opts_pos ,conf->p_h_res()-52, 3, NOSCALING));
    opts_pos = opts_pos + 50; 
  }
  
  // display shuffle mode
  if (opts.shuffle()== gettext("real random")) {
    render->current.add(new PObj(themes->audio_random, conf->p_v_res()-opts_pos, conf->p_h_res()-52, 3, NOSCALING));
    opts_pos = opts_pos + 50;
  }
  else if (opts.shuffle()== gettext("intelligent")) {
    render->current.add(new PObj(themes->audio_randomi, conf->p_v_res()-opts_pos, conf->p_h_res()-52, 3, NOSCALING));
    opts_pos = opts_pos + 50; 
  }
  else if (opts.shuffle()== gettext("winamp random")) {
    render->current.add(new PObj(themes->audio_randomw,  conf->p_v_res()-opts_pos, conf->p_h_res()-52, 3, NOSCALING)); 
    opts_pos = opts_pos + 50;
  }

  render->draw("Audio");
  render->image_mut.leaveMutex();
}

#ifdef use_imms
void Audio::print(vector<Dbaudiofile>& cur_files)
{
  if (mode == PLAYLIST or no_folders_in_dir(cur_files))
    print_tracklist(cur_files);
  else
    print_graphical(cur_files);
}

bool Audio::no_folders_in_dir(const vector<Dbaudiofile>& cur_files)
{
  for (vector<Dbaudiofile>::const_iterator i = cur_files.begin(), iend = cur_files.end(); i != iend; ++i)
    if (i->type == "dir")
      return false;

  return true;
}

void Audio::print_dir(int pos, int j, int x, int y)
{
  PObj *p;
      
  if (pos == j)
    p = new PObj(themes->audio_dir_marked, x, y, 3, NOSCALING);
  else
    p = new PObj(themes->audio_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);
}

void Audio::print_graphical(const vector<Dbaudiofile>& cur_files)
{
  render->prepare_new_image();

  render->current.add(new PObj(themes->audio_background, 0, 0, 0, SCALE_FULL));
  
  render->current.add(new PObj(themes->startmenu_music_dir, 25, 10, 2, NOSCALING));
  
  if (themes->show_header) {
    render->current.add(new TObj(gettext("Audio - Add files"), "Vera/28", 100, 10, 
				 themes->audio_header_font1, themes->audio_header_font2, 
				 themes->audio_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->audio_font1, themes->audio_font2, 
				 themes->audio_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) {

    Dbaudiofile s = vector_lookup(cur_files, start);

    if (s.type == "dir") {

      string pic = get_cover_from_dir(s.path);

      if (file_exists(pic)) {

	bool border = false;
	
	if (pos == j)
	  border = true;

	if (!file_exists(render->image_get_fullpath(pic, image_width, image_height))) {
	  render->create_scaled_image_wrapper(pic, 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(pic, image_width, image_height), &tmp);
	}

	PFObj *p = new PFObj(pic, 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
	print_dir(pos, j, x, y);

    } else {

      PObj *p = new PObj(themes->audio_file, 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);
      
      if (!s.fetched) {
	Dbaudiofile tmp_dbaf = lookup_file_in_db(s.path);
	tmp_dbaf.fetched = true;
	tmp_dbaf.set(s);
	vector_lookup(cur_files, start) = tmp_dbaf;
	s = tmp_dbaf;
      }
    }

    string name = get_name_from_file(s); 

    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->audio_marked_font1, themes->audio_marked_font2, 
				   themes->audio_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->audio_marked_font1, themes->audio_marked_font2, 
				   themes->audio_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->audio_font1, themes->audio_font2, 
				   themes->audio_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->audio_font1, themes->audio_font2, 
				   themes->audio_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->audio_font1, themes->audio_font2, 
			       themes->audio_font3, 3));

  render->draw("audio-graphical");
  render->image_mut.leaveMutex();
}

void Audio::print_tracklist(vector<Dbaudiofile>& cur_files)
{
  print_top();

  string pic = "";
  if (position_int() < cur_files.size())
    pic = get_cover_from_filename(vector_lookup(cur_files, position_int()));

  if (cur_files.size() > 0) {
    TPRGAudioFunctor<Audio> print_func(this, &Audio::print_graphical_element, !pic.empty());
    if (search_mode && cur_files.size() > 0)
      print_range_modifying(cur_files, vector_lookup(cur_files, offset), 
			    offset, &print_func);
    else
      print_range_modifying(cur_files, vector_lookup(cur_files, position_int()), position_int(), 
			    &print_func);

    if (mode == ADD) {

      if (!pic.empty()) {

	if (cur_files.size() > 0) {

	  int pos = position_int();

	  if (search_mode && cur_files.size() > 0)
	    pos = offset;

	  Dbaudiofile dbaf = vector_lookup(cur_files, pos);

	  int max_x_pic = 240, max_y_pic = 266;
	  
	  if (conf->p_v_res() == 1024) {
	    max_x_pic = 300;
	    max_y_pic = 333;
	  } else if (conf->p_h_res() == 405) {
	    max_x_pic = 150;
	    max_y_pic = 167;
	  }

	  string artist = string_format::convert(string_format::trim(dbaf.artist), true);
	  string album = string_format::convert(string_format::trim(dbaf.album), true);

	  string_format::format_to_size(artist, "Vera/20", max_x_pic, false);
	  string_format::format_to_size(album, "Vera/18", max_x_pic, false);

	  int y_artist = 100;
	  int y_album = 135;
	  int y_image = 175;

	  if (search_mode) {
	    y_artist += 35;
	    y_album += 35;
	    y_image += 35;
	  }

	  render->current.add(new TObj(artist, "Vera/20", 75, y_artist, 
				       themes->audio_font1, themes->audio_font2, 
				       themes->audio_font3, 3));

	  render->current.add(new TObj(album, "Vera/18", 75, y_album, 
				       themes->audio_font1, themes->audio_font2, 
				       themes->audio_font3, 3));

	  // scale it
	  render->create_scaled_image_wrapper(pic, max_x_pic, max_y_pic);
	  render->current.add(new PFObj(pic, 75, y_image, max_x_pic, max_y_pic, true, 3));
	}
      }
    }
  }

  print_buttom(cur_files);
}
#else
void Audio::print(const vector<Simplefile>& cur_files)
{
  print_top();

  if (cur_files.size() > 0) {
    TPRAudioFunctor<Audio> print_func(this, &Audio::print_normal_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, position_int()), 
		  position_int(), &print_func);
  }

  print_buttom(cur_files);
}
#endif

void Audio::print_list(const vector<string>& cur_files, const string& position, const int int_position, bool is_playlist)
{
  render->prepare_new_image();

  render->current.add(new PObj(themes->audio_background, 0, 0, 0, SCALE_FULL));
  
  if (is_playlist) {
    render->current.add(new PObj(themes->audio_playlist_icon, 25, 10, 2, NOSCALING));
    render->current.add(new TObj(gettext("Audio - Choose Playlist"), "Vera/28", 100, 10, 
				 themes->audio_header_font1, themes->audio_header_font2, 
				 themes->audio_header_font3, 2));
  } else {
    render->current.add(new PObj(themes->startmenu_radio, 25, 10, 2, NOSCALING));
    render->current.add(new TObj(gettext("Audio - Radio"), "Vera/28", 100, 10, 
				 themes->audio_header_font1, themes->audio_header_font2, 
				 themes->audio_header_font3, 2));
  }

  TPRSAudioFunctor<Audio> print_func(this, &Audio::print_string_element);
  print_range(cur_files, position, int_position, &print_func);

  if (is_playlist)
    render->draw("Audio choose playlist");
  else
    render->draw("Audio radio");

  render->image_mut.leaveMutex();
}

#ifdef use_media
void Audio::detect_cdformat() 
{
  int cdrom = cd->cdrom;

  switch(ioctl(cdrom, CDROM_DISC_STATUS)) {
    
  case CDS_NO_INFO:
  case CDS_NO_DISC:
  case CDS_TRAY_OPEN:
  case CDS_DRIVE_NOT_READY:
  case CDS_DISC_OK:
    {
      Print pdialog(gettext("The disc does not contain any recognizable files"), 
		    Print::SCREEN);
    }
    break;
  case CDS_AUDIO:
    cd_menu();
    break;
  case CDS_DATA_1:
  case CDS_DATA_2:
  case CDS_XA_2_1:
  case CDS_XA_2_2:
    add_data_cd();
    break;
  default:
    {
      Print pdialog(gettext("The disc does not contain any recognizable files"), 
		    Print::SCREEN);
    }
    break;
  }
}

void Audio::cd_menu()
{
  input_master->cur_map = &input_master->standard;

  int tracksize = cd->get_nr_tracks_cd();

  if (tracksize == -1) {
    Print pdialog(gettext("The disc does not contain any recognizable files"), 
		  Print::SCREEN);
    return;
  }

  ExtraMenu em("Audio CD playback");

  TimeFunctorv<Audio> t_sf = TimeFunctorv<Audio>(this, &Audio::add_audio_cd);
  em.add_item(ExtraMenuItem(gettext("Add CD to playlist and start playback"), "", &t_sf));

  TimeFunctorv<Audio> t_sf2 = TimeFunctorv<Audio>(this, &Audio::examine_audio_cd);
  em.add_item(ExtraMenuItem(gettext("Examine CD"), "", &t_sf2));

  em.mainloop();
}

vector<audio_type> Audio::get_audio_cd_list()
{
  int tracksize = cd->get_nr_tracks_cd();

  audio_state->p->update_cdrom(cd->get_device());

  // add elements to the playlist

  vector<audio_type> audiolist_new;

  Simplefile r;

  Overlay overlay = Overlay("dialog");

  DialogStepPrint pdialog(gettext("Looking up CD in database"), overlay);

  for (int i = 1; i < tracksize+1; ++i) {
    r.id = ++id;

    std::ostringstream track;
      
#ifdef player_xine
    track << "cdda:/" << i;
#endif
#ifdef player_gstreamer
    track << "cdda://" << i;
#endif
#ifdef player_alsaplayer
    if (i < 10)
      track << "Track 0" << i << ".cdda";
    else
      track << "Track " << i << ".cdda";
#endif

    audio_state->p->collect_info(track.str());

    if (audio_state->p->p_artist() == "" && audio_state->p->p_album() == "" && audio_state->p->p_title() == "") {
      r.name = "Track " + conv::itos(i);
    } else
      r.name = audio_state->p->p_artist() + " - " + audio_state->p->p_album() + " - " + audio_state->p->p_title();

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

    r.path = track.str(); 
    r.type="media-track"; 
      
#ifdef use_imms
    audiolist_new.push_back(Dbaudiofile(r));
#else
    audiolist_new.push_back(r);
#endif
  }

  pdialog.print_step2();

  return audiolist_new;
}

void Audio::add_audio_cd()
{
  vector<audio_type> audiolist_new = get_audio_cd_list();

  if (audiolist_new.size() == 0) {
    Print pdialogs(gettext("The disc does not contain any recognizable files"), 
		   Print::SCREEN);
  } else {

    int playlist_pos = playlist.size();

    for (vector<audio_type>::const_iterator iter = audiolist_new.begin(); 
	 iter != audiolist_new.end(); ++iter) {
      audio_type p = *iter;
      p.id = ++playlist_id;
      playlist.push_back(p);
      shuffle_list.add_track(p);
    }

    playlist_pos_int = playlist_pos;

    // play the first track
    mode = PLAY;
    files = &playlist;
    play_track();

    mainloop(PLAY);
  }
}

void Audio::examine_audio_cd()
{
  vector<audio_type> old_audio_list = audio_list;
  std::stack<std::pair<list<string>, int > > old_folders = folders;

  vector<audio_type> audiolist_new = get_audio_cd_list();

  if (audiolist_new.size() == 0) {
    Print pdialogs(gettext("The disc does not contain any recognizable files"), 
		   Print::SCREEN);
  } else {

    audio_list.clear();
    while (folders.size() > 0)
      folders.pop();

    for (vector<audio_type>::const_iterator iter = audiolist_new.begin(); 
	 iter != audiolist_new.end(); ++iter)
      audio_list.push_back(*iter);
    
    list<string> dirs;
    dirs.push_back(cd->get_mount_point());
    folders.push(std::make_pair(dirs, 0)); 
     
    mainloop(ADD);
 
    // restore old setting
    audio_list = old_audio_list;
    folders = old_folders;
  }
}

void Audio::add_data_cd()
{
  vector<audio_type> old_audio_list = audio_list;
  std::stack<std::pair<list<string>, int > > old_folders = folders;

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

  vector<audio_type> audiolist_new = add_dir(cd->get_mount_point(), true);

  if (audiolist_new.size() == 0) {
    Print pdialog(gettext("The disc does not contain any recognizable files"), 
		  Print::SCREEN);

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

  } else {

    audio_list.clear();
    while (folders.size() > 0)
      folders.pop();

    audio_list = audiolist_new;

    list<string> dirs;
    dirs.push_back(cd->get_mount_point());
    folders.push(std::make_pair(dirs, 0)); 
 
    //umount the disc
    run::my_system_no_kill(("umount " + cd->get_mount_point()).c_str());

    mainloop(ADD);

    // restore old setting
    audio_list = old_audio_list;
    folders = old_folders;
  }
}
#endif

#ifdef use_imms
std::string Audio::get_first_cover_in_dir(const string& dir)
{
  string cover = "";
  bool cover_found = false;

  string cur_dir = dir;

  std::list<string> dirs_to_try;

  while (!cover_found) {
    cover = get_cover_from_dir(cur_dir);

    if (!cover.empty()) {
      cover_found = true;
    }
    else {
      bool new_dir_found = false;
      for (file_iterator<file_t, default_order> i (cur_dir); i != i.end (); i.advance(false)) {
	if (isDirectory(i->getName())) {
	  if (!new_dir_found) {
	    new_dir_found = true;
	    cur_dir = i->getName();
	  } else {
	    dirs_to_try.push_back(i->getName());
	  }
	}
      }
      if (!new_dir_found) {
	if (!dirs_to_try.empty()) {
	  cur_dir = dirs_to_try.front();
	  dirs_to_try.pop_front();
	} else
	  break;
      }
    }
  }

  return cover;
}

std::string Audio::get_first_file_in_dir(const string& dir)
{
  for (file_iterator<file_t, default_order> i (dir); i != i.end (); ++i)
    {
      string filename = filesystem::FExpand(i->getName());

      const MyPair filetype = check_type(filename, conf->p_filetypes_a());

      if (filetype != emptyMyPair)
	return filename;
    }

  return "";
}

vector<Dbaudiofile> Audio::add_dir(const string& dir, bool from_media)
{
  int nr_files = 0;
  for (file_iterator<file_t, default_order> i (dir); i != i.end (); ++i)
    if (!isDirectory(i->getName()) && check_type(i->getName(), conf->p_filetypes_a()) != emptyMyPair)
      ++nr_files;

  Updater &updater = Updater::get_instance();

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

  updater.status_progressbar = 0;
  updater.total_progressbar = nr_files;
  updater.header = gettext("Extracting metadata");

  TimeFunctoriNC cp(&check_progressbar);
  TimeFunctorvNC pg(&print_progressbar);

  updater.timer.add(TimeElement("progressbar", &cp, &pg));

  vector<Dbaudiofile> cur_files;

  string cur_directory = "";
  vector<Dbaudiofile> files_in_dir;

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

      const MyPair filetype = check_type(filename, conf->p_filetypes_a());

      if (filetype != emptyMyPair) {
	if (cur_directory != i.currentDirectory()) {
	  cur_directory = i.currentDirectory();
	  add_tracks_from_dir(files_in_dir, cur_files);
	}
	
	Dbaudiofile d(addsimplefile(filename, filetype, from_media));
	files_in_dir.push_back(d);

	++updater.status_progressbar;
      }
    }

  // add tracks from the last directory
  add_tracks_from_dir(files_in_dir, cur_files);

  updater.timer.del("progressbar");

  cleanup_progressbar();

  // reenable screensaver
  updater.timer.activate("audio_fullscreen");

  return cur_files;
}

void Audio::add_tracks_from_dir(vector<Dbaudiofile> &files_in_dir, vector<Dbaudiofile> &cur_files)
{
  // check if all the files are missing the track tag
  bool missing_track = true;
  for (vector<Dbaudiofile>::iterator fid = files_in_dir.begin(), 
	 fid_end = files_in_dir.end(); fid != fid_end; ++fid)
    if (fid->track != 0) {
      missing_track = false;
      break;
    }

  if (missing_track) {

    // just add all the tracks, we don't care about the order since its
    // probably ok already

    for (vector<Dbaudiofile>::iterator fid = files_in_dir.begin(), 
	   fid_end = files_in_dir.end(); fid != fid_end; ++fid)
      cur_files.push_back(*fid);
    
    files_in_dir.clear();

  } else {

    while (!files_in_dir.empty()) {
      // find the track with the smallest id
      int smallest_track = 1000000;
      vector<Dbaudiofile>::iterator element;

      for (vector<Dbaudiofile>::iterator fid = files_in_dir.begin(), 
	     fid_end = files_in_dir.end(); fid != fid_end; ++fid)
	if (fid->track < smallest_track) {
	  smallest_track = fid->track;
	  element = fid;
	}
      assert(smallest_track != 1000000);
      cur_files.push_back(*element);
      files_in_dir.erase(element);
    }
  }
}

#else

vector<Simplefile> Audio::add_dir(const string& dir, bool from_media)
{
  vector<Simplefile> cur_files;

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

      const MyPair filetype = check_type(filename, conf->p_filetypes_a());

      if (filetype != emptyMyPair)
        cur_files.push_back(addsimplefile(filename, filetype, from_media));
    }

  return cur_files;
}
#endif

#ifdef use_imms
vector<Dbaudiofile> Audio::rdir(const string& argv)
{
  vector<Dbaudiofile> 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
	  Simplefile r;
	  r.id = ++id;
	  r.name = filename.substr(argv[argv.size()-1] == '/' ? argv.size() : argv.size() + 1);
	  if (conf->p_convert())
	    r.name = string_format::convert(r.name);
	  r.lowercase_name = string_format::lowercase(r.name);
	  r.path = filename;
	  r.type = "dir";
	  Dbaudiofile d(r);
	  cur_files.push_back(d);
	} else {
	  // file
	  const MyPair filetype = check_type(filename, conf->p_filetypes_a());
	  if (filetype != emptyMyPair) {
	    Dbaudiofile d(addsimplefile(filename, filetype));
	    cur_files.push_back(d);

	    Updater &updater = Updater::get_instance();
	    ++updater.status_progressbar;
	  }
	}
    }

  vector<Dbaudiofile> sorted_by_id_files;

  add_tracks_from_dir(cur_files, sorted_by_id_files);

  return sorted_by_id_files;
}

#else

vector<Simplefile> Audio::rdir(const string& argv)
{
  vector<Simplefile> 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
	  Simplefile r;
	  r.id = ++id;
	  r.name = filename.substr(argv[argv.size()-1] == '/' ? argv.size() : argv.size() + 1);
	  if (conf->p_convert())
	    r.name = string_format::convert(r.name);
	  r.lowercase_name = string_format::lowercase(r.name);
	  r.path = filename;
	  r.type = "dir";
	  cur_files.push_back(r);
	} else {
	  // file
	  const MyPair filetype = check_type(filename, conf->p_filetypes_a());
	  if (filetype != emptyMyPair)
	    cur_files.push_back(addsimplefile(filename, filetype));
	}
    }
  return cur_files;
}
#endif

vector<audio_type> Audio::parse_dir(const list<string>& dirs)
{
  vector<audio_type> cur_files;

  for (list<string>::const_iterator iter = dirs.begin(); 
       iter != dirs.end(); ++iter)
    {
      // Add files
      assert(iter->size() > 0);
      vector<audio_type> tempfiles = rdir(*iter);
      cur_files.insert(cur_files.end(), tempfiles.begin(), tempfiles.end());
    }

  return cur_files;
}

void Audio::save_playlist(const string& playlistfile, bool overwrite)
{
  vector<audio_type> cur_files = playlist;

  if (playlistfile == "") {
    DialogWaitPrint pdialog(3000);
    pdialog.add_line(gettext("Can not save playlist with an empty name"));
    pdialog.add_line("");
    pdialog.add_line(gettext("Please use another name"));
    pdialog.print();
    return;
  }
  
  // doesn't make any sense to save nothing
  if (cur_files.size() != 0 || playlistfile == "last") {

    if (!overwrite && playlistfile != "last" && file_exists(conf->p_var_data_dir() + "playlists/" + playlistfile)) {
      DialogWaitPrint pdialog(3000);
      pdialog.add_line(gettext("Playlist with the chosen name already exists"));
      pdialog.add_line("");
      pdialog.add_line(gettext("Please use another name"));
      pdialog.print();
      return;
    }
    
    std::ofstream file;

    string plist = conf->p_var_data_dir() + "playlists/" + playlistfile; 
    
    file.open(plist.c_str());
    
    if (!file) {
      DebugPrint perror(gettext("Could not write playlist to file: ") + plist, 
			Print::DEBUGGING, DebugPrint::CRITICAL, "Audio");
    } else {
      
      for (vector<audio_type>::const_iterator iter = cur_files.begin(); iter != cur_files.end();
	   ++iter) {

	if (iter->type == "web" || iter->type == "bttv-radio")
	  file << iter->type << "," << iter->path << ";" << iter->name << std::endl;
	else
	  file << iter->type << "," << iter->path << std::endl;
	
      }
    }
    
    file.close();
  }
}

bool Audio::load_last_playlist()
{
  std::cout << "loading last saved playlist" << std::endl;
  bool status = load_playlist("last");

  std::pair<int, bool> loaded_values = audio_state->p->load_saved_values();

  playlist_pos_int = loaded_values.first;
  play_now_warning = loaded_values.second;

  // sanity check
  if (playlist_pos_int < files->size())
    playlist_pos_int = 0;

  return status;
}

bool Audio::load_playlist(const string& playlistfile)
{
  vector<audio_type> cur_files;

  std::ostringstream playlist_path; 
  playlist_path << conf->p_var_data_dir() + "playlists/" << playlistfile;

  std::ifstream In(playlist_path.str().c_str());

  string str, type = "", filename, name;

  bool media_mounted = false;
  bool cdrom_set = false;

  bool media_checked = false;
  bool media_ok = false;

  if(!In){
    DebugPrint perror(gettext("Could not open playlist: ") + 
		      playlist_path.str(), 
		      Print::DEBUGGING, DebugPrint::CRITICAL, "Audio");
  } else {
    while (getline(In, str)) {

      int pos = str.find(",");

      if (pos != string::npos) {
	type = str.substr(0, pos);

	if (type == "web" || type == "bttv-radio") {
	  int pos_semi = str.rfind(";");
	  if (pos_semi == string::npos) {
	    DebugPrint perror(gettext("Playlist malformed, skipping line"), 
			      Print::DEBUGGING, DebugPrint::CRITICAL, "Audio");
	    continue;
	  } else {
	    filename = str.substr(pos+1, pos_semi-(pos+1));
	    name = str.substr(pos_semi+1);
	  }
	} else
	  filename = str.substr(pos+1);
      } else
	continue;
      
      if (type == "media-track") {

#ifdef use_media
	Simplefile r;
	r.id = ++playlist_id;

	// init it if it's not already done
	if (!conf->p_audio_started())
	  audio_state = Audio_s::get_instance();

	if (!cdrom_set) {
	  audio_state->p->update_cdrom(cd->get_device());
	  cdrom_set = true;
	}

	if (!media_checked) {
	  media_checked = true;
	  bool opened = false;

	  if (cd->cdrom == -1) {
	    if (!cd->open()) {
	      media_ok = false;
	      continue;
	    }
	    opened = true;
	  }

	  Cd::cdstatus cdstatus = cd->check_cddrive();
	  if (cdstatus == Cd::OK)
	    media_ok = true;
	  else
	    media_ok = false;

	  if (opened)
	    cd->close();
	}

	if (media_ok) {

	  audio_state->p->collect_info(filename);

	  if (audio_state->p->p_artist().empty() && audio_state->p->p_album().empty() && 
	      audio_state->p->p_title().empty()) {

	    // determine if alsaplayer, xine or gstreamer track
	    if (filename.find("Track") != string::npos)
	      r.name = filename.substr(0, 8);
	    else if (filename.find("cdda://") != string::npos) // gstreamer
	      r.name = "Track " + filename.substr(7);
	    else
	      r.name = "Track " + filename.substr(6);
	    
	  } else
	    r.name = audio_state->p->p_artist() + " - " + audio_state->p->p_album() + " - " + audio_state->p->p_title();

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

	  r.path = filename;
	  r.type="media-track"; 

#ifdef use_imms
	  cur_files.push_back(Dbaudiofile(r));
#else
	  cur_files.push_back(r);
#endif
	}
#endif
	
      } else if (type == "media-file") {

#ifdef use_media
	if (!media_checked) {
	  media_checked = true;
	  bool opened = false;

	  if (cd->cdrom == -1) {
	    if (!cd->open()) {
	      media_ok = false;
	      continue;
	    }
	    opened = true;
	  }

	  Cd::cdstatus cdstatus = cd->check_cddrive();
	  if (cdstatus == Cd::OK)
	    media_ok = true;
	  else
	    media_ok = false;

	  if (opened)
	    cd->close();
	}

	if (media_ok) {
	  // mount the drive if it's not mounted yet
	  if (!media_mounted) {
	    run::my_system_no_kill(("mount " + cd->get_mount_point()).c_str());
	    media_mounted = true;
	  }

	  if (file_exists(filename)) {
	    Simplefile e = addsimplefile(filename, check_type(filename, conf->p_filetypes_a()), true);
	    e.id = ++playlist_id;
#ifdef use_imms
	    cur_files.push_back(Dbaudiofile(e));
#else
	    cur_files.push_back(e);
#endif
	  }
	}

#endif

      }	else if (type == "web" || type == "bttv-radio") {

#ifdef player_alsaplayer
	if (filename.find("rtsp://") == 0 || filename.find("rtp://") == 0)
	  continue;
#endif

	Simplefile s;
	s.id = ++playlist_id;
	s.name = name;
	s.lowercase_name = string_format::lowercase(name);
	s.path = filename;
	s.type = type;

#ifdef use_imms
	Dbaudiofile d(s);
	cur_files.push_back(d);
#else
	cur_files.push_back(s);
#endif
	
      } else {
	if (file_exists(filename)) {
	  Simplefile e = addsimplefile(filename, check_type(filename, conf->p_filetypes_a()));
	  e.id = ++playlist_id;
#ifdef use_imms
	  cur_files.push_back(Dbaudiofile(e));
#else
	  cur_files.push_back(e);
#endif
	}
      }
    }
    
#ifdef use_media
    if (media_mounted)
      run::my_system_no_kill(("umount " + cd->get_mount_point()).c_str());
#endif

    if (cur_files.size() != 0) {
      shuffle_list.clear();
      for (vector<audio_type>::const_iterator i = cur_files.begin(), end = cur_files.end();
	   i != end; ++i)
	shuffle_list.add_track(*i);
      audio_state->remove_queued_tracks();
      playlist.clear();
      playlist = cur_files;
      playlist_pos_int = 0;
      return true;
    } else {
      return false;
    }
  }
  return false;
}

vector<string> Audio::list_playlists() 
{
  vector<string> cur_files;

  string argv = conf->p_var_data_dir() + "playlists/";
  
  for (file_iterator<file_t, default_order> i (argv); (i != i.end ()); i.advance(false))
    {
      string filename = i->getName();
      cur_files.push_back(filename.substr(argv.size()+1));
    }

  return cur_files;
}

void Audio::read_dirs() 
{
#ifdef use_imms
  while (!all_metadata_files.empty())
    all_metadata_files.pop();
#endif
  vector<audio_type> playlist_new;
  
  if (conf->p_audio_mode() == BOTH) {

#ifdef use_imms
    int nr_files = 0;
    list<string> dirs = conf->p_audio_dirs();
    for (list<string>::const_iterator iter = dirs.begin(); iter != dirs.end(); ++iter)
      for (file_iterator<file_t, default_order> i (*iter); i != i.end (); i.advance(false))
	if (!isDirectory (i->getName()) && check_type(i->getName(), conf->p_filetypes_a()) != emptyMyPair)
	  ++nr_files;

    Updater &updater = Updater::get_instance();

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

    updater.status_progressbar = 0;
    updater.total_progressbar = nr_files;
    updater.header = gettext("Extracting metadata");

    TimeFunctoriNC cp(&check_progressbar);
    TimeFunctorvNC pg(&print_progressbar);

    updater.timer.add(TimeElement("progressbar", &cp, &pg));
#endif

    // process audio dir 
    audio_list = parse_dir(conf->p_audio_dirs());

#ifdef use_imms
    updater.timer.del("progressbar");
    cleanup_progressbar();

    // reenable screensaver
    updater.timer.activate("audio_fullscreen");
#endif

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

    if (audio_list.size() != 0)
      // set position to the first file
      folders.top().second = 0;
  }
  // winamp mode (NOT WINAMP RANDOM MODE)
  else if (conf->p_audio_mode() == PLAYLIST) {

    list<string> dirs = conf->p_audio_dirs();
    for (list<string>::const_iterator iter = dirs.begin(); iter != dirs.end(); ++iter)
      {
	vector<audio_type> tempplaylist = add_dir(*iter);
	playlist_new.insert(playlist_new.end(), tempplaylist.begin(), tempplaylist.end());
      }
    
    playlist = playlist_new;
    if (playlist_new.size() != 0)
      playlist_pos_int = 0;

    shuffle_list.clear();
    for (vector<audio_type>::const_iterator i = playlist.begin(), end = playlist.end();
	 i != end; ++i)
      shuffle_list.add_track(*i);
  }

  if (mode == ADD)
    files = &audio_list;
  else
    files = &playlist;
}

void Audio::print_audiotrack()
{
  if (conf->p_audio_started()) {

    if (audio_state->p_playing() && audio_state->p->is_playing() 
	&& !audio_state->fullscreen_info && !audio_state->p->is_buffering()) {

      render->image_mut.enterMutex();

      render->device->wait_for_output_done();

      audio_state->has_played = true;

      if (audio_state->playback.elements.size() > 0)
	audio_state->playback.partial_cleanup();

      string pic = "";
#ifdef use_imms
      pic = get_cover_from_filename(audio_state->p->p_cur_nr());
#endif

      int x_pos = 70;
      if (pic.empty())
	x_pos = 7;

      x_pos += audio_playback_offset;

      if (!pic.empty()) {
	render->create_scaled_image_wrapper(pic, 57, 63);
	int pic_y_pos = conf->p_h_res()-65;
	if (conf->p_v_res() == 720)
	  pic_y_pos += 2;
	audio_state->playback.add(new PFObj(pic, 7 + audio_playback_offset, pic_y_pos, 57, 63, false, 0));
      }

      if (audio_state->p_pause()) {
	PObj *p = new PObj(themes->audio_playback_pause, x_pos, conf->p_h_res()-65, 0, NOSCALING);
	PObj *p2 = new PObj(themes->audio_playback_pause, x_pos + (53 - p->real_w)/2, 
			    conf->p_h_res() - 65 + (65 - p->real_h)/2, 0, NOSCALING);
	delete p;
	audio_state->playback.add(p2);
      } else {
	PObj *p = new PObj(themes->audio_playback_play, x_pos, conf->p_h_res()-65, 0, NOSCALING);
	PObj *p2 = new PObj(themes->audio_playback_play, x_pos + (53 - p->real_w)/2, 
			    conf->p_h_res() - 65 + (65 - p->real_h)/2, 0, NOSCALING);
	delete p;
	audio_state->playback.add(p2);
      }

      string buffer, artist, album, title;
      
#ifdef use_imms
      // the extracted information is more reliant for VBR mp3 than the audio
      // player, same for artist, album and title
      if (!audio_state->p->p_cur_nr().path.empty() && audio_state->p->p_cur_nr().type != "web") {

	Dbaudiofile dbaf = lookup_file_in_db(audio_state->p->p_cur_nr().path);
	buffer = format_time(audio_state->p->p_cur_time(), dbaf.length != 0 ? dbaf.length : audio_state->p->p_total_time());
	artist = string_format::convert(dbaf.artist, true);
	album = string_format::convert(dbaf.album, true);
	title = string_format::convert(dbaf.title, true);

      } else {
#endif
	if (audio_state->p->p_cur_nr().type == "web")
	  buffer = format_time(audio_state->p->p_cur_time());
	else
	  buffer = format_time(audio_state->p->p_cur_time(), audio_state->p->p_total_time());
	artist = audio_state->p->p_artist();
	album = audio_state->p->p_album();
	title = audio_state->p->p_title();

#ifdef use_imms
      }
#endif

      // print artist info
      if ((!artist.empty() && !title.empty()) || 
	  (audio_state->p->p_cur_nr().type == "web" && !title.empty())) {

	if (artist == "SCRIPT") {
	  artist = "";
	  album = "";
	}

	if (artist.empty() && audio_state->p->p_cur_nr().type == "web")
	  artist = audio_state->p->p_cur_nr().name; // radio station name

	int y_delta = 0;
	
	if (artist.empty() && album.empty())
	  y_delta = - 15;
	else {

	  int artist_x_size, album_x_size, none;
	  string_format::calculate_string_size(artist, "Vera/17", &artist_x_size, &none);
	  string_format::calculate_string_size(album, "Vera/17", &album_x_size, &none);

	  int max_size = (conf->p_v_res()-(53+x_pos)-30-146);
	  int max_artist_size = max_size/2;
	  int max_album_size = max_size/2;

	  if (artist_x_size < max_size/2)
	    max_album_size = max_size-artist_x_size;
	  else
	    if (album_x_size < max_size/2)
	      max_artist_size = max_size-album_x_size;

	  string_format::format_to_size(artist, "Vera/17", max_artist_size, true);
	  string_format::format_to_size(album, "Vera/17", max_album_size, true);

	  string artist_album = artist;
	  if (artist.empty() || album.empty())
	    artist_album += album;
	  else
	    artist_album += (" - " + album);

	  audio_state->playback.add(new TObj(artist_album, 
					     "Vera/17", 53+x_pos, conf->p_h_res()-65, 
					     themes->audio_playback_font1, themes->audio_playback_font2, 
					     themes->audio_playback_font3, 0));
	
	}

	int x_size, y_size;
	string_format::calculate_string_size(string("     ") + buffer, "Vera/17", &x_size, &y_size);

	string_format::format_to_size(title, "Vera/17", conf->p_v_res()-(53+x_pos)-x_size-146, true);

	audio_state->playback.add(new TObj(title + "     " + buffer, 
					   "Vera/17", 53+x_pos, conf->p_h_res()-35+y_delta, 
					   themes->audio_playback_font1, themes->audio_playback_font2, 
					   themes->audio_playback_font3, 0));

    
      } else {
	Simplefile cur_pos = audio_state->p->p_cur_nr();

	string name = cur_pos.name  + "     " + buffer;

	string_format::format_to_size(name, "Vera/17", conf->p_v_res()-2*(53+x_pos), true);
	audio_state->playback.add(new TObj(name, "Vera/17", 53+x_pos, conf->p_h_res()-51, 
					   themes->audio_playback_font1, themes->audio_playback_font2,
					   themes->audio_playback_font3, 0));

      }

      render->draw("Audio print track", true);
      render->image_mut.leaveMutex();

    } else if (audio_state->has_played) {

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

      audio_state->has_played = false;
      audio_state->playback.cleanup();

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

void Audio::print_lcd_audiotrack()
{
  if (conf->p_audio_started()) {
    
    if (audio_state->p_playing() && audio_state->p->is_playing()) {

      string buffer = format_time(audio_state->p->p_cur_time(), audio_state->p->p_total_time());
  
      if (!audio_state->p->p_artist().empty() && !audio_state->p->p_title().empty()) {
	lcdprint(("widget_set MMS line0 1 1 {" + audio_state->p->p_artist() + "}\n"));
	lcdprint(("widget_set MMS line1 1 2 {" + audio_state->p->p_album() + "}\n"));
	lcdprint(("widget_set MMS line2 1 3 {" + audio_state->p->p_title() + "}\n"));
	lcdprint(("widget_set MMS line3 1 4 {" + string(buffer) + "}\n"));
      } else {
	Simplefile cur_pos = audio_state->p->p_cur_nr();

	lcdprint(("widget_set MMS line0 1 1 {" + cur_pos.name + "}\n"));
	lcdprint("widget_set MMS line1 1 2 {}\n");
	lcdprint("widget_set MMS line2 1 3 {}\n");
	lcdprint(("widget_set MMS line3 1 4 {" + string(buffer) + "}\n"));
      }
    }
  }
}

int Audio::print_audio_time()
{
  return 300; // ms
}

void Audio::check_audio_state()
{
  if (conf->p_audio_started()) {

    audio_state->p->gather_info();

    // should we play the next track?
    if (audio_state->p->play_next() || 
	!audio_state->p->is_playing() && audio_state->p_playing() && 
	!audio_state->p_pause() && !audio_state->p->is_buffering()) {

      Startmenu &startmenu = Startmenu::get_instance();

      // random modes takes care of stopping when the last track has been played
      if (audio_state->check_repeat() || conv::stob(opts.repeat()) || 
	  audio_state->queue_size() > 0) {
	audio_state->p->next();
	if (startmenu.module == "playlist" && !audio_state->fullscreen_info && !printing_information)
	  print(*files);
      } else {
	// we reached the end stop playing 
	audio_state->set_playing(false);

	if (playlist_size() == 0) {
	  // make sure we can't press play after the playback has ended
	  Simplefile s;
	  audio_state->p->set_cur_nr(s);
	} else {
	  if (startmenu.module == "playlist" && !audio_state->fullscreen_info)
	    print(*files);
	}
	
	if (conv::stob(opts.shutdown())) {
	  Shutdown &sd = Shutdown::get_instance();
	  sd.enable();
	}
      }
    }
  }
}

int Audio::check_audio_time()
{
  return 100; // ms
}

void Audio::print_audio_screensaver()
{
  if (audio_state->p_playing() && audio_state->p->is_playing()) {

    render->image_mut.enterMutex();

    render->device->wait_for_output_done();
  
    if (audio_state->playback_fullscreen.elements.size() > 0)
      audio_state->playback_fullscreen.partial_cleanup();

    audio_state->playback_fullscreen.add(new RObj(0, 0, conf->p_v_res(), conf->p_h_res(), 0, 0, 0, 255, 0));

    string buffer;

    string artist, album, title;

    if (audio_state->p->p_cur_nr().type == "web") {
      buffer = format_time(audio_state->p->p_cur_time());
      artist = audio_state->p->p_artist();
      album = audio_state->p->p_album();
      title = audio_state->p->p_title();
    } else {
#ifdef use_imms
      // the extracted information is more reliant for VBR mp3 than the audio
      // player, same for artist, album and title
      Dbaudiofile dbaf = lookup_file_in_db(audio_state->p->p_cur_nr().path);

      buffer = format_time(audio_state->p->p_cur_time(), dbaf.length != 0 ? dbaf.length : audio_state->p->p_total_time());
      
      artist = string_format::convert(dbaf.artist, true);
      album = string_format::convert(dbaf.album, true);
      title = string_format::convert(dbaf.title, true);

#else
      buffer = format_time(audio_state->p->p_cur_time(), audio_state->p->p_total_time());
#endif
    }
    
    string pic = "";

#ifdef use_imms
    pic = get_cover_from_filename(audio_state->p->p_cur_nr());
#endif

    int max_x_pic = 240, max_y_pic = 266;
	  
    if (conf->p_v_res() == 1024) {
      max_x_pic = 300;
      max_y_pic = 333;
    } else if (conf->p_h_res() == 405) {
      max_x_pic = 135;
      max_y_pic = 150;
    }
    
    int x_pos = max_x_pic+60+30;
    if (pic.empty())
      x_pos = 100;

    int y;

    if ((!artist.empty() && !title.empty())
	|| (audio_state->p->p_cur_nr().type == "web" && !title.empty())) {

      y = (conf->p_h_res()-165)/2;
      
      if (!pic.empty()) {
	render->create_scaled_image_wrapper(pic, max_x_pic, max_y_pic);
	PFObj *p = new PFObj(pic, 60, 60, max_x_pic, max_y_pic, true, 2);
	y = (conf->p_h_res()-p->real_h)/2;
	p->real_y = p->y = y;
	audio_state->playback_fullscreen.add(new RObj(55, y-5, p->w+10, p->h+10, 255, 255, 255, 255, 1));
	audio_state->playback_fullscreen.add(p);
      } 
      
      if (artist == "SCRIPT") {
	artist = "";
	album = "";
      }

      if (artist.empty() && audio_state->p->p_cur_nr().type == "web")
	artist = audio_state->p->p_cur_nr().name; // radio station name

      string_format::format_to_size(artist, "Vera/28", conf->p_v_res()-x_pos-30, true);
      audio_state->playback_fullscreen.add(new TObj(artist, "Vera/28", x_pos, y, 
						    themes->audio_playback_font1, themes->audio_playback_font2, 
						    themes->audio_playback_font3, 1));

      y += 45;

      string_format::format_to_size(album, "Vera/25", conf->p_v_res()-x_pos-30, true);
      audio_state->playback_fullscreen.add(new TObj(album, "Vera/25", x_pos, y, 
						    themes->audio_playback_font1, themes->audio_playback_font2, 
						    themes->audio_playback_font3, 1));

      y += 40;

      string_format::format_to_size(title, "Vera/22", conf->p_v_res()-x_pos-30, true);
      audio_state->playback_fullscreen.add(new TObj(title, "Vera/22", x_pos, y, 
						    themes->audio_playback_font1, themes->audio_playback_font2, 
						    themes->audio_playback_font3, 1));

      y += 40;

      audio_state->playback_fullscreen.add(new TObj(buffer, "Vera/22", x_pos, y, 
						    themes->audio_playback_font1, themes->audio_playback_font2, 
						    themes->audio_playback_font3, 1));

      y += 40;

    } else {

      y = (conf->p_h_res()-165)/2;

      if (!pic.empty()) {
	render->create_scaled_image_wrapper(pic, max_x_pic, max_y_pic);
	PFObj *p = new PFObj(pic, 60, 60, max_x_pic, max_y_pic, true, 1);
	y = (conf->p_h_res()-p->real_h)/2;
	p->real_y = p->y = y;
	audio_state->playback_fullscreen.add(p);
      } 

      y = (conf->p_h_res()-80)/2;

      Simplefile cur_pos = audio_state->p->p_cur_nr();

      string name = cur_pos.name;

      string_format::format_to_size(name, "Vera/28", conf->p_v_res()-x_pos-30, true);
      audio_state->playback_fullscreen.add(new TObj(name, "Vera/22", x_pos, y, 
						    themes->audio_playback_font1, themes->audio_playback_font2,
						    themes->audio_playback_font3, 1));

      y += 40;
      
      audio_state->playback_fullscreen.add(new TObj(buffer, "Vera/22", x_pos, y, 
						    themes->audio_playback_font1, themes->audio_playback_font2,
						    themes->audio_playback_font3, 1));
      y += 40;

    }

    y += 20;

    std::ostringstream played_info;
    played_info << gettext("Playing track: ");
    
    if (opts.shuffle() != gettext("off"))
      played_info << audio_state->played_tracks.size();
    else
      played_info << get_real_playlist_pos()+1;

    played_info << "/" << playlist_size();

    audio_state->playback_fullscreen.add(new TObj(played_info.str(), "Vera/22", x_pos, y, 
						  themes->audio_playback_font1, themes->audio_playback_font2, 
						  themes->audio_playback_font3, 1));

    y += 40;

    if (audio_state->p_pause())
      audio_state->playback_fullscreen.add(new PObj(themes->audio_playback_pause, x_pos, y, 0, NOSCALING));

    render->draw("screensaver", true);
    render->image_mut.leaveMutex();
  }
}

void Audio::print_audio_fullscreen()
{
  if (conf->p_audio_started()) {

    BusyIndicator &busy_indicator = BusyIndicator::get_instance();

    if ((time(0)-conf->p_last_key()) > 60*conf->p_idle_time() && audio_state->p_playing()) {
      audio_state->fullscreen_info = true;
      audio_state->playback_fullscreen_shown = true;
      print_audio_screensaver();
    } else {
      if (audio_state->playback_fullscreen_shown) {

	Updater &updater = Updater::get_instance();
  
	render->image_mut.enterMutex();

	render->device->wait_for_output_done();

	audio_state->playback_fullscreen_shown = false;
	audio_state->fullscreen_info = false;
	audio_state->playback_fullscreen.cleanup();

	render->complete_redraw();

	render->image_mut.leaveMutex();

	Startmenu &startmenu = Startmenu::get_instance();
	if (startmenu.module == "playlist")
	  print(*files);
      }
    }
  }
}

int Audio::check_audio_fullscreen()
{
  return 300; // ms
}

#ifdef use_imms
void Audio::extract_metadata()
{
  if (!extracted_all_files && (conf->p_idle_time() > 0) && 
      (opts.metadata_extract() == gettext("yes"))) {

    int nr_extracts = 3;

    // check if we are idle
    int idle_time = time(0)-conf->p_last_key();
    if (idle_time < 3)
      return;
    else if (idle_time > 60 * conf->p_idle_time())
      nr_extracts = 4;

    if (all_metadata_files.empty()) {
      list<string> dirs = conf->p_audio_dirs();
      for (list<string>::const_iterator iter = dirs.begin(); iter != dirs.end(); ++iter)
	for (file_iterator<file_t, default_order> i (*iter); i != i.end (); ++i)
	  if (!isDirectory(i->getName()))
	    all_metadata_files.push(i->getName());
      // optimization, we don't want to both make the list and start extracting
      return;
    }

    while (!all_metadata_files.empty()) {
      if (!file_exists_in_db(all_metadata_files.top())) {
	lookup_file_in_db(all_metadata_files.top());
	if (nr_extracts == 0)
	  return;
	else
	  --nr_extracts;
      }
      all_metadata_files.pop();
    }

    extracted_all_files = true;
  } 
}

int Audio::check_metadata()
{
  return 3000; // ms
}
#endif

int Audio::check_volume()
{
  return 300; // ms
}

void Audio::show_volume()
{
  if (display_volume) {

    render->image_mut.enterMutex();

    render->device->wait_for_output_done();
  
    if (audio_state->volume.elements.size() > 0)
      audio_state->volume.partial_cleanup();

    if (audio_state->fullscreen_info) {

      if (audio_state->p->getvol()!=0)
	audio_state->volume.add(new TObj("VOL: " + conv::itos(audio_state->p->getvol()), "Vera/22", 
					 conf->p_v_res()-150, conf->p_h_res()-75, 
					 themes->audio_playback_font1, 
					 themes->audio_playback_font2, themes->audio_playback_font3, 1));

      if (audio_state->p->is_mute())
	audio_state->volume.add(new PObj(themes->audio_mute, conf->p_v_res()-200, conf->p_h_res()-75, 1, NOSCALING)); 
      
      // don't redraw volume until vol+ or vol- is pressed again
      display_volume=false;
    } else {
      // display sound level 
      if (audio_state->p->getvol() > 0)
	audio_state->volume.add(new PObj(themes->audio_volume_bar, conf->p_v_res()-40, 
					 int(conf->p_h_res() - 190 + (125  * (1-audio_state->p->getvol()/100.0))), 
					 audio_state->p->getvol()/100.0, 3));

      // display skin for sound level
      audio_state->volume.add(new PObj(themes->audio_volume, conf->p_v_res()-40 ,conf->p_h_res()-190, 3, NOSCALING));

      // display muted
      if (audio_state->p->is_mute())
	audio_state->volume.add(new PObj(themes->audio_mute, conf->p_v_res()-246, conf->p_h_res()-52, 3, NOSCALING)); 
 
      // don't redraw volume until vol+ or vol- is pressed
      display_volume=false;
    }

    audio_state->volume_shown = true;

    render->draw("volume", true);
    render->image_mut.leaveMutex();

    displayed_volume = time(0);

  } else {

    if (audio_state->volume_shown && time(0)-displayed_volume > 2) {

      render->image_mut.enterMutex();

      render->device->wait_for_output_done();

      audio_state->volume_shown = false;
      audio_state->volume.cleanup();

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

void Audio::startup_updater()
{
  Updater &updater = Updater::get_instance();
  TimeFunctori<Audio> *time_func1 = new TimeFunctori<Audio>(this, &Audio::check_audio_time);
  TimeFunctorv<Audio> *call_func1 = new TimeFunctorv<Audio>(this, &Audio::check_audio_state);
  updater.timer.add(TimeElement("audio_next", time_func1, call_func1));
  if (conf->p_lcd()) {
    TimeFunctori<Audio> *time_func2 = new TimeFunctori<Audio>(this, &Audio::print_audio_time);
    TimeFunctorv<Audio> *call_func2 = new TimeFunctorv<Audio>(this, &Audio::print_lcd_audiotrack);
    updater.timer.add(TimeElement("audio_lcd", time_func2, call_func2));
  }
  TimeFunctori<Audio> *time_func3 = new TimeFunctori<Audio>(this, &Audio::print_audio_time);
  TimeFunctorv<Audio> *call_func3 = new TimeFunctorv<Audio>(this, &Audio::print_audiotrack);
  updater.timer.add(TimeElement("audio_update", time_func3, call_func3));
  if (conf->p_idle_time() > 0) {
    TimeFunctori<Audio> *time_func4 = new TimeFunctori<Audio>(this, &Audio::check_audio_fullscreen);
    TimeFunctorv<Audio> *call_func4 = new TimeFunctorv<Audio>(this, &Audio::print_audio_fullscreen);
    updater.timer.add(TimeElement("audio_fullscreen", time_func4, call_func4));
  }
#ifdef use_imms
  TimeFunctori<Audio> *time_func5 = new TimeFunctori<Audio>(this, &Audio::check_metadata);
  TimeFunctorv<Audio> *call_func5 = new TimeFunctorv<Audio>(this, &Audio::extract_metadata);
  updater.timer.add(TimeElement("extra_metadata", time_func5, call_func5));
#endif
  TimeFunctori<Audio> *time_func6 = new TimeFunctori<Audio>(this, &Audio::check_volume);
  TimeFunctorv<Audio> *call_func6 = new TimeFunctorv<Audio>(this, &Audio::show_volume);
  updater.timer.add(TimeElement("audio_volume", time_func6, call_func6));
}

void Audio::play()
{
  if (playlist.size() > 0) {
    if (opts.shuffle() == gettext("off")) {
      if ((audio_state->p->p_cur_nr()).path.empty()) {
	audio_state->p->set_cur_nr(vector_lookup(playlist, playlist_pos_int));
	audio_state->add_track_to_played(audio_state->p->p_cur_nr());
      }
    } else
      if (!audio_state->p_pause())
	audio_state->p->next_helper();
	    
#ifdef music_radio_bttv
    if (audio_state->p->p_cur_nr().type == "bttvradio")
      bttv->play(audio_state->p->p_cur_nr().path);
    else
#endif
      audio_state->p->play();

    // update the screen with the playing track
    Startmenu &startmenu = Startmenu::get_instance();
    if (startmenu.module == "playlist" && !audio_state->fullscreen_info)
      print(*files);
  }
}

void Audio::pause()
{
#ifdef music_radio_bttv
  if (audio_state->p->p_cur_nr().type == "bttvradio")
    bttv->pause();
  else
#endif
    audio_state->p->pause();
}

void Audio::stop()
{
#ifdef music_radio_bttv
  if (audio_state->p->p_cur_nr().type == "bttvradio")
    bttv->stop();
  else
#endif
    audio_state->p->stop();

  // if clear on finish = false
  if (playlist_size() == 0) {
    Simplefile empty;
    audio_state->p->set_cur_nr(empty);
  }
}

void Audio::next()
{
  audio_state->p->next();
}

void Audio::prev()
{
  audio_state->p->prev();
}

void Audio::ff()
{
  audio_state->p->ff();
}

void Audio::fb()
{
  audio_state->p->fb();
}

void Audio::vol_plus()
{
#ifdef music_radio_bttv
  if (audio_state->p->p_cur_nr().type == "bttvradio")
    bttv->volup();
  else {
#endif
    audio_state->p->volup();
    display_volume=true;
#ifdef music_radio_bttv
  }
#endif
}

void Audio::vol_minus()
{
#ifdef music_radio_bttv
  if (audio_state->p->p_cur_nr().type == "bttvradio")
    bttv->voldown();
  else {
#endif
    audio_state->p->voldown();
    display_volume=true;
#ifdef music_radio_bttv
  }
#endif
}

void Audio::mute()
{
#ifdef music_radio_bttv
  if (audio_state->p->p_cur_nr().type == "bttvradio")
    bttv->mute();
  else
#endif
    audio_state->p->mute();
}

void Audio::change_mode_from_add()
{
  if (input_master->cur_map == &input_master->playlist_load)
    return;

  Startmenu &startmenu = Startmenu::get_instance();

  if (startmenu.module == "playlist") {

    exit_loop = true;

  } else if (playlist.size() != 0) {
    input_master->last_map = input_master->cur_map;
    startmenu.last_module = startmenu.module;

#ifdef c_picture
    // disable pictures slideshow when entering audio playlist
    Updater &updater = Updater::get_instance();
    bool pictures_fullscreen_was_running = false;
    bool audio_fullscreen_was_running = false;
    if (updater.timer.status("pictures")) {
      pictures_fullscreen_was_running = true;
      updater.timer.deactivate("pictures");
      if (!updater.timer.status("audio_fullscreen"))
	updater.timer.activate("audio_fullscreen");
      else
	audio_fullscreen_was_running = true;
    }
#endif

    mainloop(PLAY);

    input_master->cur_map = input_master->last_map;
    startmenu.module = startmenu.last_module;

    if (startmenu.module == "audio") {
      input_master->cur_map = &input_master->audio_add;
      files = &audio_list;
      mode = ADD;
    }
	
#ifdef c_picture
    if (pictures_fullscreen_was_running) {
      if (!audio_fullscreen_was_running)
	updater.timer.deactivate("audio_fullscreen");
      updater.timer.activate("pictures");
    }
#endif

  } else if (playlist.size() == 0)
    DialogWaitPrint pdialog(gettext("No tracks in playlist"), 1000);
}

void Audio::change_mode_from_playlist()
{
  if (list_playlists().size() >= 1) {

    if (mode != PLAY)
      input_master->last_map = input_master->cur_map;

    if (choose_playlist()) {
      mode = PLAY;

      files = &playlist;

      playlist_pos_int = 0;
    }

    if (mode == PLAY)
      input_master->cur_map = &input_master->playlist;
    else
      input_master->cur_map = input_master->last_map;
	      
  } else
    Print pdialog(gettext("No saved playlists found"), Print::SCREEN);
}

void Audio::playlist_commands()
{
  ExtraMenu em = ExtraMenu(gettext("Playlist Commands"));

  TimeFunctorv<Audio> t_play = TimeFunctorv<Audio>(this, &Audio::play);
  em.add_item(ExtraMenuItem(gettext("Play"), input_master->find_shortcut("play"), 
			    &t_play));
  TimeFunctorv<Audio> t_pause = TimeFunctorv<Audio>(this, &Audio::pause);
  em.add_item(ExtraMenuItem(gettext("Pause"), input_master->find_shortcut("pause"), 
			    &t_pause));
  TimeFunctorv<Audio> t_stop = TimeFunctorv<Audio>(this, &Audio::stop);
  em.add_item(ExtraMenuItem(gettext("Stop"), input_master->find_shortcut("stop"), 
			    &t_stop));
  TimeFunctorv<Audio> t_next = TimeFunctorv<Audio>(this, &Audio::next);
  em.add_item(ExtraMenuItem(gettext("Next track"), input_master->find_shortcut("next_track"), 
			    &t_next));
  TimeFunctorv<Audio> t_prev = TimeFunctorv<Audio>(this, &Audio::prev);
  em.add_item(ExtraMenuItem(gettext("Previous track"), 
			    input_master->find_shortcut("prev_track"), &t_prev));
  TimeFunctorv<Audio> t_ff = TimeFunctorv<Audio>(this, &Audio::ff);
  em.add_item(ExtraMenuItem(gettext("Fast forward"), input_master->find_shortcut("ff"), 
			    &t_ff));
  TimeFunctorv<Audio> t_fb = TimeFunctorv<Audio>(this, &Audio::fb);
  em.add_item(ExtraMenuItem(gettext("Fast backward"), input_master->find_shortcut("fb"), 
			    &t_fb));
  TimeFunctorv<Audio> t_vp = TimeFunctorv<Audio>(this, &Audio::vol_plus);
  em.add_item(ExtraMenuItem(gettext("Increase volume"), input_master->find_shortcut("vol+"), 
			    &t_vp));
  TimeFunctorv<Audio> t_vm = TimeFunctorv<Audio>(this, &Audio::vol_minus);
  em.add_item(ExtraMenuItem(gettext("Decrease volume"), input_master->find_shortcut("vol-"), 
			    &t_vm));
  TimeFunctorv<Audio> t_m = TimeFunctorv<Audio>(this, &Audio::mute);
  em.add_item(ExtraMenuItem(gettext("Mute"), input_master->find_shortcut("mute"), 
			    &t_m));

  TimeFunctorv<Audio> t_cma = TimeFunctorv<Audio>(this, 
						  &Audio::change_mode_from_add);

  if (input_master->cur_map != &input_master->playlist_load) {
    Startmenu &startmenu = Startmenu::get_instance();

    string text = gettext("Playlist");
    if (mode == PLAY && startmenu.module == "playlist")
      text = gettext("Add files to playlist");

    em.add_item(ExtraMenuItem(text, input_master->find_shortcut("mode"), 
			      &t_cma));
  }
  
#ifdef use_epg
  Updater &updater = Updater::get_instance();

  if (input_master->cur_map == &input_master->epg)
    updater.timer.deactivate("epg");
#endif

  em.mainloop();

#ifdef use_epg
  if (input_master->cur_map == &input_master->epg)
    updater.timer.activate("epg");
#endif
}

bool Audio::check_playback_keys(const Input &input, bool disable_mode_change)
{
  bool key_ok = false;

  if (input.command == "play") 
    {
      play();
      key_ok = true;
    }
  else if (input.command == "pause")
    {
      pause();
      key_ok = true;
    }
  else if (input.command == "stop")
    {
      stop();
    }
  else if (input.command == "next_track")
    {
      next();
      if (audio_state->fullscreen_info)
	key_ok = true;
    }
  else if (input.command == "prev_track")
    {
      prev();
      if (audio_state->fullscreen_info)
	key_ok = true;
    }
  else if (input.command == "ff")
    {
      ff();
      key_ok = true;
    }
  else if (input.command == "fb")
    {
      fb();
      key_ok = true;
    }
  else if (input.command == "vol+")
    {
      vol_plus();
      key_ok = true;
    }
  else if (input.command == "vol-")
    {
      vol_minus();
      key_ok = true;
    }
  else if (input.command == "mute")
    {
      mute();
      key_ok = true;
    }
  // mode
  else if (!disable_mode_change && ((input.command == "mode" && input.mode == "audio_general") 
	   || change_mode))
    {
      change_mode = false;
      change_mode_from_add();
    }
  else if (!disable_mode_change && input.command == "playlist_mode" && input.mode == "audio_general")
    {
      Startmenu &startmenu = Startmenu::get_instance();

      if (startmenu.module == "playlist" || startmenu.module == "audio")
	change_mode_from_playlist();
    }
  else if (!disable_mode_change && input.command == "playlist_commands")
    {
      if (!conf->p_sam())
	playlist_commands();
    }

  return key_ok;
}
