//  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 <iostream>
#include <sstream>

#include <libxml/tree.h>
#include <libxml/parser.h>

#include <boost/format.hpp>
#include <boost/iostreams/filtering_streambuf.hpp>
#include <boost/iostreams/copy.hpp>
#include <boost/iostreams/filter/gzip.hpp>

#include <glibmm/ustring.h>
#include <glibmm/i18n.h>
#include <glib/gstdio.h>

#include "json/value.h"
#include "json/reader.h"

#include "bmp/types/types-xspf.hh"
#include "xspf-libxml2-sax.hh"

#include "jamendo.hh"
#include "jamendodata-libxml2-sax.hh"

#include "minisoup.hh"

#include "paths.hh"
#include "util.hh"
#include "uri.hh"

#include "dialog-simple-progress.hh"
using namespace Glib;
using namespace Bmp::DB;

namespace
{
  const std::string jamendo_db_dump = "http://img.jamendo.com/data/dbdump.en.xml.gz";
}

namespace Bmp
{
  Jamendo::Jamendo ()
  : m_db                (0)
  , m_have_cover_cache  (true)
  {
    if (!file_test (build_filename (BMP_PATH_CACHE_DIR, "covers-jamendo"), FILE_TEST_EXISTS))
    {
      if (g_mkdir (build_filename (BMP_PATH_CACHE_DIR, "covers-jamendo").c_str(), 0700))
      {
        m_have_cover_cache = false;
      }
    }

    static boost::format library_f ("jamendo-library-%d-%d-%d");
    m_db = new Database ("jamendo-library", BMP_PATH_DATA_DIR, DB_OPEN);

    m_db->exec_sql ("CREATE TABLE IF NOT EXISTS artist (name TEXT, id INTEGER, link TEXT, dispname TEXT, genre TEXT, description TEXT, image TEXT);");
    m_db->exec_sql ("CREATE TABLE IF NOT EXISTS album  (name TEXT, id INTEGER, link TEXT, pubdate TEXT, releasedate TEXT, artist_j INTEGER);");
    m_db->exec_sql ("CREATE TABLE IF NOT EXISTS track  (name TEXT, id INTEGER, link TEXT, lyrics TEXT, tracknumber INTEGER, length INTEGER, album_j INTEGER);");
  }

  Jamendo::~Jamendo ()
  {
    if (m_db)
    {
      delete m_db;
    }
  }

  JamendoDataBundle const&
  Jamendo::data_bundle ()
  {
    return m_bundle;
  }

  void
  Jamendo::get_tracks_by_album (guint64 album_id, JamendoData::TrackV & x)
  {
    RowV v;
    m_db->get (v, (boost::format ("SELECT DISTINCT * FROM track WHERE album_j = '%llu' ORDER BY tracknumber;") % album_id).str());

    for (RowV::const_iterator r = v.begin(); r != v.end(); ++r)
    {
      Row const &i = (*r); 
      JamendoData::Track a;

      if (i.count ("name"))
        a.dispname = boost::get<std::string> (i.find ("name")->second);

      if (i.count ("id"))
        a.id = boost::get<guint64> (i.find ("id")->second);

      if (i.count ("link"))
        a.weblink = boost::get<std::string> (i.find ("link")->second);


      if (i.count ("tracknumber"))
        a.trackno = boost::get<guint64> (i.find ("tracknumber")->second);

      if (i.count ("length"))
        a.length = boost::get<guint64> (i.find ("length")->second);

      if (i.count ("album_j"))
        a.album_id = boost::get<guint64> (i.find ("album_j")->second);

      x.push_back (a);
    }
  }

  void
  Jamendo::get_albums_by_artist (guint64 artist_id, JamendoData::AlbumV & x)
  {
    RowV v;
    m_db->get (v, (boost::format ("SELECT DISTINCT * FROM album WHERE artist_j = '%llu' ORDER BY name;") % artist_id).str());

    for (RowV::const_iterator r = v.begin(); r != v.end(); ++r)
    {
      Row const &i = (*r); 
      JamendoData::Album a;

      if (i.count ("name"))
        a.dispname = boost::get<std::string> (i.find ("name")->second);

      if (i.count ("id"))
        a.id = boost::get<guint64> (i.find ("id")->second);

      if (i.count ("link"))
        a.weblink = boost::get<std::string> (i.find ("link")->second);


      if (i.count ("genre"))
        a.genre = boost::get<std::string> (i.find ("genre")->second);

      if (i.count ("pubdate"))
        a.pubdate = boost::get<std::string> (i.find ("pubdate")->second);

      if (i.count ("releasedate"))
        a.releasedate = boost::get<std::string> (i.find ("releasedate")->second);

      if (i.count ("artist_j"))
        a.artist_id = boost::get<guint64> (i.find ("artist_j")->second);

      x.push_back (a);
    }
  }

  void
  Jamendo::update_bundle (SimpleProgress * d)
  {
    if (d)
    {
      d->set_label (_("Parsing Artists..."));
      d->percentage (0);
    }

    m_bundle = JamendoDataBundle();
    RowV v; 

    m_db->get (v, "SELECT DISTINCT * FROM artist ORDER BY dispname;");
    int n = 0;
    for (RowV::iterator i = v.begin(); i != v.end(); ++i, ++n)
    {
      JamendoData::Artist a;

      if (i->count ("name"))
        a.name = boost::get<std::string> (i->find ("name")->second);

      if (i->count ("link"))
        a.weblink = boost::get<std::string> (i->find ("link")->second);

      if (i->count ("dispname"))
        a.dispname = boost::get<std::string> (i->find ("dispname")->second);

      if (i->count ("genre"))
        a.genre = boost::get<std::string> (i->find ("genre")->second);

      if (i->count ("description"))
        a.description = boost::get<std::string> (i->find ("description")->second);

      if (i->count ("image"))
        a.image_url = boost::get<std::string> (i->find ("image")->second);

      if (i->count ("id"))
        a.id = boost::get<guint64> (i->find ("id")->second);

      m_bundle.m_artists.push_back (a);

      if (d && !(n % 10))
      {
        d->percentage (double (std::distance (v.begin(), i)) / v.size());
      }
    }

  }

  void
  Jamendo::db_update ()
  {
    using namespace boost::iostreams;
  
    try{
      m_db->exec_sql ("DELETE FROM artist; DELETE FROM album; DELETE FROM track;");
    } catch (...) {} // db might be empty

    SimpleProgress * d = SimpleProgress::create ();
    d->set_label (_("Downloading Jamendo Database..."));
    d->present ();

    Soup::RequestSyncRefP request = Soup::RequestSync::create (jamendo_db_dump);  
    request->progress().connect (sigc::mem_fun (*d, &Bmp::SimpleProgress::percentage));
    request->add_header ("User-Agent", "BMP-2.0");

    guint code = request->run ();
    if (code == 200)
    {
      std::string data;
      request->get_data (data); 

      d->set_label (_("Decompressing..."));
      d->step (4, 1);

      std::stringstream ss_in, ss_out;
      ss_in << data;

      try{
          filtering_streambuf<input> in;
          in.push(gzip_decompressor());
          in.push(ss_in);
          copy(in, ss_out);
      }
      catch (...)
      {
          d->set_label (_("Error Decompressing Jamendo database"));
          d->done ();
          delete d;
          return;
      }


      d->set_label (_("Parsing..."));
      d->step (4, 2);

      try{
        JamendoData::parse (*m_db, ss_out.str());
        }
      catch (...)
        {
          d->set_label (_("Error Parsing Jamendo database"));
          d->done ();
          delete d;
          return;
        }

      d->set_label (_("Reading Database..."));
      d->step (4, 3);

      update_bundle (d);

      d->set_label (_("Done."));
      d->step (4, 4);
    }
    else
    {
      d->set_label (_("Error downloading Jamendo Database (database file could not be retrieved)"));
    }

    d->done ();
    delete d;
  }

  RefPixbuf 
  Jamendo::get_album_cover (ID id)
  {
    static boost::format url_f ("http://www.jamendo.com/get/album/id/album/artworkurl/redirect/%d/?artwork_size=400");
    std::string filename = build_filename (build_filename (BMP_PATH_CACHE_DIR, "covers-jamendo"), (boost::format ("cover-%d.jpg") % id).str());

    if (file_test (filename, FILE_TEST_EXISTS) && m_have_cover_cache)
    {
      return Util::get_image_from_uri (filename_to_uri (filename));
    }
    else
    {
      RefPixbuf image = Util::get_image_from_uri ((url_f % id).str());

      if (m_have_cover_cache)
      {
        image->save (filename, "jpeg");
      }

      return image;
    }
  }
}
