//-*- Mode: C++; indent-tabs-mode: nil; -*-

//  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 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 <gtk/gtk.h>
#include <gtkmm.h>
#include <glibmm.h>
#include <glibmm/i18n.h>

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <iostream>
#include <fstream>
#include <vector>

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <strings.h>

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

#include "main.hh"
#include "paths.hh"
#include "stock.hh"
#include "util.hh"
#include "core.hh"

#include "lastfm.hh"
#include "lastfm-tag-dialog.hh"

using namespace Gtk;
using namespace Glib;
using namespace Markup;
using namespace std;
using namespace Bmp;
using namespace LastFM;
using namespace LastFM::XMLRPC;
using namespace LastFM::WS;
using boost::algorithm::trim;
using boost::algorithm::split;
using boost::algorithm::split_regex;
using boost::algorithm::is_any_of;
using boost::algorithm::find_nth;
using boost::iterator_range;

namespace
{
  enum TagSelection
  {
    T_TRACK,
    T_ARTIST,
    T_ALBUM
  };
}

namespace Bmp
{
  namespace LastFM
  {
      TagDialog*
      TagDialog::create ()
      {
        const std::string path (build_filename (BMP_GLADE_DIR, "dialog-lastfm-tag.glade"));
        RefPtr<Gnome::Glade::Xml> glade_xml = Gnome::Glade::Xml::create (path);
        TagDialog *i = 0;
        glade_xml->get_widget_derived ("tag", i);
        return i;
      }

      TagDialog::~TagDialog ()
      {}

      TagDialog::TagDialog (BaseObjectType*                  obj,
                            RefPtr<Gnome::Glade::Xml> const& xml)
      : Gtk::Dialog (obj)
      , m_ref_xml   (xml)
      , m_selection_clear_block (0)
      {
        dynamic_cast <Gtk::Image *>(m_ref_xml->get_widget ("image"))->set
            (build_filename (BMP_IMAGE_DIR, BMP_THROBBER));

        m_ref_xml->get_widget ("entry_artist", m_entry_artist);
        m_ref_xml->get_widget ("entry_album",  m_entry_album);
        m_ref_xml->get_widget ("entry_title",  m_entry_title);

        m_ref_xml->get_widget ("label_artist", m_label_artist);
        m_ref_xml->get_widget ("label_album",  m_label_album);
        m_ref_xml->get_widget ("label_title",  m_label_title);

        m_ref_xml->get_widget ("notebook", m_notebook);
        m_notebook->set_current_page (0);

        m_ref_xml->get_widget ("treeview", m_tag_view);
        {
          CellRendererText *cell_text = Gtk::manage (new Gtk::CellRendererText ());
          m_tag_view->append_column ("", *cell_text);
          m_tag_view->get_column (0)->add_attribute (*cell_text, "text", 0);
          m_tag_view->get_column (0)->set_resizable (false);
        }
        m_tag_view->get_selection()->signal_changed().connect (sigc::mem_fun (*this, &LastFM::TagDialog::on_tag_selected));

        m_ref_xml->get_widget ("tag_name", m_tag_name);
        m_tag_name->signal_changed().connect (sigc::mem_fun (*this, &LastFM::TagDialog::on_tag_name_modified));

        m_ref_xml->get_widget ("tag_cbox", m_tag_cbox);
        m_tag_cbox->signal_changed().connect (sigc::mem_fun (*this, &LastFM::TagDialog::on_tag_cbox_changed));

        m_ref_xml->get_widget ("cover_image", m_cover);
      }

      void
      TagDialog::run (TrackMetadata const& item)
      {
        m_item = item;

        if (m_item.image)
          m_cover->set (m_item.image->scale_simple (64, 64, Gdk::INTERP_BILINEAR));
        else
          m_cover->set (Gdk::Pixbuf::create_from_file (build_filename (BMP_IMAGE_DIR, BMP_COVER_IMAGE_DEFAULT))->
          scale_simple (64, 64, Gdk::INTERP_BILINEAR));

        if (m_item.artist)
        {
          trim (m_item.artist.get());
          m_entry_artist->set_text (m_item.artist.get());
        }

        if (item.album)
        {
          trim (m_item.album.get());
          m_entry_album->set_text (m_item.album.get());
        }

        if (item.title)
        {
          trim (m_item.title.get());
          m_entry_title->set_text (m_item.title.get());
        }

        if ((!m_item.album) || (m_item.album && m_item.album.get().empty()))
        {
          // Q&D
          Glib::RefPtr<TreeModel> model = m_tag_cbox->get_model();
          TreePath path (TreePath::size_type (1), TreePath::value_type (2));
          gtk_list_store_remove ((GtkListStore*)(model->gobj()), const_cast<GtkTreeIter*>(model->get_iter (path)->gobj())); 
        }

        show ();
        update_view ();
        m_tag_name->grab_focus ();
        if (Dialog::run() == Gtk::RESPONSE_OK)
        {
          try {
            tag ();
            }
          catch (LastFM::Exception& cxe)
          {
            MessageDialog dialog (cxe.what(), false, MESSAGE_ERROR, BUTTONS_OK, true);
            dialog.run ();
          }
        }

        hide ();
      }

      void
      TagDialog::update_view ()
      {
        m_notebook->set_current_page (1);

        m_tag_store = ListStore::create (cr);
        m_tag_store->set_default_sort_func (sigc::mem_fun (*this, &LastFM::TagDialog::sort_func));
        m_tag_store->set_sort_column (Gtk::TreeSortable::DEFAULT_SORT_COLUMN_ID, Gtk::SORT_ASCENDING);

        using namespace LastFM::WS;
  
        TagV v, vdest;
        tags_glob (TAGS_GLOBAL_TRACK,  v, m_item.artist.get(), m_item.title.get());

        typedef std::set<ustring> Keeper;
        Keeper k;

        for (TagV::const_iterator i = v.begin(); i != v.end(); ++i)
        {
          if (k.find (i->name) == k.end())
          {
            vdest.push_back (*i);  
            k.insert (i->name);
          }
        }

        std::sort (vdest.begin(), vdest.end());

        for (TagV::const_iterator i = v.begin(); i != v.end(); ++i)
        {
          TreeIter iter = m_tag_store->append ();
          (*iter)[cr.name] = i->name;
        }

        m_tag_view->set_model (m_tag_store);
        m_tag_cbox->set_active (0);
        m_notebook->set_current_page (0);
      }

      int
      TagDialog::sort_func (Gtk::TreeModel::iterator const& m_iter1,
                            Gtk::TreeModel::iterator const& m_iter2)
      {
        ustring name1 ((*m_iter1)[cr.name]);
        ustring name2 ((*m_iter2)[cr.name]);

        return name1.compare (name2);
      }

      void
      TagDialog::on_tag_cbox_changed ()
      {
        int current = m_tag_cbox->get_active_row_number();

        // check whether album is empty; it's a possible case
        if (current == T_ALBUM)
        {
          set_response_sensitive (Gtk::RESPONSE_OK, bool (m_item.album));
        }

        m_entry_artist->set_sensitive (false);
        m_entry_album->set_sensitive (false);
        m_entry_title->set_sensitive (false);
        m_label_artist->set_sensitive (false);
        m_label_album->set_sensitive (false);
        m_label_title->set_sensitive (false);

        switch (current)
        {
          case T_ARTIST:
          {
            m_entry_artist->set_sensitive (true);
            m_label_artist->set_sensitive (true);
            break;
          }

          case T_ALBUM:
          {
            m_entry_artist->set_sensitive (true);
            m_label_artist->set_sensitive (true);
            m_entry_album->set_sensitive (true);
            m_label_album->set_sensitive (true);
            break;
          }

          case T_TRACK:
          {
            m_entry_artist->set_sensitive (true);
            m_label_artist->set_sensitive (true);
            m_entry_title->set_sensitive (true);
            m_label_title->set_sensitive (true);
            break;
          }
        }
      }

      void
      TagDialog::on_tag_selected ()
      {
        if (m_tag_view->get_selection()->count_selected_rows())
        {
          m_selection_clear_block = 1;
          TreeIter iter = m_tag_view->get_selection()->get_selected();
          m_tag_name->set_text (ustring ((*iter)[cr.name]));
        }
      }

      void
      TagDialog::on_tag_name_modified ()
      {
        if (!m_selection_clear_block)
        {
          m_tag_view->get_selection()->unselect_all ();
        }
        else
        {
          m_selection_clear_block = 0;
        }
      }

      void
      TagDialog::tag ()
      {
        using namespace Bmp::LastFM::XMLRPC;

        if (!m_tag_name->get_text().size())
          return;

        ustring method;
        switch (m_tag_cbox->get_active_row_number())
        {
          case T_ARTIST:
            method = "tagArtist";
            break;

          case T_ALBUM:
            method = "tagAlbum";
            break;

          case T_TRACK:
            method = "tagTrack";
            break;
        }

        UStrV v;
        v.push_back (m_tag_name->get_text());

        XSPF::Item item;
        item.creator = (m_item.artist ? m_item.artist.get() : std::string());
        item.album = (m_item.album ? m_item.album.get() : std::string());
        item.title = (m_item.title ? m_item.title.get() : std::string());

        TagAction action (method, item, v); 
        action.run ();
      }
  }
}
