//  BMP
//  Copyright (C) 2005-2007 <http://bmpx.backtrace.info> (see AUTHORS)
//
//  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 <revision.h>
#include <build.h>

#include <string>
#include <cstdlib>
#include <cstring>

#include <glib/gi18n.h>
#include <glib/gprintf.h>
#include <glib/gstdio.h>
#include <glibmm/fileutils.h>
#include <glibmm/init.h>
#include <glibmm/miscutils.h>
#include <glibmm/optioncontext.h>
#include <glibmm/ustring.h>
#include <glibmm/wrap_init.h>
#include <gtkmm/main.h>
#include <gtkmm/messagedialog.h>
#include <gtkmm/rc.h>
#include <gtkmm/window.h>
#include <gdkmm/wrap_init.h>
#include <gtkmm/wrap_init.h>
#include <pangomm/wrap_init.h>

#include <dbus/dbus-gtype-specialized.h>

#include <libxml/parser.h>

#include <mcs/mcs.h>
#include <gst/gst.h>

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

#ifdef HAVE_SM
#  include "sm.hh"
#endif // HAVE_SM

#ifdef HAVE_STARTUP_NOTIFICATION
#  define SN_API_NOT_YET_FROZEN
#  include <libsn/sn-launchee.h>
#  include <gdk/gdkx.h>
#endif // HAVE_STARTUP_NOTIFICATION

// BMP Audio
#include "audio/audio.hh"
#include "audio/play.hh"

// BMP Misc
#include "amazon.hh"
#include "core.hh"
#include "debug.hh"
#ifdef HAVE_HAL
# include "hal.hh"
#endif //HAVE_HAL
#include "lastfm.hh"
#include "library.hh"
#include "logger.hh"
#include "network.hh"
#include "paths.hh"
#include "sanity.h"
#include "signals.hh"
#include "ui-tools.hh"
#include "util.hh"
#include "util-file.hh"
#include "vfs.hh"

#include "main.hh"

using namespace Bmp;
using namespace Glib;

Logger                * logger = 0;
VFS::VFS              * vfs    = 0;
Mcs::Mcs              * mcs    = 0;

#ifdef HAVE_HAL
HAL                   * hal               = 0;
#endif //HAVE_HAL

// Application paths
std::string bmp_paths[BMP_N_PATHS];

// Indicates startup state
bool in_startup = false;

#include "splash-screen.hh"

namespace
{
#ifdef HAVE_SM
  char* session_id = 0;
#endif // HAVE_SM

#ifdef HAVE_STARTUP_NOTIFICATION
  SnLauncheeContext* sn_context = 0;
  SnDisplay*         sn_display = 0;
#endif // HAVE_STARTUP_NOTIFICATION

  gboolean show_version   = FALSE;
  gboolean log_stdout     = FALSE;
  gboolean no_remote      = FALSE;
  gboolean no_network     = FALSE;
  gboolean show_configure = FALSE;

  Splashscreen * splash = 0;

  char const * splash_messages[] =
  {
    N_("Initializing Application"),
    N_("Initializing HAL"),
    N_("Initializing Library"),
    N_("Initializing Amazon"),
    N_("Initializing VFS"),
    N_("Initializing Playback"),
    N_("Initializing LastFM (Scrobbler)"),
    N_("Initializing LastFM (Radio)"),
    N_("Starting GUI")
  };

  unsigned int message_id = 0;

  char const*
  splash_get_message ()
  {
    return _(splash_messages[message_id++]);
  }

  double
  splash_get_percent ()
  {
    return double (message_id) / (G_N_ELEMENTS(splash_messages)-1); 
  }

  void
  quick_exit ()
  {
    Session::end ();
    Core::Obj()->quit ();
    Core::Obj(OBJ_FLAG_DELETE);
    Library::Obj(OBJ_FLAG_DELETE);
    std::exit (EXIT_SUCCESS);
  }

  GOptionEntry options[] =
  {
      {"version",     'v', 0, G_OPTION_ARG_NONE,  &show_version,
       N_("Display version"), 0},

      {"no-log",      'l', 0, G_OPTION_ARG_NONE,  &log_stdout,
       N_("Disable logfile, log messages to shell"), 0},

      {"no-remote",   'r', 0, G_OPTION_ARG_NONE,  &no_remote,
       N_("Disable DBUS remote interface"), 0},

      {"no-network",  'n', 0, G_OPTION_ARG_NONE,  &no_network,
       N_("Disable Network dependent services"), 0},

      {"configure",   'c', 0, G_OPTION_ARG_NONE,  &show_configure,
       N_("Display configure arguments"), 0},

#ifdef HAVE_SM
      {"sm-client-id", 0, 0, G_OPTION_ARG_STRING, &session_id,
       N_("Specify session management ID"), N_("ID")},
#endif // HAVE_SM

      { NULL }
  };

  static const char *features[] =
  {

#ifdef HAVE_SM
      N_("X11 Session Management"),
#endif //HAVE_SM

      N_("D-BUS support"),

#ifdef HAVE_HAL
      N_("HAL support"),
#endif //HAVE_HAL

#ifdef HAVE_ALSA
      N_("ALSA Support (Linux)"),
#endif //HAVE_ALSA

#ifdef HAVE_SUN
      N_("SUN Audio support"),
#endif //HAVE_SUN

#ifdef HAVE_OFA
      N_("MusicIP support"),
#endif //HAVE_OFA

#ifdef HAVE_MP4V2
      N_("M4A/AAC taglib support"),
#endif //HAVE_MP4V2

#ifdef HAVE_SID
      N_("SID taglib support"),
#endif //HAVE_SID

#ifdef HAVE_MOD
      N_("Tracker modules taglib support")
#endif //HAVE_MOD
  };

  void
  print_version ()
  {
    g_print ("%s %s",
             PACKAGE,
             VERSION);

    if (std::strlen (RV_REVISION))
      g_print (" (%s-R%s)",
               RV_LAST_CHANGED_DATE,
               RV_REVISION);

    g_print (_("\nCopyright (c) 2005-2007 BMP Project <http://bmpx.backtrace.info>\n\n"));

    g_print (_("Built on %s for %s with:\n"),
             BUILD_DATE,
             BUILD_ARCH);

    for (unsigned int i = 0; i < G_N_ELEMENTS (features); i++)
      g_print ("\t* %s\n", _(features[i]));

    g_print ("\n");
  }

  void
  print_configure ()
  {
    g_print("%s\n",
            CONFIGURE_ARGS);
  }

  int
  create_path (std::string const& name)
  {
    const mode_t mode755 = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;

    if (file_test (name, FILE_TEST_IS_DIR))
      return 0;

    int mkdir_rv = g_mkdir_with_parents (name.c_str (), mode755);

    if (mkdir_rv)
    {
      int errsv = errno;
      g_critical ("%s: Unable to create %s: %s", G_STRFUNC, name.c_str (), g_strerror (errsv));
    }

    return mkdir_rv;
  }

  void
  create_paths ()
  {
    const mode_t mode755 = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
    int          sanity  = 0;

    std::string cache_dir = build_filename (g_get_user_cache_dir (), BMP_XDG_BASEDIR);

    if (!file_test (cache_dir, FILE_TEST_IS_DIR))
    {
        sanity |= g_mkdir_with_parents (cache_dir.c_str (), mode755);

        if (sanity != 0)
        {
            int errsv = errno;
            g_critical ("%s: Unable to create %s: %s", G_STRFUNC, cache_dir.c_str (), g_strerror (errsv));
        }
    }

    sanity |= create_path (BMP_PATH_USER_DIR);
    sanity |= create_path (BMP_PATH_DATA_DIR);
    sanity |= create_path (BMP_PATH_LOGS_DIR);
    sanity |= create_path (BMP_PATH_CACHE_DIR);
    sanity |= create_path (BMP_PATH_COVER_THUMB_DIR);
    sanity |= create_path (BMP_PATH_LYRIC_CACHE_DIR);
    sanity |= create_path (BMP_PATH_PODCAST_CACHE_DIR);

    if (sanity)
    {
      g_error (_("%s: Unable to create one or more user directories used by BMP (please see log above). "
                  "Aborting startup; please check permissions in your home directory"), G_STRFUNC);
      std::exit (EXIT_FAILURE);
    }
  }

  void
  paths_init ()
  {
    std::string culprit = build_filename (get_home_dir (), ".config");

    if (file_test (culprit, FILE_TEST_EXISTS) &&
        file_test (culprit, FILE_TEST_IS_REGULAR))
    {
        std::string destination = build_filename (get_home_dir (), ".config-moved-by-bmpx");

        try
          {
            Util::copy_file (culprit.c_str(), destination.c_str());
          }
        catch (std::exception& error)
          {
            g_critical ("%s: Unable to copy file to destination. Reason: '%s'. Source: '%s'. Destination: '%s'",
                        G_STRLOC, error.what(), culprit.c_str (), destination.c_str ());
          }

        g_unlink (culprit.c_str());

        g_message ("%s: The file %s has been moved to %s to accomodate for the XDG user home config dir",
                   G_STRLOC, culprit.c_str(), destination.c_str());
    }

    BMP_PATH_USER_DIR = build_filename (g_get_user_config_dir (), BMP_XDG_BASEDIR);
    BMP_PATH_DATA_DIR = build_filename (g_get_user_data_dir (),   BMP_XDG_BASEDIR);
    BMP_PATH_LOGS_DIR = build_filename (BMP_PATH_USER_DIR,        BMP_BASENAME_LOGS_DIR);
    BMP_PATH_CACHE_DIR = build_filename (g_get_user_cache_dir (), BMP_XDG_BASEDIR);
    BMP_PATH_COVER_THUMB_DIR = build_filename (BMP_PATH_CACHE_DIR, BMP_BASENAME_COVER_THUMB_DIR);
    BMP_PATH_LYRIC_CACHE_DIR = build_filename (BMP_PATH_CACHE_DIR, BMP_BASENAME_LYRIC_CACHE_DIR);
    BMP_PATH_PODCAST_CACHE_DIR = build_filename (BMP_PATH_CACHE_DIR, BMP_BASENAME_PODCAST_CACHE_DIR);

    create_paths ();
  }

  void
  i18n_init ()
  {
    gtk_set_locale ();
    bindtextdomain (PACKAGE, LOCALE_DIR);
    bind_textdomain_codeset (PACKAGE, "UTF-8");
    textdomain (PACKAGE);
  }

#ifdef HAVE_STARTUP_NOTIFICATION
  void
  sn_error_trap_push(SnDisplay *display, Display *xdisplay)
  {
    gdk_error_trap_push();
  }

  void
  sn_error_trap_pop(SnDisplay *display, Display *xdisplay)
  {
    gdk_error_trap_pop();
  }

  void
  startup_notification_complete()
  {
    Display *xdisplay;

    xdisplay = GDK_DISPLAY();
    sn_display = sn_display_new(xdisplay,
                                sn_error_trap_push,
                                sn_error_trap_pop);
    sn_context =
      sn_launchee_context_new_from_environment(sn_display,
                                               DefaultScreen(xdisplay));

    if (sn_context != 0)
      {
        sn_launchee_context_complete(sn_context);
        sn_launchee_context_unref(sn_context);

        sn_display_unref(sn_display);
      }
  }
#endif // HAVE_STARTUP_NOTIFICATION

  void
  on_startup_complete ()
  {
    if (splash)
      {
        delete splash;
        splash = 0;
      }
  }

  void
  setup_mcs ()
  {
    try{
      mcs = new Mcs::Mcs (build_filename (BMP_PATH_USER_DIR, "config.xml"), "bmp", 0.30);
    }
    catch (Mcs::Mcs::Exceptions & cxe)
    {
      if (cxe == Mcs::Mcs::PARSE_ERROR)
      {
        g_log (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, _("Unable to parse configuration file"));
      }
    }

    mcs->domain_register ("main-window");
    mcs->key_register ("main-window", "width", 0); //FIXME:
    mcs->key_register ("main-window", "height", 0); //FIXME:
    mcs->key_register ("main-window", "pos_x", 0);
    mcs->key_register ("main-window", "pos_y", 0);

    mcs->domain_register ("ui");
    mcs->key_register ("ui", "show-statusbar", true);

    mcs->domain_register ("bmp");
    mcs->key_register ("bmp", "no-ui", false);
    mcs->key_register ("bmp", "ui-esc-trayconify", false);
    mcs->key_register ("bmp", "keep-above", false);
    mcs->key_register ("bmp", "file-chooser-close-on-open", true);
    mcs->key_register ("bmp", "file-chooser-close-on-add", false);
    mcs->key_register ("bmp", "file-chooser-path", get_home_dir ());
    mcs->key_register ("bmp", "icon-theme", std::string ("tango"));
    mcs->key_register ("bmp", "force-rgba-enable", false);
    mcs->key_register ("bmp", "display-notifications", true);
    mcs->key_register ("bmp", "no-remote", false);
    mcs->key_register ("bmp", "time-remaining", false);
    mcs->key_register ("bmp", "volume", 50);
    mcs->key_register ("bmp", "follow-current-track", false);
    mcs->key_register ("bmp", "shuffle", false);
    mcs->key_register ("bmp", "repeat", false);
    mcs->key_register ("bmp", "enable-autoplay", false);
    mcs->key_register ("bmp", "spm-listen", false);

    mcs->domain_register ("audio");

    mcs->key_register ("audio", "band0", 0.0);
    mcs->key_register ("audio", "band1", 0.0);
    mcs->key_register ("audio", "band2", 0.0);
    mcs->key_register ("audio", "band3", 0.0);
    mcs->key_register ("audio", "band4", 0.0);
    mcs->key_register ("audio", "band5", 0.0);
    mcs->key_register ("audio", "band6", 0.0);
    mcs->key_register ("audio", "band7", 0.0);
    mcs->key_register ("audio", "band8", 0.0);
    mcs->key_register ("audio", "band9", 0.0);

    mcs->key_register ("audio", "cdrom-device", std::string ("/dev/cdrom"));
    mcs->key_register ("audio", "sink", std::string (DEFAULT_SINK));
    mcs->key_register ("audio", "enable-eq", bool( false )); 

#ifdef HAVE_HAL
    mcs->key_register ("audio", "hal-udi", std::string (""));
#endif // HAVE_HAL

#ifdef HAVE_ALSA
    mcs->key_register ("audio", "alsa-buffer-time", 200000);
    mcs->key_register ("audio", "device-alsa", std::string (DEFAULT_DEVICE_ALSA));
#endif // HAVE_ALSA

#ifdef HAVE_SUN
    mcs->key_register ("audio", "sun-buffer-time", 200000);
    mcs->key_register ("audio", "device-sun", std::string (DEFAULT_DEVICE_SUN));
#endif // HAVE_SUN

    mcs->key_register ("audio", "oss-buffer-time", 200000);
    mcs->key_register ("audio", "device-oss", std::string (DEFAULT_DEVICE_OSS));

    mcs->key_register ("audio", "esd-buffer-time", 200000);
    mcs->key_register ("audio", "device-esd", std::string (DEFAULT_DEVICE_ESD));

    mcs->key_register ("audio", "pulse-buffer-time", 200000);
    mcs->key_register ("audio", "pulse-device", std::string ());
    mcs->key_register ("audio", "pulse-server", std::string ());

    mcs->key_register ("audio", "jack-buffer-time", 200000);
    mcs->key_register ("audio", "jack-server", std::string ());

    mcs->key_register ("audio", "video-output", 0);

    mcs->domain_register ("lastfm");
    mcs->key_register ("lastfm", "username", std::string ());
    mcs->key_register ("lastfm", "password", std::string ());
    mcs->key_register ("lastfm", "radio-connect", false);
    mcs->key_register ("lastfm", "queue-enable", false);
    mcs->key_register ("lastfm", "discoverymode", false);

#ifdef HAVE_OFA
    mcs->domain_register ("musicbrainz");
    mcs->key_register ("musicbrainz", "username", std::string ());
    mcs->key_register ("musicbrainz", "password", std::string ());
#endif //HAVE_OFA

    mcs->domain_register ("albums");
    mcs->key_register ("albums", "continuous-play", false);

    mcs->domain_register ("podcasts");
    mcs->key_register ("podcasts", "download-dir", get_home_dir ());
    mcs->key_register ("podcasts", "download-policy", 0);
    mcs->key_register ("podcasts", "update-interval", 0);
    mcs->key_register ("podcasts", "cache-policy", 0);

    mcs->domain_register ("playlist");
    mcs->key_register ("playlist", "rootpath", get_home_dir ());

    mcs->domain_register ("library");
    mcs->key_register ("library", "rootpath", std::string (build_filename (get_home_dir (), "Music")));

    mcs->domain_register ("hotkeys");
    mcs->key_register ("hotkeys", "enable", bool (true)); 
    mcs->key_register ("hotkeys", "system", int (1)); // GNOME Configured is default
    mcs->key_register ("hotkeys", "key-1", int (0)); // Play
    mcs->key_register ("hotkeys", "key-1-mask", int (0));
    mcs->key_register ("hotkeys", "key-2", int (0)); // Pause
    mcs->key_register ("hotkeys", "key-2-mask", int (0));
    mcs->key_register ("hotkeys", "key-3", int (0)); // Prev
    mcs->key_register ("hotkeys", "key-3-mask", int (0));
    mcs->key_register ("hotkeys", "key-4", int (0)); // Next
    mcs->key_register ("hotkeys", "key-4-mask", int (0));
    mcs->key_register ("hotkeys", "key-5", int (0)); // Stop
    mcs->key_register ("hotkeys", "key-5-mask", int (0));

    mcs->load (Mcs::Mcs::VERSION_IGNORE);
  }

  void
  setup_library ()
  {
    bool    library_recreated = false;
    DBFlags db_flags;

    Library::Obj (OBJ_FLAG_CREATE);

    // Check library for version and flags
    if (!Library::Obj()->open ())
    {
      static boost::format msg_fmt (_("The library could not be created. Please check permissions for creating the file '%s'"));
      std::string library_file = build_filename (BMP_PATH_DATA_DIR, "library.mlib");
      ustring message ((msg_fmt % library_file).str ());
      Gtk::MessageDialog dialog (message, false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true);
      dialog.run();
      quick_exit ();
    }

    db_flags = Library::Obj()->get_db_flags();

#ifdef HAVE_HAL

    if (!library_recreated && ((db_flags & DB_FLAG_USING_HAL) == 0))
    {
      Gtk::MessageDialog dialog (_("<big><b>Incompatible Library: no HAL support</b></big>\n\n"
                                    "Your current library file was created <b>without</b> HAL support, but this version of BMPx"
                                    " was built <b>with</b> support for HAL, and can not use your library file.\n"
                                    "You can either cancel the startup and recompile BMPx using --disable-hal, or continue,"
                                    " in which case the library has to be recreated (with HAL support), but you will have to"
                                    " re-add your music once again."),
      true, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK_CANCEL, true);
      dialog.set_title (_("Incompatible Library: No HAL support - BMP"));

      if (dialog.run() == Gtk::RESPONSE_CANCEL)
      {
        quick_exit ();
      }
      else
      {
        Library::Obj()->recreate_db();
        library_recreated = true;
      }
    }

#else //!HAVE_HAL

    if (!library_recreated && ((db_flags & DB_FLAG_USING_HAL) != 0))
    {
      Gtk::MessageDialog dialog (_("<big><b>Incompatible Library: HAL-dependent</b></big>\n\n"
                                    "Your current library file was created <b>with</b> HAL support, but this version of BMPx"
                                    " was built without support for HAL, and can not use your library file.\n"
                                    "You can either cancel the startup and recompile BMPx using --enable-hal, or continue,"
                                    " in which case the library has to be recreated (with HAL support) and you will have to"
                                    " re-add your music once again."),
      true, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK_CANCEL, true);
      dialog.set_title (_("Incompatible Library: HAL-dependent - BMP"));

      if (dialog.run() == Gtk::RESPONSE_CANCEL)
      {
        quick_exit ();
      }
      else
      {
        Library::Obj()->recreate_db();
        library_recreated = true;
      }
    }

#endif

    Library::Obj()->init ();
  }

  Gtk::Main * kit = 0;
  void
  app_start () { kit->run(); }
  void
  app_quit  () { kit->quit(); }

} //!<unnamed>::

int
main (int    argc,
      char** argv)
{
  thread_init (0);

  char* sanity_error = sanity_check_libs ();
  if (sanity_error)
  {
    g_printerr ("%s\n", sanity_error);
    g_free (sanity_error);
    std::exit (EXIT_FAILURE);
  }

  init ();
  wrap_init();
  i18n_init ();
  paths_init ();

  xmlInitParser ();
  xmlDefaultSAXHandlerInit ();

  GOptionContext *_oc = g_option_context_new (_(" - Run BMP"));
  g_option_context_add_main_entries (_oc, options, "bmpx");
  g_option_context_add_group (_oc, gst_init_get_option_group ());
  OptionContext oc (_oc, true);

  gst_registry_fork_set_enabled (FALSE);

  try {
    kit = new Gtk::Main (argc, argv, oc);
    }
  catch (OptionError & cxe)
    {
      g_warning ("%s: %s", G_STRLOC, cxe.what().c_str());
      std::exit (EXIT_FAILURE);
    }

  char *const style = 
  "style \"bmp-default\" \n"
  "{ \n"
  "GtkMenuBar     ::shadow-type       = GTK_SHADOW_NONE \n"
  "GtkToolbar     ::shadow-type       = GTK_SHADOW_NONE \n"
  "} \n"
  "class \"GtkWidget\" style \"bmp-default\"";
  Gtk::RC::parse_string (style);

  if (show_version)
  {
    print_version ();
    goto main_early_exit;
  }

  if (show_configure)
  {
    print_configure ();
    goto main_early_exit;
  }

  debug_init ();
  in_startup = true;

  if (!log_stdout)
  {
    logger = new Logger;
  }

  if (!no_remote)
  {
    dbus_g_type_specialized_init ();
  }
  setup_mcs ();

#ifdef HAVE_STARTUP_NOTIFICATION
  Gtk::Window::set_auto_startup_notification (false);
#endif //HAVE_STARTUP_NOTIFICATION

  Util::check_alpha ();
  splash = new Splashscreen();
  splash->show ();

  Signals::handlers_init ();
  Session::start (argc, argv, session_id);

  splash->set_message (splash_get_message(), splash_get_percent());

  NM::Obj(OBJ_FLAG_CREATE);

  if (no_network)
  {
    NM::Obj()->Disable ();
  }

  Core::Obj (OBJ_FLAG_CREATE);

  Core::Obj()->signal_startup_complete().connect
    (sigc::ptr_fun (&on_startup_complete));

  Core::Obj()->register_application_control
    (sigc::ptr_fun (&app_start),
     sigc::ptr_fun (&app_quit));

  splash->set_message (splash_get_message(), splash_get_percent());

#ifdef HAVE_HAL
  try{
    hal = new HAL;
    }
  catch (Bmp::HAL::NotInitializedError & cxe)
    {
      quick_exit ();
    }
#endif //HAVE_HAL

  splash->set_message (splash_get_message(), splash_get_percent());
  setup_library ();

  splash->set_message (splash_get_message(), splash_get_percent());
  Amazon::Covers::Obj (OBJ_FLAG_CREATE);

  splash->set_message (splash_get_message(), splash_get_percent());
  vfs = new VFS::VFS;

  splash->set_message (splash_get_message(), splash_get_percent());
  Play::Obj (OBJ_FLAG_CREATE);

  splash->set_message (splash_get_message(), splash_get_percent());
  LastFM::Scrobbler::Obj (OBJ_FLAG_CREATE);

  splash->set_message (splash_get_message(), splash_get_percent());
  if (NM::Obj()->Check_Status())
  LastFM::Radio::Obj (OBJ_FLAG_CREATE);

  splash->set_message (splash_get_message(), splash_get_percent());
  Core::Obj()->ui_start ();

#ifdef HAVE_STARTUP_NOTIFICATION
  startup_notification_complete ();
#endif //HAVE_STARTUP_NOTIFICATION

  ///////////////////// RUN BMP RUN
  in_startup = false;
  Core::Obj()->run ();
  ///////////////////// RUN BMP RUN

  Core::Obj()->ui_stop ();
  Session::end ();

  LastFM::Scrobbler::Obj (OBJ_FLAG_DELETE); // delete
  LastFM::Radio::Obj (OBJ_FLAG_DELETE); // delete
  Play::Obj (OBJ_FLAG_DELETE); // delete

#ifdef HAVE_HAL
  delete hal;
#endif //HAVE_HAL
  
  Amazon::Covers::Obj (OBJ_FLAG_DELETE);

  delete vfs;

  Library::Obj (OBJ_FLAG_DELETE);  

  delete mcs;
  delete logger;

  Core::Obj (OBJ_FLAG_DELETE);
  NM::Obj(OBJ_FLAG_DELETE);

 main_early_exit:

  std::exit (EXIT_SUCCESS);
}
