//  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.

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

#include <gtkmm.h>
#include <gtk/gtk.h>
#include <glibmm.h>
#include <glibmm/i18n.h>
#include <libglademm.h>

#include "network.hh"
#include "paths.hh"
#include "stock.hh"
#include "util.hh"

#include "audio/play.hh"
#include "x_vfs.hh"

#include "ui-part-jamendo.hh"

using namespace Glib;
using namespace Gtk;

#define JAMENDO_ACTION_UI_PART_JAMENDO  "jamendo-action-ui-part-jamendo"
#define JAMENDO_ACTION_UPDATE_DB        "jamendo-action-update-db"

namespace
{
	const Bmp::DB::Variant dummy = double (2);
}

namespace Bmp
{
  namespace
  {
    const char * ui_string_jamendo =
    "<ui>"
    ""
    "<menubar name='MenuBarMain'>"
    "   <placeholder name='PlaceholderSource'>"
    "   <menu action='MenuUiPartJamendo'>"
    "     <menuitem action='" JAMENDO_ACTION_UPDATE_DB "'/>"
    "   </menu>"
    "   </placeholder>"
    "</menubar>"
    ""
    "</ui>";
  }

  namespace UiPart
  {
    Jamendo::Jamendo (RefPtr<Gnome::Glade::Xml> const& xml,
                      RefPtr<Gtk::UIManager>    const& ui_manager)
    : PlaybackSource  (_("Jamendo"),  PlaybackSource::Caps  (PlaybackSource::CAN_PROVIDE_METADATA),
                                      PlaybackSource::Flags (PlaybackSource::F_HANDLE_LASTFM | PlaybackSource::F_ALWAYS_IMAGE_FRAME |
                                                             PlaybackSource::F_HANDLE_LASTFM_ACTIONS)),
      Base (xml, ui_manager)
    {
      m_jamendo = new ::Bmp::Jamendo ();

      xml->get_widget_derived ("jamendo-view-artists", m_artists);
      xml->get_widget_derived ("jamendo-view-albums", m_albums);
      xml->get_widget_derived ("jamendo-playlist", m_tracks);

      m_actions = Gtk::ActionGroup::create ("Actions_UiPartJamendo");

      m_actions->add (Gtk::Action::create ("MenuUiPartJamendo", _("_Jamendo")));
      m_actions->add (Gtk::Action::create (JAMENDO_ACTION_UPDATE_DB,
                                            _("Update Jamendo Database")),
                                            sigc::mem_fun (*this, &UiPart::Jamendo::update_db));

      m_ui_manager->insert_action_group (m_actions);

      m_artists->get_selection()->signal_changed().connect (
        sigc::mem_fun (*this, &Bmp::UiPart::Jamendo::on_artist_selection_changed));

      m_albums->get_selection()->signal_changed().connect (
        sigc::mem_fun (*this, &Bmp::UiPart::Jamendo::on_album_selection_changed));

      m_tracks->get_selection()->signal_changed().connect (
        sigc::mem_fun (*this, &Bmp::UiPart::Jamendo::on_track_selection_changed));

      m_jamendo->update_bundle ();
      update_view ();

      m_artists->set_search_column (m_artists->cr.searchkey);

      m_tracks->activated().connect
        (sigc::mem_fun (mSignalPlayRequest, &SignalPlaybackRequest::emit));
    }

    Jamendo::~Jamendo ()
    {
      delete m_jamendo;
    }

    void
    Jamendo::update_view ()
    {
      m_artists->clear();        
      m_albums->clear();        
      m_tracks->clear();        

      JamendoDataBundle const& bundle = m_jamendo->data_bundle ();   
      for (JamendoData::ArtistV::const_iterator i = bundle.m_artists.begin(); i != bundle.m_artists.end(); ++i)
      {
        TreeIter iter = m_artists->m_store->append();
        (*iter)[m_artists->cr.artist] = (*i); 
        (*iter)[m_artists->cr.searchkey] = i->dispname; 

        m_artists->add_uid (i->id, iter);
      }
    }

    void
    Jamendo::update_db ()
    {
      m_jamendo->db_update ();
      update_view ();
    }

    void
    Jamendo::on_artist_selection_changed ()
    {
      m_tracks->clear();
      m_albums->clear();

      if( m_artists->get_selection()->count_selected_rows() )
      {
            TreeIter iter = m_artists->get_selection()->get_selected();
            JamendoData::Artist a = (*iter)[m_artists->cr.artist]; 
            JamendoData::AlbumV x;
            m_jamendo->get_albums_by_artist (a.id, x);

            for (JamendoData::AlbumV::const_iterator i = x.begin(); i != x.end(); ++i)
            {
                  TreeIter iter = m_albums->m_store->append();
                  (*iter)[m_albums->cr.album] = (*i);
                  m_albums->add_uid (i->id, iter);
            }
            send_caps ();
      }
    }

    void
    Jamendo::on_album_selection_changed ()
    {
      m_albums->set_sensitive (0);
      m_tracks->set_sensitive (0);
      m_tracks->clear();

      if( m_albums->get_selection()->count_selected_rows() )
      {
    	  TreeIter iter = m_albums->get_selection()->get_selected();
    	  JamendoData::Album a = (*iter)[m_albums->cr.album]; 
    	  m_tracks->set_album (*m_jamendo, a);
    	  send_caps ();
      }
      else
      {
    	  m_tracks->clear ();
      }

      m_albums->set_sensitive (1);
      m_tracks->set_sensitive (1);
    }

    void
    Jamendo::on_track_selection_changed ()
    {
      if( m_tracks->get_selection()->count_selected_rows() )
      {
            m_caps = Caps (m_caps | PlaybackSource::CAN_PLAY);

            TreeIter iter;

            iter = m_albums->get_selection()->get_selected();
            m_current_album = ((*iter)[m_albums->cr.album]);

            iter = m_artists->get_selection()->get_selected();
            m_current_artist = ((*iter)[m_artists->cr.artist]);

      }
      else
      {
            m_caps = Caps (m_caps & ~PlaybackSource::CAN_PLAY);
            m_current_track   = JamendoData::Track();
            m_current_album   = JamendoData::Album();
            m_current_artist  = JamendoData::Artist();
      }

      send_caps ();
    }

    //////

    guint
    Jamendo::add_ui ()
    {
      return m_ui_manager->add_ui_from_string (ui_string_jamendo);
    }

    Glib::ustring
    Jamendo::get_uri ()
    {
      static boost::format track_uri_f ("http://www.jamendo.com/get/track/id/track/audio/redirect/%llu/?aue=ogg2");
      ustring uri = (track_uri_f % m_current_track.id).str();
      return uri;
    }

    bool
    Jamendo::go_next ()
    {
      if( m_tracks->has_next () )
      {
          m_tracks->go_next ();
          m_current_track = m_tracks->get_current_track ();
          return true;
      }

      return false;
    }

    bool
    Jamendo::go_prev ()
    {
      if( m_tracks->has_prev () )
      {
          m_tracks->go_prev ();
          m_current_track = m_tracks->get_current_track ();
          return true;
      }

      return false;
    }

    void
    Jamendo::stop ()
    {
      if ( !m_tracks->get_selection()->count_selected_rows())
      {
        m_caps = Caps (m_caps & ~PlaybackSource::CAN_PLAY);
      }

      m_tracks->set_playing_track (0);
      m_tracks->queue_draw ();
      m_caps = Caps (m_caps & ~PlaybackSource::CAN_PAUSE);
      m_caps = Caps (m_caps & ~PlaybackSource::CAN_BOOKMARK);
      send_caps ();
    }

    bool
    Jamendo::play ()
    {
      m_tracks->set_playing_track ();
      m_albums->set_playing_album (m_current_album.id);
      m_current_track = m_tracks->get_current_track ();
      return true;
    }

    void
    Jamendo::play_post ()
    {
      send_metadata ();
      m_caps = Caps (m_caps | PlaybackSource::CAN_PAUSE | PlaybackSource::CAN_PROVIDE_METADATA | PlaybackSource::CAN_BOOKMARK);
    }

    void
    Jamendo::next_post ()
    {
      send_metadata ();
    }

    void
    Jamendo::prev_post ()
    {
      send_metadata ();
    }

    void
    Jamendo::send_metadata () // private
    {
      TrackMetadata metadata;
      metadata.title = m_current_track.dispname;
      metadata.artist = m_current_artist.dispname;
      metadata.album = m_current_album.dispname;
      metadata.genre = m_current_album.genre;
      metadata.image = m_jamendo->get_album_cover (m_current_album.id);
      metadata.duration = m_current_track.length;

      mSignalMetadata.emit (metadata);
    } 

    void
    Jamendo::send_caps () // protected
    {
      if( m_tracks->has_next() )
            m_caps = Caps (m_caps | PlaybackSource::CAN_GO_NEXT);
      else
            m_caps = Caps (m_caps & ~PlaybackSource::CAN_GO_NEXT);

      if( m_tracks->has_prev() )
            m_caps = Caps (m_caps | PlaybackSource::CAN_GO_PREV);
      else
            m_caps = Caps (m_caps & ~PlaybackSource::CAN_GO_PREV);

      mSignalCaps.emit (m_caps);
    }

    void
    Jamendo::restore_context ()
    {
    }

    //// JMBase

    void
    Jamendo::JMBase::clear ()
    {
      m_store->clear();
      m_uid_iter_map.clear();
    }

    void
    Jamendo::JMBase::select_uid (guint64 uid)
    {
      get_selection()->select (m_uid_iter_map.find (uid)->second);
      scroll_to_row (m_store->get_path (m_uid_iter_map.find (uid)->second), 0.5);
      while (gtk_events_pending()) gtk_main_iteration();
    }

    void
    Jamendo::JMBase::add_uid (guint64 uid, TreeIter const& iter)
    {
      m_uid_iter_map.insert (std::make_pair (uid, iter));
    }

    //// JMArtists
    Jamendo::JMArtists::JMArtists (BaseObjectType                       * obj,
                                   Glib::RefPtr<Gnome::Glade::Xml> const& xml)
    : JMBase (obj, xml)
    {
      TreeViewColumn * column = 0;
      {
        column = manage (new TreeViewColumn (_("Artist")));
        CellRendererText * cell = manage (new CellRendererText());
        cell = manage (new CellRendererText());
        cell->property_xalign() = 0.0;
        cell->property_yalign() = 0.5;
        cell->property_xpad() = 4;
        cell->property_ypad() = 2;
        column->pack_start (*cell, true);
        column->set_resizable (false);
        column->set_expand (false);
        column->set_cell_data_func (*cell, sigc::bind (sigc::mem_fun (*this, &Bmp::UiPart::Jamendo::JMArtists::cell_data_func), 0));
        append_column (*column);
      }

      m_store = Gtk::ListStore::create (cr);
      set_model (m_store);
      get_selection()->set_mode (SELECTION_SINGLE);
    }

    void
    Jamendo::JMArtists::cell_data_func (Gtk::CellRenderer * basecell, Gtk::TreeIter const& iter, int cell)
    {
      JamendoData::Artist artist = (*iter)[cr.artist];
      CellRendererText  * cell_t = 0;

      switch (cell)
      {
        case 0:
          cell_t = static_cast<CellRendererText*>(basecell);
          cell_t->property_markup() = Markup::escape_text (artist.dispname);
          break;
      }
    }

    JamendoData::Artist
    Jamendo::JMArtists::get_current_artist ()
    {
      if( get_selection()->count_selected_rows() )
      {
        JamendoData::Artist artist = (*get_selection()->get_selected())[cr.artist];
        return artist;
      }
      return JamendoData::Artist();
    }

    //// JMAlbums
    Jamendo::JMAlbums::JMAlbums (BaseObjectType                       * obj,
                                 Glib::RefPtr<Gnome::Glade::Xml> const& xml)
    : JMBase (obj, xml)
    , m_playing_album (0)
    {
      TreeViewColumn * column = 0;
      {
        column = manage (new TreeViewColumn (_("Album")));
        CellRendererText * cell = manage (new CellRendererText());
        cell = manage (new CellRendererText());
        cell->property_xalign() = 0.0;
        cell->property_yalign() = 0.5;
        cell->property_xpad() = 4;
        cell->property_ypad() = 2;
        column->pack_start (*cell, true);
        column->set_resizable (false);
        column->set_expand (false);
        column->set_cell_data_func (*cell, sigc::bind (sigc::mem_fun (*this, &Bmp::UiPart::Jamendo::JMAlbums::cell_data_func), 0));
        append_column (*column);
      }

      m_store = Gtk::ListStore::create (cr);
      set_model (m_store);
      get_selection()->set_mode (SELECTION_SINGLE);
    }

    void
    Jamendo::JMAlbums::cell_data_func (Gtk::CellRenderer * basecell, Gtk::TreeIter const& iter, int cell)
    {
      JamendoData::Album album = (*iter)[cr.album];
      CellRendererText  * cell_t = 0;

      switch (cell)
      {
        case 0:
          cell_t = static_cast<CellRendererText*>(basecell);
          cell_t->property_markup() = Markup::escape_text (album.dispname);
          break;
      }
    }
    
    void
    Jamendo::JMAlbums::set_playing_album (guint64 playing_album)
    {
      m_playing_album = playing_album;
    }

    JamendoData::Album
    Jamendo::JMAlbums::get_current_album ()
    {
      if( get_selection()->count_selected_rows() )
      {
        JamendoData::Album album = (*get_selection()->get_selected())[cr.album];
        return album;
      }
      return JamendoData::Album(); //FIXME: erh?
    }

    //// JMTracks
    Jamendo::JMTracks::JMTracks (BaseObjectType                       * obj,
                                 Glib::RefPtr<Gnome::Glade::Xml> const& xml)
    : JMBase (obj, xml)
    , m_has_playing   (0)
    , m_playing_track (0)
    {
      m_playing = Gdk::Pixbuf::create_from_file (build_filename (BMP_IMAGE_DIR_STOCK, "play.png"));

      TreeViewColumn * column = 0;
      {
        column = manage (new TreeViewColumn (""));
        CellRendererPixbuf * cell = manage (new CellRendererPixbuf());
        cell->property_width() = 24;
        column->set_fixed_width (24); 
        column->pack_start (*cell, false);
        column->set_cell_data_func (*cell, sigc::bind (sigc::mem_fun (*this, &Bmp::UiPart::Jamendo::JMTracks::cell_data_func), 0));
        append_column (*column);
      }
      {
        column = manage (new TreeViewColumn (_("Track")));
        CellRendererText * cell = manage (new CellRendererText());
        cell->property_xalign() = 1.0;
        column->pack_start (*cell, true);
        column->set_cell_data_func (*cell, sigc::bind (sigc::mem_fun (*this, &Bmp::UiPart::Jamendo::JMTracks::cell_data_func), 1));
        append_column (*column);
      }
      {
        column = manage (new TreeViewColumn (_("Title")));
        CellRendererText * cell = manage (new CellRendererText());
        cell->property_xalign() = 0.0;
        column->pack_start (*cell, true);
        column->set_cell_data_func (*cell, sigc::bind (sigc::mem_fun (*this, &Bmp::UiPart::Jamendo::JMTracks::cell_data_func), 2));
        append_column (*column);
      }
      {
        column = manage (new TreeViewColumn (_("Length")));
        CellRendererText * cell = manage (new CellRendererText());
        cell->property_xalign() = 1.0;
        column->pack_start (*cell, false);
        column->set_cell_data_func (*cell, sigc::bind (sigc::mem_fun (*this, &Bmp::UiPart::Jamendo::JMTracks::cell_data_func), 3));
        append_column (*column);
      }

      m_store = Gtk::ListStore::create (cr);
      set_model (m_store);
      get_selection()->set_mode (SELECTION_SINGLE);
    }

    // Gtk::Widget
    void
    Jamendo::JMTracks::on_row_activated (Gtk::TreeModel::Path const& G_GNUC_UNUSED, Gtk::TreeViewColumn* G_GNUC_UNUSED)
    {
      signal_activated_.emit ();
    }
  
    void
    Jamendo::JMTracks::cell_data_func (Gtk::CellRenderer * basecell, Gtk::TreeIter const& iter, int cell)
    {
      JamendoData::Track x = (*iter)[cr.track];

      CellRendererText    * cell_t = 0;
      CellRendererPixbuf  * cell_p = 0;

      switch (cell)
      {
        case 0:
          cell_p = static_cast<CellRendererPixbuf*>(basecell);
          break;

        case 1:
        case 2:
        case 3:
          cell_t = static_cast<CellRendererText*>(basecell);
          break;
      }

      switch (cell)
      {
        case 0:
          if( x.id == m_playing_track )
                cell_p->property_pixbuf() = m_playing; 
          else
                cell_p->property_pixbuf() = Glib::RefPtr<Gdk::Pixbuf>(0); 
          break;

        case 1:
          cell_t->property_text() = (boost::format ("%llu") % x.trackno).str(); 
          break;

        case 2:
          cell_t->property_markup() = Markup::escape_text (x.dispname);
          break;

        case 3:
          cell_t->property_text() = (boost::format ("%d:%02d") % (x.length / 60) % (x.length % 60)).str();
          break;
      }
    }

    void
    Jamendo::JMTracks::clear ()
    {
      m_has_playing = false;
      JMBase::clear ();
    }

    void
    Jamendo::JMTracks::set_album (::Bmp::Jamendo & jamendo, JamendoData::Album const& a)
    {
      unset_model ();

      JamendoData::TrackV x;
      jamendo.get_tracks_by_album (a.id, x);

                  m_has_playing = false;

      clear ();

      for (JamendoData::TrackV::const_iterator i = x.begin(); i != x.end(); ++i)
      {
            TreeIter iter = m_store->append();
            (*iter)[cr.track] = (*i);
            add_uid (i->id, iter);
        
            if (i->id == m_playing_track) 
            {
                  m_has_playing = true;
                  m_playing_iter = iter;
            }
      }
      set_model (m_store);
    }

    JamendoData::Track
    Jamendo::JMTracks::get_current_track () const
    {
      return JamendoData::Track ((*m_playing_iter)[cr.track]);
    }

    void
    Jamendo::JMTracks::set_playing_track (guint64 playing_track)
    {
      m_playing_track = playing_track;
    }

    void
    Jamendo::JMTracks::set_playing_track ()
    {
      g_return_if_fail (get_selection()->count_selected_rows());
   
      TreeIter iter = get_selection()->get_selected(); 
      assign_current_track (iter);
    }

    bool
    Jamendo::JMTracks::has_next ()
    {
      return (m_playing_iter && m_playing_track && m_has_playing && 
                (TreeNodeChildren::size_type( m_store->get_path (m_playing_iter).get_indices().data()[0] ) < (m_store->children().size()-1)));
    }

    bool  
    Jamendo::JMTracks::has_prev ()
    {
      return (m_playing_iter && m_playing_track && m_has_playing && (m_store->get_path (m_playing_iter).get_indices().data()[0] > 0)); 
    }

    void  
    Jamendo::JMTracks::go_next ()
    {
      assign_current_track (++m_playing_iter);
    }

    void  
    Jamendo::JMTracks::go_prev ()
    {
      assign_current_track (--m_playing_iter);
    }

    void
    Jamendo::JMTracks::assign_current_track (TreeIter const& iter) // private
    {
      JamendoData::Track track ((*iter)[cr.track]);
      m_playing_iter = iter;
      m_playing_track = track.id;
      m_has_playing = true;
      queue_draw ();
    }

    // BookmarkHandler
    bool 
    Jamendo::get_bookmark (ustring const& id, /* the id to be used as the hostname part, but otherwise ignored */
                           ustring & url,
                           ustring & title)
    {
      static boost::format url_f ("ibs://%s/?artist_id=%llu&album_id=%llu");
      static boost::format title_f ("%s - %s");

      JamendoData::Album album = m_albums->get_current_album ();
      JamendoData::Artist artist = m_artists->get_current_artist ();
      url = (url_f % id % artist.id % album.id).str();
      title = (title_f % artist.dispname.c_str() % album.dispname.c_str()).str();
      return true;
    }

    bool 
    Jamendo::set_bookmark (ustring const& url)
    {
      Bmp::URI u (url);
      Bmp::URI::Query q;
      u.parse_query (q);

      guint64 artist_id = g_ascii_strtoull (q.find ("artist_id")->second.second.c_str(), NULL, 10);
      guint64 album_id = g_ascii_strtoull (q.find ("album_id")->second.second.c_str(), NULL, 10);

      mSignalStopRequest.emit ();

      m_artists->select_uid (artist_id);
      m_albums->select_uid (album_id);
      m_tracks->get_selection()->select (TreePath (TreePath::size_type (1),TreePath::value_type(0)));

      mSignalPlayRequest.emit ();

      return true;
    }

  } // UiPart
} // Bmp
