//  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_SHELL_HH
#define BMP_SHELL_HH

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

#include <map>
#include <string>

#include <glib/gtypes.h>
#include <glib/ghash.h>
#include <glibmm/main.h>
#include <glibmm/ustring.h>
#include <gdkmm/pixbuf.h>
#include <gdkmm/pixbufanimation.h>
#include <gtkmm/box.h>
#include <gtkmm/label.h>
#include <gtkmm/scale.h>
#include <gtkmm/notebook.h>
#include <gtkmm/statusbar.h>
#include <gtkmm/toggleaction.h>
#include <gtkmm/uimanager.h>
#include <gtkmm/window.h>
#include <sigc++/connection.h>
#include <sigc++/signal.h>

#include <boost/shared_ptr.hpp>
#include <boost/optional.hpp>
#include "mcs/mcs.h"

#include <libglademm/xml.h>

// BMP Widgets
#include "widgets/bmp_tray_icon.h"
#include "widgets/bmp_status_icon.h"

// BMP Types
#include "bmp/types/types-basic.hh"
#include "bmp/types/types-ui.hh"
#include "bmp/types/types-lastfm.hh"

#include "playbacksource.hh"
#include "preferences.hh"

#include "bookmark-manager.hh"
#include "audio/audio-types.hh"
#include "vfs.hh"

using namespace Bmp::LastFM;

namespace Bmp
{
  class InfoArea;
  class TrackDetails;
  class Equalizer;
  class Preferences;
  class AboutDialog;
  class SimpleProgress;
  class Popup;
  class Scroll;
  class SourceButton;
  class VideoWindow;

  enum PlaybackSourceID
  {
    SOURCE_NONE         = -1,
    SOURCE_LIBRARY      =  0,
    SOURCE_PLAYLIST     =  1,
    SOURCE_RADIO        =  2,
    SOURCE_LASTFM       =  3,
    SOURCE_PODCASTS     =  4,
    SOURCE_CDDA         =  5,
    SOURCE_JAMENDO      =  6,

    N_SOURCES           =  7,
  };

  typedef sigc::signal<void, int> SignalSourceSelected;

  class SourceSelection
    : public Gtk::VBox
  {
    private:
      SignalSourceSelected  source_selected_;
    public:

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

      void
      source_select   (PlaybackSourceID source);

      void
      source_activate (PlaybackSourceID source);

      SignalSourceSelected&
      source_selected ()
      {
        return source_selected_;
      }

      PlaybackSourceID      m_active_source;
      PlaybackSourceID      m_selected_source;

   protected:

      virtual bool
      on_expose_event (GdkEventExpose * event);

    private:

      typedef std::map<PlaybackSourceID, SourceButton*> ButtonsMap;
      typedef std::map<PlaybackSourceID, unsigned int>  SourceNotebookTabMap;

      void
      on_source_button_clicked (PlaybackSourceID source);

      bool
      on_parent_configure (GdkEventConfigure * event);

      void
      on_size_allocate (Gtk::Allocation & alloc);

      ButtonsMap            m_source_selection_buttons_map;
      SourceNotebookTabMap  m_notebook_tab_map;
  };

  class PlayerShell
    : public Gtk::Window
  {
    protected:

      virtual bool on_delete_event (GdkEventAny *event);
      virtual bool on_key_press_event (GdkEventKey*);

    public:

      class Player;

    private:

      typedef boost::shared_ptr<PlaybackSource> SourceSP;
      typedef std::map<std::string, PlaybackSourceID> URISchemeMap;

      Glib::RefPtr<Gnome::Glade::Xml> m_ref_xml;
      Player * m_player_dbus_obj; // MPRIS /Player object

      SourceSelection           * m_selection;
      SourceSP                    m_sources[N_SOURCES];
      PlaybackSource::Caps        m_source_caps[N_SOURCES];
      PlaybackSource::Flags       m_source_flags[N_SOURCES];
      URISchemeMap                m_uri_map;
      Glib::Mutex                 m_source_state_lock;
      Glib::Mutex                 m_source_cf_lock;
      Glib::Mutex                 m_metadata_lock;
      Glib::RecMutex              m_playback_lock;
      TrackMetadata               m_metadata;
      opt_stdstring               m_asin;

      GHashTable * get_metadata_hash_table ();

      /////////////// GENERIC
      void  on_close_window ();

      /////////////// SCROBBLING
      void  shell_lastfm_scrobble_current ();
      void  shell_lastfm_now_playing ();

      /////////////// PLAYBACK CONTROL
      void  play ();
      void  pause ();
      void  safe_pause_unset();
      void  prev ();
      void  next ();
      void  stop ();

      void  next_async_cb (int source_id);
      void  prev_async_cb (int source_id);
      void  play_async_cb (int source_id);
      void  play_post_internal (int source_id);

      void  on_play_status_changed ();
      void  on_play_position (guint64 position);
      void  on_play_buffering (double buffered);
      void  on_play_error (Glib::ustring const& element, Glib::ustring const& uri, Glib::ustring const& debug, GError * error, GstElement const* src);
      void  on_play_eos ();

      bool  on_seek_event (GdkEvent*);
      bool  on_seek_expose (GdkEventExpose*);

      void  on_source_caps (PlaybackSource::Caps  caps,  PlaybackSourceID source);
      void  on_source_flags (PlaybackSource::Flags flags, PlaybackSourceID source);
      void  on_source_stop (PlaybackSourceID source);
      void  on_source_next (PlaybackSourceID source);
      void  on_source_track_metadata (TrackMetadata const& metadata,
              PlaybackSourceID source, BmpMetadataParseFlags flags = PARSE_FLAGS_ALL);
      void  on_source_segment (PlaybackSourceID source);

      void  on_source_message_set (Glib::ustring const& message);
      void  on_source_message_clear ();
      void  on_source_play_request (PlaybackSourceID source);
      void  on_go_to_mbid (ustring const& mbid);

      bool  source_restore_context (GdkEventButton*);
      void  source_changed (int n);
      void  install_source (PlaybackSourceID source);
      void  reset_seek_range ();

      /////////////// METADATA HANDLING
      void  on_gst_metadata (BmpGstMetadataField field);
      bool  metadata_update_when_idle (BmpMetadataParseFlags flags);
      void  reparse_metadata (BmpMetadataParseFlags flags = PARSE_FLAGS_ALL);
      void  set_current_source_image ();

      /////////////// SIMPLE PROGRESS
      void  progress_start (int n_items, const char * title);
      void  progress_step ();
      void  progress_end (bool wait_for_close);

      /////////////// BOOKMARKS API
      BookmarkManager m_bookmark_manager;
      void  on_bookmark_loaded (Bookmark&, guint64);
      void  on_bookmark_create ();
      void  on_bookmark_activate (std::string const&);
      void  bookmark_append (Bookmark const&, guint64);

      /////////////// STATUSICON/POPUP
      static void on_tray_embedded  (GtkPlug *plug, gpointer userdata);
      static void on_tray_destroyed  (BmpTrayIcon *tray, gpointer userdata);
      static void status_icon_click (BmpStatusIcon *icon, gpointer data);
      static void status_icon_scroll_up (BmpStatusIcon *icon, gpointer data);
      static void status_icon_scroll_down (BmpStatusIcon *icon, gpointer data);
      static void status_icon_popup_menu (BmpStatusIcon *icon, guint arg1, guint arg2, gpointer data);
      static gboolean status_icon_enter (BmpStatusIcon *icon, GdkEventCrossing *event, gpointer data);
      static gboolean status_icon_leave (BmpStatusIcon *icon, GdkEventCrossing *event, gpointer data);
      bool display_popup_delayed ();
      ////////////////////////////////////////

      void  on_shell_lastfm_recm ();
      void  on_shell_lastfm_play ();
      void  on_shell_lastfm_tag ();
      void  on_shell_lastfm_love ();

      void  on_shell_display_preferences ();
      void  on_shell_display_track_details ();
      void  on_shell_display_eq ();

      void  on_shell_toggle_statusbar ();
      void  on_shell_toggle_sources ();
      void  on_shell_toggle_info ();

      void  on_play_files ();
      void  on_enqueue_files ();
      void  on_play_stream ();
      void  on_volume_changed (MCS_CB_DEFAULT_SIGNATURE);
      void  on_enable_eq_changed (MCS_CB_DEFAULT_SIGNATURE);

      ////////////// LAST.FM STUFF
      void  on_last_fm_radio_connected ();
      void  on_last_fm_radio_disconnected ();
      void  on_scrobbler_connected ();
      void  on_scrobbler_disconnected ();
      //////////////////////////////////////

      BmpMetadataParseFlags m_parse_flags;

      Glib::RefPtr<Gtk::ActionGroup>      m_actions;
      Glib::RefPtr<Gdk::Pixbuf>           m_source_icons[N_SOURCES];
      Glib::RefPtr<Gdk::Pixbuf>           m_source_icons_small[N_SOURCES];
      Glib::RefPtr<Gdk::Pixbuf>           m_source_icons_med[N_SOURCES];

      Glib::RefPtr<Gdk::PixbufAnimation>  yy_paused;
      Glib::RefPtr<Gdk::PixbufAnimation>  m_throbber;

      Glib::RefPtr<Gtk::UIManager>        m_ui_manager;
      guint                               m_ui_merge_id, m_ui_merge_id_context, m_ui_merge_id_tray, m_ui_merge_id_lastfm;

      Glib::RefPtr<Gdk::Pixbuf> m_status_icon_image_default;
      Glib::RefPtr<Gdk::Pixbuf> m_status_icon_image_paused;
      Glib::RefPtr<Gdk::Pixbuf> m_status_icon_image_playing;

      Glib::RefPtr<Gdk::Pixbuf> m_audio_default;
      Glib::RefPtr<Gdk::Pixbuf> m_audio_default_64;

      Glib::RefPtr<Gdk::Pixbuf> m_video_default;
      Glib::RefPtr<Gdk::Pixbuf> m_video_default_64;

      SimpleProgress*     m_simple_progress;

      Gtk::Image*         m_yy_image;
      Gtk::Widget*        m_menu_bar;
      Gtk::Notebook*      m_notebook_main;
      Gtk::Statusbar*     m_statusbar;

      Scroll*             m_volume;
      Popup*              m_popup;
      BmpStatusIcon*      m_status_icon;
      AboutDialog*        m_about;
      Preferences*        m_prefs;
      Equalizer*          m_equalizer;
      InfoArea*           m_infoarea;

      //Gtk::Label*         m_time_label;
      //Gtk::HScale*        m_seek_range;
      Gtk::ProgressBar*   m_seek_progress;

      bool  on_info_area_event (GdkEvent* /*ev*/);
      void  message_set (const Glib::ustring&);
      void  message_clear ();

      guint64 m_n_status_messages;
      guint64 m_n_progress_items;
      guint64 m_nth_progress_item;

      bool m_visible;
      bool m_seeking;
      bool m_spm_paused;
      bool m_IgnorePauseToggled;

      enum SeekDirection
      {
        M_SEEK_FORWARD,
        M_SEEK_BACKWARD
      };
      SeekDirection m_seek_direction;
      double m_seek_position;

      sigc::connection m_popup_delay_conn;

      void  status_message_clear ();
      void  check_lastfm_actions ();
      void  play_uris (VUri const&);
      void  enqueue (VUri const&);

      enum grab_type
      {
        NONE = 0,
        SETTINGS_DAEMON,
        X_KEY_GRAB
      };

      DBusGProxy * m_mmkeys_dbusproxy;
      grab_type m_mmkeys_grab_type;

      static void
      media_player_key_pressed (DBusGProxy *proxy,
                                const gchar *application,
                                const gchar *key,
                                gpointer data);

      bool
      window_focus_cb (GdkEventFocus *event);

      void grab_mmkey (int key_code,
                       int mask,
                       GdkWindow *root);
      void grab_mmkey (int key_code,
                       GdkWindow *root);
      void ungrab_mmkeys (GdkWindow *root);
      static GdkFilterReturn
      filter_mmkeys (GdkXEvent *xevent,
                     GdkEvent *event,
                     gpointer data);
      void
      mmkeys_grab (bool grab);

      void
      mmkeys_get_offending_modifiers ();

      guint m_capslock_mask, m_numlock_mask, m_scrolllock_mask;

      void
      mmkeys_activate ();

      void 
      mmkeys_deactivate ();

      void
      on_mm_edit_begin (); 

      void
      on_mm_edit_done (); 

      bool mm_active;
      sigc::connection mWindowFocusConn;

      //// Video
      ::Window
      set_da_window_id ();

      void
      set_video_ar ();

      void
      on_gst_window_geom (int /*width*/, int /*height*/, GValue* /*par*/);

      void
      enable_video_check (std::string const&);


      VideoWindow* mVideoWidget;
      Gtk::Notebook *mMainNotebook;

    public:

#include "exception.hh"

      EXCEPTION(InvalidSourceError)
      EXCEPTION(NoMetadataError)

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

      static PlayerShell*
      create ();

      void
      init ();

      virtual void
      raise ();

      bool  status_push_message_idle (Glib::ustring const& message);
      void  status_push_message (Glib::ustring const& message);
  };
}
#endif
