//  BMP
//  Copyright (C) 2005-2008 BMP development.
//
//  This program is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License Version 2
//  as published by the Free Software Foundation.
//
//  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 <glibmm.h>
#include <glibmm/i18n.h>
#include <gtkmm.h>
#include <libglademm.h>
#include <glade/glade.h>
#include <glib/gstdio.h>

#include <cstring>
#include <iostream>
#include <sstream>
#include <string>

#include <boost/format.hpp>
#include <boost/optional.hpp>
#include <boost/algorithm/string.hpp>

#include "dialog-track-details.hh"

#include "database.hh"
#include "lastfm.hh"
#include "main.hh"
#include "network.hh"
#include "paths.hh"
#include "playbacksource.hh"
#include "stock.hh"
#include "ui-tools.hh"
#include "util.hh"
#include "uri.hh"

#include "amazon.hh"
#include "x_library.hh"
#include "x_mcsbind.hh"

#include "musicbrainz/mb-utils.hh"

using namespace Glib;
using namespace Gtk;
using namespace Bmp;
using namespace DB;
using namespace std; 

namespace
{
  char const* ui_string_track_details =
  "<ui>"
  "<menubar name='popup-details'>"
  "   <menu action='dummy' name='menu-details'>"
  "     <menuitem action='action-open'/>"
  "     <menuitem action='action-copy'/>"
  "   </menu>"
  "</menubar>"
  "</ui>";
}
 
namespace Bmp
{
  typedef RefPtr<TextTag> RefTextTag;
  typedef std::vector<RefTextTag> TagV;

  class LinkTag
    : public TextTag
  {
    private:

      LinkTag (ustring const& link)
      : ObjectBase (typeid(LinkTag))
      , TextTag ()
      , m_link (link)
      {
        property_foreground() = "#0000ff"; 
      }

    public:

      static RefPtr<LinkTag> create (ustring const& link)
      {
        return RefPtr<LinkTag> (new LinkTag (link)); 
      }

      virtual ~LinkTag () {}

    public:

      typedef sigc::signal<void, ustring const&> SignalUrlActivated;
      typedef sigc::signal<void, ustring const&, guint, guint32> SignalPopupMenu;
    
    private:

      SignalUrlActivated signal_url_activated_;
      SignalPopupMenu    signal_popup_menu_;

    public:

      SignalUrlActivated&
      signal_url_activated ()
      {
        return signal_url_activated_;
      }

      SignalPopupMenu&
      signal_popup_menu ()
      {
        return signal_popup_menu_;
      }

    protected:

      virtual bool
      on_event (RefPtr<Object> const& event_object, GdkEvent* event, TextIter const& iter) 
      {
        if (event->type == GDK_BUTTON_PRESS)
        {
              GdkEventButton * ev = (GdkEventButton*)(event);
              if( ev->button == 1 )
              {
                    return true;
              }
              else if( ev->button == 3 )
              {
                    signal_popup_menu_.emit (m_link, ev->button, ev->time);
                    return true;
                }
        }
        if (event->type == GDK_BUTTON_RELEASE)
        {
              GdkEventButton * ev = (GdkEventButton*)(event);
              if (ev->button == 1)
              { 
                    signal_url_activated_.emit (m_link);
                    return true;
              }
        }
        return false;
      }

    private:
    
      ustring m_link;
  };

  class LastFmWikiView
    : public TextView
  {
      RefTextTag  m_current_tag;
      bool        m_hand;

    public:

      LastFmWikiView (BaseObjectType*                  obj,
                     RefPtr<Gnome::Glade::Xml> const& xml)
      : TextView  (obj) 
      , m_hand    (0) 
      { 
        gtk_widget_add_events (GTK_WIDGET (gobj()), 
                               GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_LEAVE_NOTIFY_MASK);
      }

      virtual ~LastFmWikiView ()
      {}

    protected:

      virtual bool on_motion_notify_event (GdkEventMotion * event)
      {
        int x, y;
        int x_orig, y_orig;
        GdkModifierType state;

        if (event->is_hint)
        {
          gdk_window_get_pointer (event->window, &x_orig, &y_orig, &state);
        }
        else
        {
          x_orig = int (event->x);
          y_orig = int (event->y);
          state = GdkModifierType (event->state);
        }

        window_to_buffer_coords (TEXT_WINDOW_WIDGET, x_orig, y_orig, x, y);

        RefPtr<TextBuffer> buf = get_buffer();
        TextBuffer::iterator iter;
        get_iter_at_location (iter, x, y);      
      
        TagV tags = iter.get_tags();
    
        for (TagV::const_iterator i = tags.begin(); i != tags.end(); ++i) 
        {
          RefPtr<LinkTag> tag = RefPtr<LinkTag>::cast_dynamic (*i);
          if (tag)
          {
            GdkWindow * window = GDK_WINDOW (gtk_text_view_get_window (GTK_TEXT_VIEW (gobj()), GTK_TEXT_WINDOW_TEXT));
            GdkCursor * cursor = gdk_cursor_new_from_name (gdk_display_get_default (), "hand2");
            if (!cursor)
            {
              cursor = gdk_cursor_new_for_display (gdk_display_get_default (), GDK_HAND2);
            }
            gdk_window_set_cursor (window, cursor);
            m_hand = 1;
            return false;
          }
        }
 
        if (m_hand) 
        {
          GdkWindow * window = GDK_WINDOW (gtk_text_view_get_window (GTK_TEXT_VIEW (gobj()), GTK_TEXT_WINDOW_TEXT));
          GdkCursor * cursor = gdk_cursor_new_from_name (gdk_display_get_default (), "xterm");
          if (!cursor)
          {
            cursor = gdk_cursor_new_for_display (gdk_display_get_default (), GDK_XTERM);
          }
          gdk_window_set_cursor (window, cursor);
          m_hand = 0;
       }
    
        return false;
      }
  };
}

namespace Bmp
{
  TrackDetails::TrackDetails (BaseObjectType                 * obj,
                              RefPtr<Gnome::Glade::Xml> const& xml)
  : Window            (obj)
  , m_ref_xml         (xml)
  , m_lyrics_request  (LyricWiki::TextRequestRefP (0))
  , m_artist_request  (LastFM::XMLRPC::ArtistMetadataRequestRefPtr (0))
  {
    Util::window_set_icon_list (*this, "player");

    m_ref_xml->get_widget_derived ("textview-artist", m_widget_artist);

    m_ref_xml->get_widget ("image-cover",     m_widget_cover);
    m_ref_xml->get_widget ("textview-lyrics", m_widget_lyrics);
    m_ref_xml->get_widget ("notebook-artist", m_notebook_artist);
    m_ref_xml->get_widget ("notebook-lyrics", m_notebook_lyrics);

    dynamic_cast<Image*>(m_ref_xml->get_widget ("throbber-artist"))->set (build_filename (BMP_IMAGE_DIR, BMP_THROBBER));
    dynamic_cast<Image*>(m_ref_xml->get_widget ("throbber-lyrics"))->set (build_filename (BMP_IMAGE_DIR, BMP_THROBBER));

    dynamic_cast<Button*>(m_ref_xml->get_widget ("button-refresh-lyrics"))->signal_clicked().connect
      (sigc::bind (sigc::mem_fun (*this, &Bmp::TrackDetails::get_lyrics), true));
    dynamic_cast<Button*>(m_ref_xml->get_widget ("button-refresh-artist"))->signal_clicked().connect
      (sigc::mem_fun (*this, &Bmp::TrackDetails::get_artist));

    m_size_group = Gtk::SizeGroup::create (Gtk::SIZE_GROUP_HORIZONTAL);
    m_size_group->add_widget( *m_ref_xml->get_widget( "button-refresh-lyrics" ) );
    m_size_group->add_widget( *m_ref_xml->get_widget( "button-refresh-artist" ) );

    m_cover = Gdk::Pixbuf::create_from_file (build_filename(BMP_IMAGE_DIR, BMP_COVER_IMAGE_DEFAULT))->scale_simple (256, 256, Gdk::INTERP_BILINEAR);

    m_ui_manager = UIManager::create ();
    m_actiongroup = ActionGroup::create ("ActionsTrackDetails");

    m_actiongroup->add (Action::create ("dummy"));

    m_actiongroup->add (Action::create ("action-open",
                                    _("Open Link")),
                                    sigc::mem_fun (*this, &TrackDetails::on_menu_open_uri));

    m_actiongroup->add (Action::create ("action-copy",
                                    Gtk::Stock::COPY,
                                    _("Copy Link Address")),
                                    sigc::mem_fun (*this, &TrackDetails::on_menu_copy_uri));

    m_ui_manager->insert_action_group (m_actiongroup);
    m_ui_manager->add_ui_from_string (ui_string_track_details);

    add_accel_group( m_ui_manager->get_accel_group() );

    m_ui_manager->ensure_update ();
  }

  TrackDetails::~TrackDetails ()
  {
    m_lyrics_request.clear ();
    m_artist_request.clear ();
  }

  TrackDetails*
  TrackDetails::create ()
  {
    const string path (BMP_GLADE_DIR G_DIR_SEPARATOR_S "dialog-track-details.glade");
    RefPtr<Gnome::Glade::Xml> glade_xml = Gnome::Glade::Xml::create (path);
    TrackDetails* dialog = 0;
    glade_xml->get_widget_derived ("dialog-track-details", dialog);
    glade_xml_signal_autoconnect (glade_xml->gobj());
    return dialog;
  }

  void
  TrackDetails::on_menu_open_uri ()
  {
    open_uri( m_link );
  }

  void
  TrackDetails::on_menu_copy_uri ()
  {
    Clipboard::get_for_display (Gdk::Display::get_default())->set_text( m_link );
  }

  void
  TrackDetails::popup_uri_menu (ustring const& link, guint button, guint32 time)
  {
    m_link = link;

    m_ui_manager->ensure_update ();
    Gtk::Menu * menu = dynamic_cast < Gtk::Menu* > (Util::get_popup (m_ui_manager, "/popup-details/menu-details"));
    if (menu) // better safe than screwed
    {
        menu->popup( button, time );
    }
  }

  void
  TrackDetails::open_uri (ustring const& link)
  {
    system (((boost::format ("xdg-open %s &") % link.c_str()).str()).c_str());
  }

  void
  TrackDetails::got_artist (std::string const& metadata, guint code)
  {
    if( code == 200 )
    {
      std::string text = Util::sanitize_lastfm (metadata);
      if( !text.empty() )
      {
          m_widget_artist->get_buffer()->set_text (text);
          RefPtr<LinkTag> tag = LinkTag::create ((boost::format ("http://last.fm/music/%s/+wiki") % URI::escape_string ((*MData.Iter).artist.get())).str());
          tag->signal_url_activated().connect (sigc::mem_fun (*this, &TrackDetails::open_uri));
          tag->signal_popup_menu().connect (sigc::mem_fun (*this, &TrackDetails::popup_uri_menu));
          m_widget_artist->get_buffer()->get_tag_table()->add (tag);
          TextBuffer::iterator iter = m_widget_artist->get_buffer()->end();
          m_widget_artist->get_buffer()->insert_with_tag (iter, _("[Read More]"), tag);
      }
    }

    m_ref_xml->get_widget ("button-refresh-artist")->set_sensitive (1);
    m_notebook_artist->set_current_page (0);
  }

  void
  TrackDetails::get_artist ()
  {
    if ((*MData.Iter).artist)
    {
        m_artist_request.clear ();
        m_ref_xml->get_widget ("button-refresh-artist")->set_sensitive (0);
        m_notebook_artist->set_current_page (1);
        m_artist_request = LastFM::XMLRPC::ArtistMetadataRequest::create ((*MData.Iter).artist.get());
        m_artist_request->reply().connect (sigc::mem_fun (*this, &TrackDetails::got_artist));
        m_artist_request->run ();
    }
  }

  void
  TrackDetails::got_lyrics (std::string const& lyrics, bool have_em)
  {
    if (have_em)
    {
      m_widget_lyrics->get_buffer()->set_text (lyrics);
    }

    m_notebook_lyrics->set_current_page (0);
    m_ref_xml->get_widget ("button-refresh-lyrics")->set_sensitive (1);
  }

  void
  TrackDetails::get_lyrics (bool forced)
  {
    if ((*MData.Iter).artist && (*MData.Iter).title)
    {
        m_lyrics_request.clear ();
        m_notebook_lyrics->set_current_page (1);
        m_ref_xml->get_widget ("button-refresh-lyrics")->set_sensitive (0);
        m_lyrics_request = LyricWiki::TextRequest::create ((*MData.Iter).artist.get(), (*MData.Iter).title.get(), forced);
        m_lyrics_request->lyrics().connect (sigc::mem_fun (*this, &TrackDetails::got_lyrics));
        m_lyrics_request->run ();
    }
  }

  void
  TrackDetails::display (TrackMetadata const& metadata)
  {
    Glib::Mutex::Lock L (MData.Lock);

    MData.V.push_back (metadata);
    MData.Iter = MData.V.begin();

    static boost::format title_f1 ("%s - Track Details (BMP)");
    static boost::format title_f2 ("%s: %s");

    if((*MData.Iter).artist && (*MData.Iter).title)
    {
      set_title ((title_f2 % (*MData.Iter).artist.get() % (*MData.Iter).title.get()).str());
    }
    else
    if((*MData.Iter).title)
    { 
      set_title ((title_f1 % (*MData.Iter).title.get()).str());
    }
    else
    {
      set_title (_("(Unknown Title) - Track Details (BMP)"));
    }

    if ((*MData.Iter).image)
    {
      m_widget_cover->set ((*MData.Iter).image->scale_simple (256, 256, Gdk::INTERP_BILINEAR));
    }
    else if ((*MData.Iter).asin && NM::Obj()->Check_Status())
    {
      try{
        RefPixbuf cover; 
        Amazon::Covers::Obj()->fetch ((*MData.Iter).asin.get(), cover, true);
        m_widget_cover->set (cover->scale_simple (256, 256, Gdk::INTERP_BILINEAR));
      }
      catch (...)
      {
        m_widget_cover->set (m_cover);
      }
    }
    else
    {
        m_widget_cover->set (m_cover);
    }

    get_lyrics (0);
    get_artist ();
   
    show_all (); 
  }
} // namespace Bmp
