//  BMP
//  Copyright (C) 2005-2007 BMP development.
//
//  This program is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation; either version 2 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//
//  --
//
//  The BMPx project hereby grants permission for non GPL-compatible GStreamer
//  plugins to be used and distributed together with GStreamer and BMPx. This
//  permission is above and beyond the permissions granted by the GPL license
//  BMPx is covered by.

#ifndef BMP_PREFERENCES_HH
#define BMP_PREFERENCES_HH

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif //HAVE_CONFIG_H

#include <map>
#include <set>
#include <string>
#include <vector>

#include <glibmm/ustring.h>
#include <gdkmm/pixbuf.h>
#include <gtkmm.h>
#include <libglademm/xml.h>
#include <mcs/mcs.h>

// BMP Audio
#include "audio/audio.hh"

#ifdef HAVE_HAL
#  include "hal.hh"
#  include "dialog-manage-volume.hh"
#endif //HAVE_HAL

namespace Bmp
{
  class Core;

  /** Preferences dialog
   *
   * Bmp::Preferences is a complex dialog for adjusting run time parameters
   * of BMP trough the GUI instead of having to manipulate the configuration
   * file.
   */
  class Preferences
    : public Gtk::Window
  {
    protected:

      virtual void on_hide ();

      Glib::RefPtr<Gnome::Glade::Xml> m_ref_xml;

    public:

      Preferences (BaseObjectType                       * obj,
                   Glib::RefPtr<Gnome::Glade::Xml> const& xml);
      static Preferences * create ();
      virtual ~Preferences () {}

      void setup_lastfm ();
#ifdef HAVE_HAL
      void rescan_devices ();
#endif //HAVE_HAL

    private:

#ifdef HAVE_HAL
      class HALView
        : public Gtk::TreeView
      {
            public:

              HALView (BaseObjectType                       * obj,
                       Glib::RefPtr<Gnome::Glade::Xml> const& xml);

              void rescan_devices ();

            private:

              typedef std::pair <std::string, std::string> VolumeKey;

              class ColumnsVolumes
                : public Gtk::TreeModel::ColumnRecord
              {
                public:

                  Gtk::TreeModelColumn<Glib::ustring> voltype;
                  Gtk::TreeModelColumn<Glib::ustring> volname;
                  Gtk::TreeModelColumn<HAL::Volume>   volume;
                  Gtk::TreeModelColumn<bool>          mounted;

                  ColumnsVolumes ()
                  {
                    add (voltype);
                    add (volname);
                    add (volume);
                    add (mounted);
                  }
            };
            ColumnsVolumes volume_cr;

            Glib::RefPtr<Gtk::ListStore>    m_list_store;
            Glib::RefPtr<Gnome::Glade::Xml> m_ref_xml;

            enum Labels
            {
              L_MOUNT_PATH,
              L_DEVICE_FILE,
              L_SIZE,
              L_MOUNT_TIME,
              N_LABELS
            };

            Gtk::Label  * m_label[N_LABELS];

            void update_volume_details ();

            void cell_data_func_mounted       (Gtk::CellRenderer* cell, Gtk::TreeIter const& iter);
            void cell_data_func_mountpath     (Gtk::CellRenderer* cell, Gtk::TreeIter const& iter);
            void cell_data_func_device_udi    (Gtk::CellRenderer* cell, Gtk::TreeIter const& iter);
            void cell_data_func_volume_udi    (Gtk::CellRenderer* cell, Gtk::TreeIter const& iter);
            void cell_data_func_volume_name   (Gtk::CellRenderer* cell, Gtk::TreeIter const& iter);

            void hal_volume_add   (HAL::Volume const& volume);
            void hal_volume_del   (HAL::Volume const& volume);

            int  store_sort_func  (Gtk::TreeIter const& a,
                                   Gtk::TreeIter const& b);

            typedef std::map < VolumeKey, Gtk::TreeModel::RowReference > MVolumes;
            MVolumes m_volumes;
    
          public:
            VolumeManageDialog * m_volume_manage_dialog;
          private:
            void on_manage_volume ();
      };
  #endif //HAVE_HAL

      //// ---- Audio

      class AudioSystemColumnRecord
        : public Gtk::TreeModel::ColumnRecord
      {
        public:

          Gtk::TreeModelColumn<Glib::ustring> description;
          Gtk::TreeModelColumn<std::string>   name;
          Gtk::TreeModelColumn<int>           tab;
          Gtk::TreeModelColumn<Audio::Sink>   sink;

          AudioSystemColumnRecord()
          {
              add (description);
              add (name);
              add (tab);
              add (sink);
          }
      };
      AudioSystemColumnRecord  audio_system_columns;
      std::vector<int> audio_system_cbox_ids;

      Gtk::StockID stock (bool truth);
      void setup_audio_widgets ();
      void setup_audio ();

      void audio_system_apply_set_sensitive ();
      void audio_system_changed ();
      void audio_system_apply ();

      Gtk::ToggleButton                 * m_lastfm_submit_enable;
      Gtk::ToggleButton                 * m_lastfm_queue_enable;
      Gtk::Button                       * m_button_audio_system_apply;
      Gtk::Button                       * m_button_audio_system_reset;
      Gtk::ComboBox                     * m_cbox_audio_system;
      Gtk::HBox                         * m_warning_audio_system_changed;
      Gtk::Notebook                     * m_notebook_audio_system;
      std::set<std::string>               m_sinks;
#ifdef HAVE_HAL
      HALView                           * m_volumes_view;
#endif //HAVE_HAL

      Glib::RefPtr<Gtk::ListStore>        m_list_store_audio_systems;
      Glib::RefPtr<Gdk::PixbufAnimation>  m_throbber;
      Glib::RefPtr<Gdk::Pixbuf>           m_state_ok;
      Glib::RefPtr<Gdk::Pixbuf>           m_state_err;

#ifdef HAVE_ALSA
      // ALSA
      struct AlsaDevice
      {
          std::string   m_handle;
          int	          m_card_id;
          int	          m_device_id;
          std::string   m_name;

          AlsaDevice  () {}
          AlsaDevice  ( std::string const&  handle,
                        int                 card_id,
                        int                 device_id,
                        std::string const&  name)

          : m_handle    (handle),
            m_card_id   (card_id),
            m_device_id (device_id),
            m_name      (name) {}
      };
      typedef std::vector <AlsaDevice> AlsaDevices;

      struct AlsaCard
      {
          std::string   m_handle;
          int           m_card_id;
          std::string   m_id;
          std::string   m_name;
          std::string   m_longname;
          std::string   m_driver;
          std::string   m_mixer;

          AlsaDevices   m_devices;

          AlsaCard () {}
          AlsaCard (std::string const&  handle,
                    int                 card_id,
                    std::string const&  id,
                    std::string const&  name,
                    std::string const&  longname,
                    std::string const&  driver,
                    std::string const&  mixer,
                    AlsaDevices      &  devices)

          : m_handle    (handle),
            m_card_id   (card_id),
            m_id        (id),
            m_name      (name),
            m_longname  (longname),
            m_driver    (driver),
            m_mixer     (mixer)
          {
            std::swap (devices, m_devices);
          }

          operator Glib::ustring ()
          {
            return Glib::ustring ((m_longname.size() ? m_longname : m_name));
          }
      };
      typedef std::vector <AlsaCard> AlsaCards;

      class AlsaCardColumnRecord
        : public Gtk::TreeModel::ColumnRecord
      {
        public:

          Gtk::TreeModelColumn <Glib::ustring> name;
          Gtk::TreeModelColumn <AlsaCard>      card;

          AlsaCardColumnRecord ()
          {
            add (name);
            add (card);
          }
      };

      class AlsaDeviceColumnRecord
        : public Gtk::TreeModel::ColumnRecord
      {
        public:

          Gtk::TreeModelColumn <Glib::ustring> name;
          Gtk::TreeModelColumn <AlsaDevice>    device;

          AlsaDeviceColumnRecord ()
          {
            add (name);
            add (device);
          }
      };

      AlsaCardColumnRecord m_alsa_card_columns;
      AlsaDeviceColumnRecord m_alsa_device_columns;


      Gtk::ComboBox                     * m_cbox_alsa_card;
      Gtk::ComboBox                     * m_cbox_alsa_device;
      Gtk::SpinButton                   * m_alsa_buffer_time;
      Glib::RefPtr<Gtk::ListStore>        m_list_store_alsa_cards;
      Glib::RefPtr<Gtk::ListStore>        m_list_store_alsa_device;

      AlsaCards get_alsa_cards ();
      void on_alsa_card_changed ();
#endif //HAVE_ALSA

      // Video
      Gtk::ComboBox                     * m_cbox_video_out;

      // OSS
      Gtk::ComboBoxEntry                * m_oss_cbe_device;
      Gtk::SpinButton                   * m_oss_buffer_time;

      // ESD
      Gtk::Entry                        * m_esd_host;
      Gtk::SpinButton                   * m_esd_buffer_time;

      // PulseAudio
      Gtk::Entry                        * m_pulse_server;
      Gtk::Entry                        * m_pulse_device;
      Gtk::SpinButton                   * m_pulse_buffer_time;

      // Jack
      Gtk::Entry                        * m_jack_server;
      Gtk::SpinButton                   * m_jack_buffer_time;

#ifdef HAVE_SUN
      Gtk::ComboBoxEntry                * m_sun_cbe_device;
      Gtk::SpinButton                   * m_sun_buffer_time;
#endif // HAVE_SUN

#ifdef HAVE_HAL
      Gtk::Entry                        * m_halaudio_udi;
#endif // HAVE_SUN

      // Podcasts
      Gtk::FileChooserButton            * m_podcasts_fcb;

      // Last.fm
      void  mcs_scrobbler_queue_enable_changed (MCS_CB_DEFAULT_SIGNATURE);
      void  mcs_lastfm_credentials_changed (MCS_CB_DEFAULT_SIGNATURE);

      void  on_last_fm_radio_handshake ();
      void  on_last_fm_radio_connected ();
      void  on_last_fm_radio_disconnected ();

      void  on_scrobbler_handshake ();
      void  on_scrobbler_connected ();
      void  on_scrobbler_disconnected ();

      // Library
      void  import_tracks ();
      void  import_tracks_cancel ();

      void  fetch_covers ();

      void  scan_path (std::string const&);

      int   m_import_canceled;
    
      // MM Keys
      struct KeyControls
      {
        gint key, mask;

        KeyControls () : key(0), mask(0) {}
      };

      std::vector<KeyControls> m_mm_key_controls;
      int m_mm_option;

      void  set_keytext (gint entry_id, gint key, gint mask);
      bool  on_entry_key_press_event(GdkEventKey * event, gint entry_id);
      bool  on_entry_key_release_event(GdkEventKey * event, gint entry_id);
      void  on_clear_keyboard (gint entry_id);
      void  on_mm_option_changed (gint option);
      void  mm_load ();
      void  mm_apply ();
      void  mm_toggled ();

    public:
      typedef sigc::signal<void> Signal;
      struct SignalsT
      {
        Signal  LibraryUpdateRequest;
        Signal  HotkeyEditBegin;
        Signal  HotkeyEditEnd;
      };
    private:
      SignalsT Signals;
    public:

      Signal&
      signal_library_update_request ()
      {
        return Signals.LibraryUpdateRequest;
      }

      Signal&
      signal_mm_edit_begin ()
      {
        return Signals.HotkeyEditBegin;
      }

      Signal&
      signal_mm_edit_done ()
      {
        return Signals.HotkeyEditEnd;
      }

  }; // class Preferences
} // namespace Bmp

#endif // BMP_PREFERENCES_HH
