//  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 <glib/gstdio.h>
#include <gtkmm.h>
#include <glibmm/i18n.h>
#include <libglademm.h>
#include <boost/format.hpp>
#include "bmp/types/types-library.hh"
#include "dialog-export.hh"
#include "dialog-simple-progress.hh"

#ifdef HAVE_HAL
#include "x_hal.hh"
#endif //HAVE_HAL

#include "x_vfs.hh"

#include "ui-tools.hh"
#include "util-file.hh"

#include "audio/audio.hh"
#include "paths.hh"

using namespace Bmp::Audio;
using namespace Glib;
using namespace Gtk;
using namespace std;

namespace Bmp
{
  ExportDialog*
  ExportDialog::create ()
  {
    char* const path = BMP_GLADE_DIR G_DIR_SEPARATOR_S "dialog-export.glade";
    RefPtr<Gnome::Glade::Xml> xml = Gnome::Glade::Xml::create (path);
    ExportDialog* dialog = 0;
    xml->get_widget_derived ("export_dialog", dialog);
    return dialog;
  }

  void
  ExportDialog::transcode_stream_eos ()
  {
    m_transcode_stream_eos = true;
  }

  void
  ExportDialog::transcode_stream_abort ()
  {
    m_transcode_stream_abort = true;
  }

  void
  ExportDialog::transcode_stream_error (Glib::ustring const& error)
  {
    m_transcode_stream_eos = true;
    m_transcode_stream_error = true;
    m_transcode_error = error;
  }

  void
  ExportDialog::transcode_stream_position (int position, ProcessorFileTranscode* p, SimpleProgress* sp)
  {
    guint64 length (p->prop_length().get_value()); 
    double percentage ((1.*position) / (1.*length));
    sp->percentage (percentage);
  }

  void
  ExportDialog::run (TrackV & v)
  {
#ifdef HAVE_HAL
    if (!hal->is_initialized())
    {
          MessageDialog dialog (_("The HAL subsystem is not initialized. Exporting of files is not possible."),
            true, MESSAGE_ERROR, BUTTONS_OK, true);
          dialog.run ();
          return;
    }
#endif //HAVE_HAL
    
    if (Gtk::Dialog::run() != Gtk::RESPONSE_CANCEL)
    {
          TrackV dest_v;

          if (m_cb_export_audio->get_active())
          {
                if (m_rb_none->get_active())
                {
                      SimpleProgress * sp = SimpleProgress::create ();
                      sp->set_modal (false);
                      sp->set_title (_("Copying Files"));
                      sp->set_label ("");
                      sp->present ();

                      TrackV::size_type n = 0;
                      for (TrackV::const_iterator i = v.begin(); i != v.end(); ++i)
                      {
                            std::string filename_canonical  = i->get_canonical_filename ();
                            std::string filename_target     = build_filename (m_dest_path_entry->get_text(), filename_canonical);
                            std::string filename_origin     = filename_from_uri (i->location.get());

                            try{

                                Util::copy_file (filename_origin, filename_target);

                                Track dest = (*i);
                                dest.location = filename_to_uri (filename_target);
#ifdef HAVE_HAL
                                try{
                                  HAL::Volume const& volume = hal->get_volume_for_uri (i->location.get());
                                  dest.volume_udi = volume.volume_udi;
                                  dest.device_udi = volume.device_udi;
                                  dest.volume_relative_path = filename_from_uri (dest.location.get()).substr (volume.mount_point.length());
                                  dest_v.push_back (dest);
                                  }
                                catch (HAL::NoVolumeForUriError & cxe) { g_warning ("%s: No volume for this URI!", G_STRLOC); continue; }
#else
                                  dest_v.push_back (dest);
#endif //HAVE_HAL

                              }
                            catch (std::ios::failure & cxe)
                              {
                                  MessageDialog dialog ((boost::format ("An error occured during copying of the stream '%s':\n'%s'") %
                                    i->location.get().c_str() % cxe.what()).str(),
                                    true, MESSAGE_ERROR, BUTTONS_OK, true);
                                  dialog.run ();
                              }
                            catch (Glib::ConvertError & cxe) { /* now we're really screwed */ }

                            sp->step (v.size(), ++n);
                      }
                      delete sp;
                }
                else
                {
                      ConversionType  type = ConversionType (-1);
                      std::string     ext;

                      if (m_rb_ogg->get_active())
                      {
                            type = CONV_OGG_VORBIS;
                            ext = "ogg";
                      }
                      else
                      if (m_rb_mp3->get_active())
                      {
                            type = CONV_MPEG1_L3;
                            ext = "mp3";
                      }

                      for (TrackV::const_iterator i = v.begin(); i != v.end(); ++i)
                      {
                            m_transcode_stream_eos    = 0;
                            m_transcode_stream_error  = 0;
                            m_transcode_stream_abort  = 0;

                            std::string filename_canonical  = i->get_canonical_filename (ext);
                            std::string filename_target  = build_filename (m_dest_path_entry->get_text(), filename_canonical);

                            ProcessorFileTranscode * p = new ProcessorFileTranscode (filename_from_uri (i->location.get()), filename_target, type);
                               
                            SimpleProgress * sp = SimpleProgress::create ();
                            sp->set_modal (false);
                            sp->set_title (_("Transcoding File"));
                            sp->set_label (Markup::escape_text (filename_to_utf8 (filename_canonical)));
                            sp->present ();
                            sp->enable_cancel ();

                            p->signal_position().connect
                                  (sigc::bind
                                        (sigc::mem_fun (*this, &Bmp::ExportDialog::transcode_stream_position) /* bind */ , p, sp /* !bind */ ));

                            p->signal_error().connect
                                        (sigc::mem_fun (*this, &Bmp::ExportDialog::transcode_stream_error));

                            p->signal_eos().connect
                                        (sigc::mem_fun (*this, &Bmp::ExportDialog::transcode_stream_eos));

                            sp->signal_cancel().connect
                                        (sigc::mem_fun (*this, &Bmp::ExportDialog::transcode_stream_abort));

                            p->run ();

                            while (!m_transcode_stream_eos && !m_transcode_stream_abort) 
                            {
                              while (gtk_events_pending()) gtk_main_iteration ();
                            }

                            if (m_transcode_stream_abort)
                            {
                              g_unlink (filename_target.c_str());
                              delete sp;
                              delete p;
                              return;
                            }
    
                            if (!m_transcode_stream_error)
                            {
                              try{
                                  Track dest = (*i);
                                  dest.location = filename_to_uri (filename_target);
#ifdef HAVE_HAL
                                  try{
                                    HAL::Volume const& volume = hal->get_volume_for_uri (i->location.get());
                                    dest.volume_udi = volume.volume_udi;
                                    dest.device_udi = volume.device_udi;
                                    dest.volume_relative_path = filename_from_uri (dest.location.get()).substr (volume.mount_point.length());
                                    dest_v.push_back (dest);
                                    }
                                  catch (HAL::NoVolumeForUriError & cxe) { g_warning ("%s: No volume for this URI!", G_STRLOC); continue; }
#else
                                  dest_v.push_back (dest);
#endif //HAVE_HAL
                                }
                              catch (Glib::ConvertError & cxe) { /* now we're really screwed */ }
                            }
                            else
                            {
                              MessageDialog dialog ((boost::format ("An error occured during transcoding of the stream '%s':\n'%s'")
                                % i->location.get().c_str() % m_transcode_error.c_str()).str(),
                                true, MESSAGE_ERROR, BUTTONS_OK, true);
                              dialog.run ();
                            }
    
                            delete sp;
                            delete p;
                      }
                }
          }
          else
          {
            std::swap (dest_v, v);
          }

          if (m_cb_export_playlist->get_active())
          {
                try {
                  vfs->write (dest_v, filename_to_uri (filename_from_utf8 (build_filename (m_dest_path_entry->get_text(), m_filename_entry->get_text()))), m_container);
                  }
                catch (Bmp::VFS::Exception& cxe)
                  {
                    /* FIXME: We need to enhance VFS to check for file permissions, etc */
                  }
          }
    }
  }

  ExportDialog::ExportDialog (BaseObjectType                 * obj,
                              RefPtr<Gnome::Glade::Xml> const& xml)
    : Dialog      (obj)
    , m_ref_xml   (xml)
    , m_transcode_stream_eos    (false)
    , m_transcode_stream_error  (false)
    , m_transcode_stream_abort  (false)
  {
    Util::window_set_icon_list (*this, "player");

    m_ref_xml->get_widget ("button_browse", m_button_browse);
    m_button_browse->signal_clicked().connect
      (sigc::mem_fun (*this, &ExportDialog::on_select_destination_folder));

    m_ref_xml->get_widget ("cb-export-audio", m_cb_export_audio);
    m_cb_export_audio->signal_toggled().connect
      (sigc::mem_fun (*this, &Bmp::ExportDialog::on_export_audio_toggled));

    m_ref_xml->get_widget ("cb-export-playlist", m_cb_export_playlist);
    m_cb_export_playlist->signal_toggled().connect
      (sigc::mem_fun (*this, &Bmp::ExportDialog::on_export_playlist_toggled));

    m_ref_xml->get_widget ("plugins_view", m_tree_view_plugins);
    CellRendererText* cell = manage (new CellRendererText ());
    m_tree_view_plugins->append_column (_("Export As..."), *cell);
    m_tree_view_plugins->get_column (0)->add_attribute (*cell, "text", 0);
    m_tree_view_plugins->signal_cursor_changed().connect
      (sigc::mem_fun (*this, &ExportDialog::on_container_treeview_cursor_changed));

    VFS::ExportDataList containers;
    vfs->get_containers (containers);
    m_list_store_plugins = ListStore::create (m_columns);
    for (VFS::ExportDataList::const_iterator i  = containers.begin (); i != containers.end (); ++i)
    {
      TreeModel::iterator iter (m_list_store_plugins->append ());
      (*iter)[m_columns.desc] = i->description;
      (*iter)[m_columns.extension] = i->extension;
    }
    m_tree_view_plugins->set_model (m_list_store_plugins);

    TreeModel::iterator iter (m_list_store_plugins->children().begin());
    m_tree_view_plugins->get_selection()->select (iter);

    m_ref_xml->get_widget ("filename_entry", m_filename_entry);
    m_filename_entry->set_text ("playlist." + string ((*iter)[m_columns.extension]));

    m_ref_xml->get_widget ("dest_path_entry", m_dest_path_entry);
    m_dest_path_entry->set_text (filename_to_utf8 (get_home_dir()));

    m_ref_xml->get_widget ("filebrowser", m_file_browser);
    m_file_browser->set_current_folder (get_home_dir());

    m_ref_xml->get_widget ("rb-1", m_rb_none);
    m_ref_xml->get_widget ("rb-2", m_rb_ogg);
    m_ref_xml->get_widget ("rb-3", m_rb_mp3);

    ProcessorFileTranscode * p; 

    p = 0;
    try{
      p = new ProcessorFileTranscode (CONV_OGG_VORBIS);
      }
    catch (...) {}

    m_rb_ogg->set_sensitive (p != 0);
    if (p) delete p;
     
 
    p = 0;
    try{
      p = new ProcessorFileTranscode (CONV_MPEG1_L3);
      }
    catch (...) {}

    m_rb_mp3->set_sensitive (p != 0);
    if (p) delete p;


    if (!m_rb_mp3->is_sensitive() && !m_rb_ogg->is_sensitive())
      m_rb_none->set_active ();

    m_cb_export_playlist->set_active ();
    response_check ();
  }

  void
  ExportDialog::on_export_audio_toggled () 
  {
    m_ref_xml->get_widget ("vbox-audio-format")->set_sensitive (m_cb_export_audio->get_active());
    response_check ();
  }

  void
  ExportDialog::on_export_playlist_toggled () 
  {
    m_ref_xml->get_widget ("vbox-playlist")->set_sensitive (m_cb_export_playlist->get_active());
    response_check ();
  }

  void
  ExportDialog::response_check ()
  {
    bool active (m_cb_export_playlist->get_active () || m_cb_export_audio->get_active());
    set_response_sensitive (GTK_RESPONSE_OK, active);
  }

  void
  ExportDialog::on_container_treeview_cursor_changed ()
  {
    ustring name, composited, text (m_filename_entry->get_text ());
    ustring::size_type position = text.rfind (".");

    TreeModel::iterator iter (m_tree_view_plugins->get_selection()->get_selected ());
    string extension ((*iter)[m_columns.extension]);

    if (position != ustring::npos)
        name = text.substr (0, position+1);
    else
        name = text;

    if (position != string::npos)
        composited = name + extension;
    else
        composited = name + "." + extension;

    m_filename_entry->set_text (composited);
    m_container = (*iter)[m_columns.extension];
  }

  void
  ExportDialog::on_select_destination_folder ()
  {
    m_file_browser->set_current_folder (m_dest_path_entry->get_text ());
    switch (m_file_browser->run ())
    {
      case RESPONSE_CANCEL:
        break;
      case RESPONSE_OK:
      {
        m_dest_path_entry->set_text (m_file_browser->get_filename ());
        break;
      }
    }
    m_file_browser->hide ();
  }
}
