#ifndef AUDIO_HPP
#define AUDIO_HPP

#include "config.h"

#include "module.hpp"
#include "simplefile.hpp"
#include "dbaudiofile.hpp"
#include "shuffle_list.hpp"
#include "options.hpp"

#ifdef music_radio_bttv
#include "bttv.hpp"
#endif

#include "audio_s.hpp"

#include <stack>
#include <vector>

#ifdef use_imms
#define audio_type Dbaudiofile
#else
#define audio_type Simplefile
#endif

class Audio : public Module 
{
  friend class Player;
  friend class Updater;
private:

  // class definitions
  class AudioOpts : public Options 
  {
  public:
    AudioOpts();
    ~AudioOpts();
  
    std::string repeat() 
    {
      return val[2]->values[val[2]->pos];
    }

    std::string shutdown() 
    {
      return val[3]->values[val[3]->pos];
    }
    
    std::string shuffle() 
    {
      return val[4]->values[val[4]->pos];
    }

    std::string bitrate()
    {
      return val[5]->values[val[5]->pos];
    }
  
#ifdef use_imms
    std::string metadata_extract() 
    {
      return val[6]->values[val[6]->pos];
    }
#endif

    std::vector<Option*> values() 
    {
      return val;
    }
  
  private:
    void read_dirs();
    Option *reload_p, *repeat_p, *shutdown_p, *shuffle_p, *bitrate_p;
#ifdef use_imms
    Option *extract_meta_p;
#endif
  };

  Audio();

  // commands
  void exit();
  void search_func();
  void options();
#ifdef use_imms
  void print_information();
#endif
  // audio add specific
  void add();
  void add_all();
  void play_now();
  void play_track_now();
  void enter_dir();
  void leave_dir();
  // playlist specific
  void play_track();
  void move_up();
  void move_down();
  void delete_track();
  void queue_track();
  void clear_playlist();
  void save_playlist_func();
  // playlist
  void play();
  void pause();
  void stop();
  void next();
  void prev();
  void ff();
  void fb();
  void vol_plus();
  void vol_minus();
  void mute();
  void change_mode_from_add();
  void change_mode_from_playlist();

  void action_load_playlist();
  void delete_playlist();
  std::vector<std::string> saved_playlists;
  int saved_playlist_pos;
  bool returned, return_value;

  bool exit_loop;
  bool change_mode;

  bool printing_information;
  
#ifdef use_imms
  ost::Mutex imms_mut;
  Dbaudiofile lookup_file_in_db(const std::string& file);
#endif

  std::string format_time(int cur_time, int total_time);
  std::string format_time(int time);

  const int position_int();
  void set_position_int(int pos);

#ifdef use_imms
  void print_graphical(const std::vector<Dbaudiofile>& cur_files);
  bool no_folders_in_dir(const std::vector<Dbaudiofile>& cur_files);
  void print_tracklist(std::vector<Dbaudiofile>& cur_files);
  void print_dir(int pos, int j, int x, int y);
  std::string get_first_file_in_dir(const string& dir);

  int y_start;

  int image_width;
  int image_height;
  int image_height_all_eks_text;

  // including text and space around image
  int image_width_all;
  int image_height_all;
  int image_height_all_search;

  int images_per_row;
  int rows;
  int rows_search;
#endif

#ifdef use_imms
  void print_graphical_element(Dbaudiofile& r, const Dbaudiofile& position, 
			       int y, bool picture_dir);
#else
  void print_normal_element(const Simplefile& r, const Simplefile& position, 
			    int y);
#endif
  void print_string_element(const std::string& r, const std::string& position, int y);

  void print_options(const std::string& marking);

  // used for both radio + choose_playlist
  void print_list(const std::vector<std::string>& files, const std::string& position, 
		  const int int_position, bool playlist);

#ifdef use_media
  void add_audio_cd();
  void examine_audio_cd();
  void add_data_cd();

  // helper function
  std::vector<audio_type> get_audio_cd_list();
  void cd_menu();

  bool mounted_media;
#endif

  // add an audio dir recursively (all the files in it) to the playlist
  std::vector<audio_type> add_dir(const std::string& dir, bool from_media = false);
#ifdef use_imms
  // helper function for add_dir to add files in the correct order based on
  // track nr
  void add_tracks_from_dir(vector<Dbaudiofile> &files_in_dir, 
				  vector<Dbaudiofile> &cur_files);
#endif
  // read a dir and spit out a list of files and dirs
  std::vector<audio_type> rdir(const std::string& argv);
  // The following function for parsing audio dirs.
  std::vector<audio_type> parse_dir(const std::list<std::string>& dirs);

  // for saving information about how you traverse a filesystem a stack is a
  // much better solution. The first element is the dirs (cur_audio_dir before)
  // and the next is the position (audio_int_pos)
  std::stack<std::pair<std::list<std::string>, int > > folders;

  // used when adding files to the playlist
  int playlist_id;

  ShuffleList shuffle_list;

  std::string mainloop() 
  { return ""; }

#ifdef music_radio_bttv
  Bttv *bttv;
#endif
  
  struct file_sort
  {
    bool operator()(const audio_type& lhs, const audio_type& rhs);
  };

#ifdef use_imms
  int check_metadata();
  void extract_metadata();

  bool file_exists_in_db(const std::string& file);

  bool extracted_all_files;
  std::stack<std::string> all_metadata_files;
#endif

  bool radio_stations_downloaded;

  int audio_playback_offset;

  bool disabled_busy_indicator;

  bool load_playlist(const std::string& playlistfile);

  bool display_message;

public:
  static Audio* instance;

  // singleton
  static Audio* get_instance();

  ~Audio();

  bool play_now_warning;

  Audio_s *audio_state;

  int get_real_playlist_pos();

  // used for other modules to offset the position of audio playback
  void set_audio_playback_pos(int x)
  {
    audio_playback_offset = x;
  }

#ifdef use_media
  void pos_umount();
  void check_mount_before(const audio_type& track);
  void check_mount_after(const audio_type& track);
#endif

  void reset();

  void playlist_commands();

  // gcc 2.95 fix
  enum AudioMode { ADD, PLAY };

  AudioMode mode;

  std::vector<audio_type> *files;

  std::string get_name_from_file(const audio_type& d);
  std::string get_name_from_file_in_picture_dir(const audio_type& d);
  std::string get_first_cover_in_dir(const std::string& dir);

  bool search_compare(const audio_type& t);

  void print_top();
  void print_buttom(const std::vector<audio_type>& files);

#ifdef use_imms
  void print(std::vector<Dbaudiofile>& files);
#else
  void print(const std::vector<Simplefile>& files);
#endif

  int audiolist_size() { return audio_list.size(); }
  int playlist_size() { return playlist.size(); }

  bool choose_playlist();
  std::vector<std::string> list_playlists();

  // for changing mode
  std::vector<audio_type> audio_list;

  // for imms
  std::vector<audio_type> playlist;
  int playlist_pos_int;

  void load_current_dirs();
  void read_dirs();

  void save_playlist(const std::string& playlistfile, bool overwrite = false);
  bool load_last_playlist();

#ifdef use_media
  void detect_cdformat();
#endif

  std::string mainloop(AudioMode mode);

  void choose_radio();

  AudioOpts opts;

  bool display_volume;
  int displayed_volume;

  void print_audiotrack();
  void print_lcd_audiotrack();
  int print_audio_time();
  
  void check_audio_state();
  int check_audio_time();

  void print_audio_screensaver();

  void print_audio_fullscreen();
  int check_audio_fullscreen();

  int check_volume();
  void show_volume();

  void startup_updater();

  bool check_playback_keys(const Input &input, bool disable_mode_change = false);
};

#endif
