/*
 *      Copyright (C) 2005-2009 Team XBMC
 *      http://www.xbmc.org
 *
 *  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, 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 XBMC; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 *  http://www.gnu.org/copyleft/gpl.html
 *
 */
#include "AddonManager.h"
#include "Addon.h"
#include "DllLibCPluff.h"
#include "utils/StringUtils.h"
#include "utils/JobManager.h"
#include "threads/SingleLock.h"
#include "FileItem.h"
#include "LangInfo.h"
#include "settings/Settings.h"
#include "settings/GUISettings.h"
#include "settings/AdvancedSettings.h"
#include "utils/log.h"

#ifdef HAS_VISUALISATION
#include "Visualisation.h"
#endif
#ifdef HAS_SCREENSAVER
#include "ScreenSaver.h"
#endif
//#ifdef HAS_SCRAPERS
#include "Scraper.h"
//#endif
#include "PluginSource.h"
#include "Repository.h"
#include "Skin.h"
#include "Service.h"

using namespace std;

namespace ADDON
{

cp_log_severity_t clog_to_cp(int lvl);
void cp_fatalErrorHandler(const char *msg);
void cp_logger(cp_log_severity_t level, const char *msg, const char *apid, void *user_data);
bool GetExtElementDeque(DEQUEELEMENTS &elements, cp_cfg_element_t *base, const char *path);

/**********************************************************
 * CAddonMgr
 *
 */

map<TYPE, IAddonMgrCallback*> CAddonMgr::m_managers;

AddonPtr CAddonMgr::Factory(const cp_extension_t *props)
{
  if (!PlatformSupportsAddon(props->plugin))
    return AddonPtr();

  /* Check if user directories need to be created */
  const cp_cfg_element_t *settings = GetExtElement(props->configuration, "settings");
  if (settings)
    CheckUserDirs(settings);

  const TYPE type = TranslateType(props->ext_point_id);
  switch (type)
  {
    case ADDON_PLUGIN:
    case ADDON_SCRIPT:
      return AddonPtr(new CPluginSource(props));
    case ADDON_SCRIPT_LIBRARY:
    case ADDON_SCRIPT_LYRICS:
    case ADDON_SCRIPT_WEATHER:
    case ADDON_SCRIPT_SUBTITLES:
    case ADDON_SCRIPT_MODULE:
    case ADDON_WEB_INTERFACE:
      return AddonPtr(new CAddon(props));
    case ADDON_SERVICE:
      return AddonPtr(new CService(props));
    case ADDON_SCRAPER_ALBUMS:
    case ADDON_SCRAPER_ARTISTS:
    case ADDON_SCRAPER_MOVIES:
    case ADDON_SCRAPER_MUSICVIDEOS:
    case ADDON_SCRAPER_TVSHOWS:
    case ADDON_SCRAPER_LIBRARY:
      return AddonPtr(new CScraper(props));
    case ADDON_VIZ:
    case ADDON_SCREENSAVER:
      { // begin temporary platform handling for Dlls
        // ideally platforms issues will be handled by C-Pluff
        // this is not an attempt at a solution
        CStdString value;
        if (type == ADDON_SCREENSAVER && 0 == strnicmp(props->plugin->identifier, "screensaver.xbmc.builtin.", 25))
        { // built in screensaver
          return AddonPtr(new CAddon(props));
        }
#if defined(_LINUX) && !defined(__APPLE__)
        if ((value = GetExtValue(props->plugin->extensions->configuration, "@library_linux")) && value.empty())
          break;
#elif defined(_WIN32) && defined(HAS_SDL_OPENGL)
        if ((value = GetExtValue(props->plugin->extensions->configuration, "@library_wingl")) && value.empty())
          break;
#elif defined(_WIN32) && defined(HAS_DX)
        if ((value = GetExtValue(props->plugin->extensions->configuration, "@library_windx")) && value.empty())
          break;
#elif defined(__APPLE__)
        if ((value = GetExtValue(props->plugin->extensions->configuration, "@library_osx")) && value.empty())
          break;
#elif defined(_XBOX)
        if ((value = GetExtValue(props->plugin->extensions->configuration, "@library_xbox")) && value.empty())
          break;
#endif
        if (type == ADDON_VIZ)
        {
#if defined(HAS_VISUALISATION)
          return AddonPtr(new CVisualisation(props));
#endif
        }
        else
          return AddonPtr(new CScreenSaver(props));
      }
    case ADDON_SKIN:
      return AddonPtr(new CSkinInfo(props));
    case ADDON_VIZ_LIBRARY:
      return AddonPtr(new CAddonLibrary(props));
    case ADDON_REPOSITORY:
      return AddonPtr(new CRepository(props));
    default:
      break;
  }
  return AddonPtr();
}

bool CAddonMgr::CheckUserDirs(const cp_cfg_element_t *settings)
{
  if (!settings)
    return false;

  const cp_cfg_element_t *userdirs = GetExtElement((cp_cfg_element_t *)settings, "userdirs");
  if (!userdirs)
    return false;

  DEQUEELEMENTS elements;
  bool status = GetExtElementDeque(elements, (cp_cfg_element_t *)userdirs, "userdir");
  if (!status)
    return false;

  IDEQUEELEMENTS itr = elements.begin();
  while (itr != elements.end())
  {
    CStdString path = GetExtValue(*itr++, "@path");
    if (!CFile::Exists(path))
    {
      if (!CUtil::CreateDirectoryEx(path))
      {
        CLog::Log(LOGERROR, "CAddonMgr::CheckUserDirs: Unable to create directory %s.", path.c_str());
        return false;
      }
    }
  }

  return true;
}

CAddonMgr::CAddonMgr()
{
}

CAddonMgr::~CAddonMgr()
{
  DeInit();
}

CAddonMgr &CAddonMgr::Get()
{
  static CAddonMgr sAddonMgr;
  return sAddonMgr;
}

IAddonMgrCallback* CAddonMgr::GetCallbackForType(TYPE type)
{
  if (m_managers.find(type) == m_managers.end())
    return NULL;
  else
    return m_managers[type];
}

bool CAddonMgr::RegisterAddonMgrCallback(const TYPE type, IAddonMgrCallback* cb)
{
  if (cb == NULL)
    return false;

  m_managers.erase(type);
  m_managers[type] = cb;

  return true;
}

void CAddonMgr::UnregisterAddonMgrCallback(TYPE type)
{
  m_managers.erase(type);
}

bool CAddonMgr::Init()
{
  m_cpluff = new DllLibCPluff;
  m_cpluff->Load();

  m_database.Open();

  if (!m_cpluff->IsLoaded())
  {
    CLog::Log(LOGERROR, "ADDONS: Fatal Error, could not load libcpluff");
    return false;
  }

  m_cpluff->set_fatal_error_handler(cp_fatalErrorHandler);

  cp_status_t status;
  status = m_cpluff->init();
  if (status != CP_OK)
  {
    CLog::Log(LOGERROR, "ADDONS: Fatal Error, cp_init() returned status: %i", status);
    return false;
  }

  //TODO could separate addons into different contexts
  // would allow partial unloading of addon framework
  m_cp_context = m_cpluff->create_context(&status);
  assert(m_cp_context);
  status = m_cpluff->register_pcollection(m_cp_context, _P("special://home/addons"));
  status = m_cpluff->register_pcollection(m_cp_context, _P("special://xbmc/addons"));
  status = m_cpluff->register_pcollection(m_cp_context, _P("special://xbmcbin/addons"));
  if (status != CP_OK)
  {
    CLog::Log(LOGERROR, "ADDONS: Fatal Error, cp_register_pcollection() returned status: %i", status);
    return false;
  }

  status = m_cpluff->register_logger(m_cp_context, cp_logger,
      &CAddonMgr::Get(), clog_to_cp(g_advancedSettings.m_logLevel));
  if (status != CP_OK)
  {
    CLog::Log(LOGERROR, "ADDONS: Fatal Error, cp_register_logger() returned status: %i", status);
    return false;
  }

  FindAddons();
  return true;
}

void CAddonMgr::DeInit()
{
  if (m_cpluff)
    m_cpluff->destroy();
  delete m_cpluff;
  m_cpluff = NULL;
  m_database.Close();
}

bool CAddonMgr::HasAddons(const TYPE &type, bool enabled /*= true*/)
{
  // TODO: This isn't particularly efficient as we create an addon type for each addon using the Factory, just so
  //       we can check addon dependencies in the addon constructor.
  VECADDONS addons;
  return GetAddons(type, addons, enabled);
}

bool CAddonMgr::GetAllAddons(VECADDONS &addons, bool enabled /*= true*/, bool allowRepos /* = false */)
{
  for (int i = ADDON_UNKNOWN+1; i < ADDON_VIZ_LIBRARY; ++i)
  {
    if (!allowRepos && ADDON_REPOSITORY == (TYPE)i)
      continue;
    VECADDONS temp;
    if (CAddonMgr::Get().GetAddons((TYPE)i, temp, enabled))
      addons.insert(addons.end(), temp.begin(), temp.end());
  }
  return !addons.empty();
}

bool CAddonMgr::GetAllOutdatedAddons(VECADDONS &addons, bool enabled /*= true*/)
{
  CSingleLock lock(m_critSection);
  for (int i = ADDON_UNKNOWN+1; i < ADDON_VIZ_LIBRARY; ++i)
  {
    VECADDONS temp;
    if (CAddonMgr::Get().GetAddons((TYPE)i, temp, enabled))
    {
      AddonPtr repoAddon;
      for (unsigned int j = 0; j < temp.size(); j++)
      {
        if (!m_database.GetAddon(temp[j]->ID(), repoAddon))
          continue;

        if (temp[j]->Version() < repoAddon->Version())
          addons.push_back(repoAddon);
      }
    }
  }
  return !addons.empty();
}

bool CAddonMgr::HasOutdatedAddons(bool enabled /*= true*/)
{
  VECADDONS dummy;
  return GetAllOutdatedAddons(dummy,enabled);
}

bool CAddonMgr::GetAddons(const TYPE &type, VECADDONS &addons, bool enabled /* = true */)
{
  CSingleLock lock(m_critSection);
  addons.clear();
  cp_status_t status;
  int num;
  CStdString ext_point(TranslateType(type));
  cp_extension_t **exts = m_cpluff->get_extensions_info(m_cp_context, ext_point.c_str(), &status, &num);
  for(int i=0; i <num; i++)
  {
    AddonPtr addon(Factory(exts[i]));
    if (addon && m_database.IsAddonDisabled(addon->ID()) != enabled)
      addons.push_back(addon);
  }
  m_cpluff->release_info(m_cp_context, exts);
  return addons.size() > 0;
}

bool CAddonMgr::GetAddon(const CStdString &str, AddonPtr &addon, const TYPE &type/*=ADDON_UNKNOWN*/, bool enabledOnly /*= true*/)
{
  CSingleLock lock(m_critSection);

  cp_status_t status;
  cp_plugin_info_t *cpaddon = m_cpluff->get_plugin_info(m_cp_context, str.c_str(), &status);
  if (status == CP_OK && cpaddon)
  {
    addon = GetAddonFromDescriptor(cpaddon);
    m_cpluff->release_info(m_cp_context, cpaddon);
    if (addon.get() && enabledOnly && m_database.IsAddonDisabled(addon->ID()))
      return false;
    return NULL != addon.get();
  }
  if (cpaddon)
    m_cpluff->release_info(m_cp_context, cpaddon);

  return false;
}

//TODO handle all 'default' cases here, not just scrapers & vizs
bool CAddonMgr::GetDefault(const TYPE &type, AddonPtr &addon)
{
  CStdString setting;
  switch (type)
  {
  case ADDON_VIZ:
    setting = g_guiSettings.GetString("musicplayer.visualisation");
    break;
  case ADDON_SCREENSAVER:
    setting = g_guiSettings.GetString("screensaver.mode");
    break;
  case ADDON_SCRAPER_ALBUMS:
    setting = g_guiSettings.GetString("musiclibrary.albumsscraper");
    break;
  case ADDON_SCRAPER_ARTISTS:
    setting = g_guiSettings.GetString("musiclibrary.artistsscraper");
    break;
  case ADDON_SCRAPER_MOVIES:
    setting = g_guiSettings.GetString("scrapers.moviesdefault");
    break;
  case ADDON_SCRAPER_MUSICVIDEOS:
    setting = g_guiSettings.GetString("scrapers.musicvideosdefault");
    break;
  case ADDON_SCRAPER_TVSHOWS:
    setting = g_guiSettings.GetString("scrapers.tvshowsdefault");
    break;
  case ADDON_WEB_INTERFACE:
    setting = g_guiSettings.GetString("services.webskin");
    break;
  default:
    return false;
  }
  return GetAddon(setting, addon, type);
}

bool CAddonMgr::SetDefault(const TYPE &type, const CStdString &addonID)
{
  switch (type)
  {
  case ADDON_VIZ:
    g_guiSettings.SetString("musicplayer.visualisation",addonID);
    break;
  case ADDON_SCREENSAVER:
    g_guiSettings.SetString("screensaver.mode",addonID);
    break;
  case ADDON_SCRAPER_ALBUMS:
    g_guiSettings.SetString("musiclibrary.albumsscraper",addonID);
    break;
  case ADDON_SCRAPER_ARTISTS:
    g_guiSettings.SetString("musiclibrary.artistsscraper",addonID);
    break;
  case ADDON_SCRAPER_MOVIES:
    g_guiSettings.SetString("scrapers.moviesdefault",addonID);
    break;
  case ADDON_SCRAPER_MUSICVIDEOS:
    g_guiSettings.SetString("scrapers.musicvideosdefault",addonID);
    break;
  case ADDON_SCRAPER_TVSHOWS:
    g_guiSettings.SetString("scrapers.tvshowsdefault",addonID);
    break;
  default:
    return false;
  }

  return true;
}

CStdString CAddonMgr::GetString(const CStdString &id, const int number)
{
  AddonPtr addon;
  if (GetAddon(id, addon))
    return addon->GetString(number);

  return "";
}

void CAddonMgr::FindAddons()
{
  CSingleLock lock(m_critSection);
  if (m_cpluff && m_cp_context)
    m_cpluff->scan_plugins(m_cp_context, CP_SP_UPGRADE);
}

void CAddonMgr::RemoveAddon(const CStdString& ID)
{
  if (m_cpluff && m_cp_context)
    m_cpluff->uninstall_plugin(m_cp_context,ID.c_str());
}

const char *CAddonMgr::GetTranslatedString(const cp_cfg_element_t *root, const char *tag)
{
  if (!root)
    return NULL;

  const cp_cfg_element_t *eng = NULL;
  for (unsigned int i = 0; i < root->num_children; i++)
  {
    const cp_cfg_element_t &child = root->children[i];
    if (strcmp(tag, child.name) == 0)
    { // see if we have a "lang" attribute
      const char *lang = m_cpluff->lookup_cfg_value((cp_cfg_element_t*)&child, "@lang");
      if (lang && 0 == strcmp(lang,g_langInfo.GetDVDAudioLanguage().c_str()))
        return child.value;
      if (!lang || 0 == strcmp(lang, "en"))
        eng = &child;
    }
  }
  return (eng) ? eng->value : NULL;
}

AddonPtr CAddonMgr::AddonFromProps(AddonProps& addonProps)
{
  switch (addonProps.type)
  {
    case ADDON_PLUGIN:
    case ADDON_SCRIPT:
      return AddonPtr(new CPluginSource(addonProps));
    case ADDON_SCRIPT_LIBRARY:
    case ADDON_SCRIPT_LYRICS:
    case ADDON_SCRIPT_WEATHER:
    case ADDON_SCRIPT_SUBTITLES:
    case ADDON_SCRIPT_MODULE:
    case ADDON_WEB_INTERFACE:
      return AddonPtr(new CAddon(addonProps));
    case ADDON_SERVICE:
      return AddonPtr(new CService(addonProps));
    case ADDON_SCRAPER_ALBUMS:
    case ADDON_SCRAPER_ARTISTS:
    case ADDON_SCRAPER_MOVIES:
    case ADDON_SCRAPER_MUSICVIDEOS:
    case ADDON_SCRAPER_TVSHOWS:
    case ADDON_SCRAPER_LIBRARY:
      return AddonPtr(new CScraper(addonProps));
    case ADDON_SKIN:
      return AddonPtr(new CSkinInfo(addonProps));
#if defined(HAS_VISUALISATION)
    case ADDON_VIZ:
      return AddonPtr(new CVisualisation(addonProps));
#endif
    case ADDON_SCREENSAVER:
      return AddonPtr(new CScreenSaver(addonProps));
    case ADDON_VIZ_LIBRARY:
      return AddonPtr(new CAddonLibrary(addonProps));
    case ADDON_REPOSITORY:
      return AddonPtr(new CRepository(addonProps));
    default:
      break;
  }
  return AddonPtr();
}

void CAddonMgr::UpdateRepos(bool force)
{
  CSingleLock lock(m_critSection);
  if (!force && m_watch.IsRunning() && m_watch.GetElapsedSeconds() < 600)
    return;
  m_watch.StartZero();
  VECADDONS addons;
  GetAddons(ADDON_REPOSITORY,addons);
  for (unsigned int i=0;i<addons.size();++i)
  {
    RepositoryPtr repo = boost::dynamic_pointer_cast<CRepository>(addons[i]);
    CDateTime lastUpdate = m_database.GetRepoTimestamp(repo->ID());
    if (force || !lastUpdate.IsValid() || lastUpdate + CDateTimeSpan(0,6,0,0) < CDateTime::GetCurrentDateTime())
    {
      CLog::Log(LOGDEBUG,"Checking repository %s for updates",repo->Name().c_str());
      CJobManager::GetInstance().AddJob(new CRepositoryUpdateJob(repo), NULL);
    }
  }
}

/*
 * libcpluff interaction
 */

bool CAddonMgr::PlatformSupportsAddon(const cp_plugin_info_t *plugin) const
{
  if (!plugin || !plugin->num_extensions)
    return false;
  const cp_extension_t *metadata = GetExtension(plugin, "xbmc.addon.metadata");
  if (!metadata)
    return false;

  vector<CStdString> platforms;
  if (CAddonMgr::Get().GetExtList(metadata->configuration, "platform", platforms))
  {
    for (unsigned int i = 0; i < platforms.size(); ++i)
    {
      if (platforms[i] == "all")
        return true;
#if defined(_LINUX) && !defined(__APPLE__)
      if (platforms[i] == "linux")
#elif defined(_WIN32) && defined(HAS_SDL_OPENGL)
      if (platforms[i] == "wingl")
#elif defined(_WIN32) && defined(HAS_DX)
      if (platforms[i] == "windx")
#elif defined(__APPLE__)
      if (platforms[i] == "osx")
#endif
        return true;
    }
    return false; // no <platform> works for us
  }
  return true; // assume no <platform> is equivalent to <platform>all</platform>
}

const cp_cfg_element_t *CAddonMgr::GetExtElement(cp_cfg_element_t *base, const char *path)
{
  const cp_cfg_element_t *element = NULL;
  if (base)
    element = m_cpluff->lookup_cfg_element(base, path);
  return element;
}

/* Returns all duplicate elements from a base element */
bool GetExtElementDeque(DEQUEELEMENTS &elements, cp_cfg_element_t *base, const char *path)
{
  if (!base)
    return false;

  unsigned int i = 0;
  while (true)
  {
    if (i >= base->num_children)
      break;
    CStdString temp = (base->children+i)->name;
    if (!temp.compare(path))
      elements.push_back(base->children+i);
    i++;
  }

  if (elements.empty()) return false;
  return true;
}

const cp_extension_t *CAddonMgr::GetExtension(const cp_plugin_info_t *props, const char *extension) const
{
  if (!props)
    return NULL;
  for (unsigned int i = 0; i < props->num_extensions; ++i)
  {
    if (0 == strcmp(props->extensions[i].ext_point_id, extension))
      return &props->extensions[i];
  }
  return NULL;
}

ADDONDEPS CAddonMgr::GetDeps(const CStdString &id)
{
  ADDONDEPS result;
  cp_status_t status;

  cp_plugin_info_t *info = m_cpluff->get_plugin_info(m_cp_context,id.c_str(),&status);
  if (info)
  {
    for (unsigned int i=0;i<info->num_imports;++i)
      result.insert(make_pair(CStdString(info->imports[i].plugin_id),
                              make_pair(AddonVersion(info->version),
                                        AddonVersion(info->version))));
    m_cpluff->release_info(m_cp_context, info);
  }

  return result;
}

CStdString CAddonMgr::GetExtValue(cp_cfg_element_t *base, const char *path)
{
  const char *value = NULL;
  if (base && (value = m_cpluff->lookup_cfg_value(base, path)))
    return CStdString(value);
  else return CStdString();
}

bool CAddonMgr::GetExtList(cp_cfg_element_t *base, const char *path, vector<CStdString> &result) const
{
  if (!base || !path)
    return false;
  CStdString all = m_cpluff->lookup_cfg_value(base, path);
  if (all.IsEmpty())
    return false;
  StringUtils::SplitString(all, " ", result);
  return true;
}

AddonPtr CAddonMgr::GetAddonFromDescriptor(const cp_plugin_info_t *info)
{
  if (!info || !info->extensions)
    return AddonPtr();

  // FIXME: If we want to support multiple extension points per addon, we'll need to extend this to not just take
  //        the first extension point (eg use the TYPE information we pass in)

  // grab a relevant extension point, ignoring our xbmc.addon.metadata extension point
  for (unsigned int i = 0; i < info->num_extensions; ++i)
  {
    if (0 != strcmp("xbmc.addon.metadata", info->extensions[i].ext_point_id))
    { // note that Factory takes care of whether or not we have platform support
      return Factory(&info->extensions[i]);
    }
  }
  return AddonPtr();
}

// FIXME: This function may not be required
bool CAddonMgr::LoadAddonDescription(const CStdString &path, AddonPtr &addon)
{
  cp_status_t status;
  cp_plugin_info_t *info = m_cpluff->load_plugin_descriptor(m_cp_context, _P(path).c_str(), &status);
  if (info)
  {
    addon = GetAddonFromDescriptor(info);
    m_cpluff->release_info(m_cp_context, info);
    return NULL != addon.get();
  }
  return false;
}

bool CAddonMgr::AddonsFromRepoXML(const TiXmlElement *root, VECADDONS &addons)
{
  // create a context for these addons
  cp_status_t status;
  cp_context_t *context = m_cpluff->create_context(&status);
  if (!root || !context)
    return false;

  // each addon XML should have a UTF-8 declaration
  TiXmlDeclaration decl("1.0", "UTF-8", "");
  const TiXmlElement *element = root->FirstChildElement("addon");
  while (element)
  {
    // dump the XML back to text
    std::string xml;
    xml << decl;
    xml << *element;
    cp_status_t status;
    cp_plugin_info_t *info = m_cpluff->load_plugin_descriptor_from_memory(context, xml.c_str(), xml.size(), &status);
    if (info)
    {
      AddonPtr addon = GetAddonFromDescriptor(info);
      if (addon.get())
        addons.push_back(addon);
      m_cpluff->release_info(context, info);
    }
    element = element->NextSiblingElement("addon");
  }
  m_cpluff->destroy_context(context);
  return true;
}

bool CAddonMgr::LoadAddonDescriptionFromMemory(const TiXmlElement *root, AddonPtr &addon)
{
  // create a context for these addons
  cp_status_t status;
  cp_context_t *context = m_cpluff->create_context(&status);
  if (!root || !context)
    return false;

  // dump the XML back to text
  std::string xml;
  xml << TiXmlDeclaration("1.0", "UTF-8", "");
  xml << *root;
  cp_plugin_info_t *info = m_cpluff->load_plugin_descriptor_from_memory(context, xml.c_str(), xml.size(), &status);
  if (info)
  {
    addon = GetAddonFromDescriptor(info);
    m_cpluff->release_info(context, info);
  }
  m_cpluff->destroy_context(context);
  return addon != NULL;
}

bool CAddonMgr::StartServices()
{
  CLog::Log(LOGDEBUG, "ADDON: Starting service addons.");

  VECADDONS services;
  if (!GetAddons(ADDON_SERVICE, services))
    return false;

  bool ret = true;
  for (IVECADDONS it = services.begin(); it != services.end(); ++it)
  {
    boost::shared_ptr<CService> service = boost::dynamic_pointer_cast<CService>(*it);
    if (service)
      ret &= service->Start();
  }

  return ret;
}

void CAddonMgr::StopServices()
{
  CLog::Log(LOGDEBUG, "ADDON: Stopping service addons.");

  VECADDONS services;
  if (!GetAddons(ADDON_SERVICE, services))
    return;

  for (IVECADDONS it = services.begin(); it != services.end(); ++it)
  {
    boost::shared_ptr<CService> service = boost::dynamic_pointer_cast<CService>(*it);
    if (service)
      service->Stop();
  }
}

int cp_to_clog(cp_log_severity_t lvl)
{
  if (lvl >= CP_LOG_ERROR)
    return LOGINFO;
  return LOGDEBUG;
}

cp_log_severity_t clog_to_cp(int lvl)
{
  if (lvl >= LOG_LEVEL_DEBUG)
    return CP_LOG_INFO;
  return CP_LOG_ERROR;
}

void cp_fatalErrorHandler(const char *msg)
{
  CLog::Log(LOGERROR, "ADDONS: CPluffFatalError(%s)", msg);
}

void cp_logger(cp_log_severity_t level, const char *msg, const char *apid, void *user_data)
{
  if(!apid)
    CLog::Log(cp_to_clog(level), "ADDON: cpluff: '%s'", msg);
  else
    CLog::Log(cp_to_clog(level), "ADDON: cpluff: '%s' reports '%s'", apid, msg);
}

} /* namespace ADDON */

