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

#include "lastfm-parsers.hh"

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

using namespace Glib;
using namespace std;
using namespace Bmp;
using boost::algorithm::split;
using boost::algorithm::is_any_of;
using boost::algorithm::find_nth;
using boost::iterator_range;

#define STATE(e) ((m_state & e) != 0)
#define SET_STATE(e) ((m_state |= e))
#define CLEAR_STATE(e) ((m_state &= ~ e))

namespace
{
  Glib::ustring
  strip_ns (Glib::ustring const& name)
  {
    ::Bmp::StrV subs;
    split (subs, name, is_any_of (":"));

    if (subs.size() == 2)
      return Glib::ustring(subs[1]);
    else
      return Glib::ustring(subs[0]);
  }
}

namespace Bmp
{
  namespace LastFM
  {

    // class LqmParser
    void
    LqmParser::on_start_element  (Glib::Markup::ParseContext & context,
                                  Glib::ustring const& name_,
                                  AttributeMap  const& _props)
    {
      Glib::ustring name (strip_ns (name_));

      if (name == N_LQM)
      {
            SET_STATE(E_LQM);
            return;
      }

      if (name == N_LQM_QUEUE)
      {
            SET_STATE(E_QUEUE);
            return;
      }

      if (name == N_LQM_TRACK)
      {
            SET_STATE(E_TRACK);

            AttributeMap props = _props;

            TrackQueueItem item;
            item.mbid   = props.find ("mbid")->second;
            item.date   = g_ascii_strtoull (props.find ("date")->second.c_str(), NULL, 10);
            item.length = g_ascii_strtoull (props.find ("length")->second.c_str(), NULL, 10);
            item.nr     = g_ascii_strtoull (props.find ("nr")->second.c_str(), NULL, 10);
            item.artist = props["artist"];
            item.track  = props["track"];
            item.album  = props["album"];
            item.source = props["source"];
            item.rating = props["rating"];
            m_queue.push_back (item);
            return;
      }
    }

    void
    LqmParser::on_end_element    (Glib::Markup::ParseContext& context,
                                       Glib::ustring const& name_)
    {
      Glib::ustring name (strip_ns (name_));

      if (name == N_LQM)
        {
          CLEAR_STATE(E_LQM);
          return;
        }

      if (name == N_LQM_QUEUE)
        {
          CLEAR_STATE(E_QUEUE);
          return;
        }

      if (name == N_LQM_TRACK)
        {
          CLEAR_STATE(E_TRACK);
          return;
        }
    }

    void
    LqmParser::check_sanity ()
    {
      if (m_state)
      {
        g_warning (G_STRLOC ": State should be 0, but is %d", m_state);
      }
    }

    namespace WS
    {
      // class ArtistParser
      void
      ArtistParser::on_start_element  (Glib::Markup::ParseContext & context,
                                       Glib::ustring const& element_name,
                                       AttributeMap const& attributes)
      {
        if (element_name == "artist")
        {
          SET_STATE(E_ARTIST);

          m_current = LastFMArtist();

          if (attributes.find ("name") != attributes.end())
          {
            m_current.name = attributes.find ("name")->second;
          }

          if (attributes.find ("count") != attributes.end())
          {
            m_current.count = g_ascii_strtoull (attributes.find ("count")->second.c_str(), NULL, 10); 
          }

          m_current.streamable = (attributes.find ("streamable") != attributes.end()) && (
                            (attributes.find("streamable")->second == "yes") || (attributes.find("streamable")->second == "1"));
        }
        else
        if (element_name == "name")
        {
          SET_STATE(E_NAME);
        }
        else
        if (element_name == "mbid")
        {
          SET_STATE(E_MBID);
        }
        else
        if (element_name == "playcount")
        {
          SET_STATE(E_COUNT);
        }
        else
        if (element_name == "rank")
        {
          SET_STATE(E_RANK);
        }
        else
        if (element_name == "url")
        {
          SET_STATE(E_URL);
        }
        else
        if (element_name == "thumbnail")
        {
          SET_STATE(E_THUMBNAIL);
        }
        else
        if (element_name == "image")
        {
          SET_STATE(E_IMAGE);
        }
        else
        if (element_name == "image_small")
        {
          SET_STATE(E_IMAGE_SMALL);
        }
        else
        if (element_name == "streamable")
        {
          SET_STATE(E_STREAMABLE);
        }
        else
        if (element_name == "match")
        {
          SET_STATE(E_MATCH);
        }
      }

      void
      ArtistParser::on_end_element    (Glib::Markup::ParseContext& context,
                                       Glib::ustring const& element_name)
      {
        if (element_name == "artist")
        {
          CLEAR_STATE(E_ARTIST);
          m_artists.push_back (m_current);
        }
        else
        if (element_name == "name")
        {
          CLEAR_STATE(E_NAME);
        }
        else
        if (element_name == "mbid")
        {
          CLEAR_STATE(E_MBID);
        }
        else
        if (element_name == "playcount")
        {
          CLEAR_STATE(E_COUNT);
        }
        else
        if (element_name == "rank")
        {
          CLEAR_STATE(E_RANK);
        }
        else
        if (element_name == "url")
        {
          CLEAR_STATE(E_URL);
        }
        else
        if (element_name == "thumbnail")
        {
          CLEAR_STATE(E_THUMBNAIL);
        }
        else
        if (element_name == "image")
        {
          CLEAR_STATE(E_IMAGE);
        }
        else
        if (element_name == "image_small")
        {
          CLEAR_STATE(E_IMAGE_SMALL);
        }
        else
        if (element_name == "streamable")
        {
          CLEAR_STATE(E_STREAMABLE);
        }
        else
        if (element_name == "match")
        {
          CLEAR_STATE(E_MATCH);
        }
      }

      void
      ArtistParser::on_text  (Glib::Markup::ParseContext & context,
                              Glib::ustring const& text)
      {
        if (STATE(E_ARTIST))
        {
          if (STATE(E_NAME))
          {
            m_current.name = text;
          }
          else
          if (STATE(E_MBID))
          {
            m_current.mbid = text;
          }
          else
          if (STATE(E_COUNT))
          {
            m_current.count = g_ascii_strtoull (text.c_str(), NULL, 10); 
          }
          else
          if (STATE(E_RANK))
          {
            m_current.rank = g_ascii_strtoull (text.c_str(), NULL, 10); 
          }
          else
          if (STATE(E_URL))
          {
            m_current.url = text;
          }
          else
          if (STATE(E_THUMBNAIL))
          {
            m_current.thumbnail = text;
          }
          else
          if (STATE(E_IMAGE))
          {
            m_current.image = text;
          }
          else
          if (STATE(E_IMAGE_SMALL))
          {
            m_current.thumbnail = text;
          }
          else
          if (STATE(E_STREAMABLE))
          {
            m_current.streamable = bool (g_ascii_strtoull (text.c_str(), NULL, 10));
          }
          else
          if (STATE(E_MATCH))
          {
            m_current.count = 100 * strtod (text.c_str(), NULL);
          }
        }
      }

      // class TagParser
      void
      TagParser::on_start_element  (Glib::Markup::ParseContext & context,
                                    Glib::ustring const& element_name,
                                    AttributeMap const& attributes)
      {
        if (element_name == "tag")
        {
          SET_STATE(E_TAG);
          m_current = Tag();
        }
        else
        if (element_name == "name")
        {
          SET_STATE(E_NAME);
        }
        else
        if (element_name == "count")
        {
          SET_STATE(E_COUNT);
        }
        else
        if (element_name == "url")
        {
          SET_STATE(E_URL);
        }
      }

      void
      TagParser::on_end_element    (Glib::Markup::ParseContext& context,
                                    Glib::ustring const& element_name)
      {
        if (element_name == "tag")
        {
          CLEAR_STATE(E_TAG);
          m_tags.push_back (m_current);
        }
        else
        if (element_name == "name")
        {
          CLEAR_STATE(E_NAME);
        }
        else
        if (element_name == "count")
        {
          CLEAR_STATE(E_COUNT);
        }
        else
        if (element_name == "url")
        {
          CLEAR_STATE(E_URL);
        }
      }

      void
      TagParser::on_text  (Glib::Markup::ParseContext & context,
                           Glib::ustring const& text)
      {
        if (STATE(E_TAG))
        {
          if (STATE(E_NAME))
          {
            m_current.name = text;
            return;
          }

          if (STATE(E_COUNT))
          {
            m_current.count = g_ascii_strtoull (text.c_str(), NULL, 10); 
            return;
          }

          if (STATE(E_URL))
          {
            m_current.url = text;
            return;
          }
        }
      }

      // class UserParser
      void
      UserParser::on_start_element  (Glib::Markup::ParseContext& context,
                                     Glib::ustring const& element_name,
                                     AttributeMap const& attributes)
      {
        if (element_name == "user")
        {
          SET_STATE(E_USER);
          m_current = User();
          m_current.username = attributes.find ("username")->second;
        }
        else 
        if (STATE(E_USER))
        {
          if (element_name == "url")
          {
            SET_STATE(E_URL);
          }
          else
          if (element_name == "image")
          {
            SET_STATE(E_IMAGE);
          }
          else
          if (element_name == "image")
          {
            SET_STATE(E_IMAGE);
          }
          else
          if (element_name == "match")
          {
            SET_STATE(E_MATCH);
          }
        }
      }

      void
      UserParser::on_end_element    (Glib::Markup::ParseContext& context,
                                     Glib::ustring const& element_name)
      {
          if (element_name == "user")
          {
            CLEAR_STATE(E_USER);
            if (m_state)
            {
              g_warning (G_STRLOC ": State should be 0, but is %d", m_state);
              m_state = 0;
            }
            m_users.push_back (m_current);
          }
          else
          if (element_name == "url")
          {
            CLEAR_STATE(E_URL);
          }
          else
          if (element_name == "image")
          {
            CLEAR_STATE(E_IMAGE);
          }
          else
          if (element_name == "match")
          {
            CLEAR_STATE(E_MATCH);
          }
      }

      void
      UserParser::on_text (Glib::Markup::ParseContext& context, Glib::ustring const& text)
      {
        if (STATE(E_USER))
        {
          if (STATE(E_URL))
          {
            m_current.url = text;
          }
          else
          if (STATE(E_IMAGE))
          {
            m_current.image = text;
          }
          else
          if (STATE(E_MATCH))
          {
            m_current.match = g_strtod (text.c_str(), NULL);
          }
        }
      }

    } // namespace:WS
  } // namespace:LastFM
} // namespace:Bmp
