/*  This file is part of the KDE libraries
 *  Copyright (C) 1999 David Faure   <faure@kde.org>
 *                1999 Waldo Bastian <bastian@kde.org>
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Library General Public
 *  License version 2 as published by the Free Software Foundation;
 *
 *  This library 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
 *  Library General Public License for more details.
 *
 *  You should have received a copy of the GNU Library General Public License
 *  along with this library; see the file COPYING.LIB.  If not, write to
 *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 *  Boston, MA 02110-1301, USA.
 **/

#include "kbuildservicefactory.h"
#include "ksycoca.h"
#include "ksycocadict.h"
#include "kresourcelist.h"
#include "kmimetype.h"

#include <kglobal.h>
#include <kstandarddirs.h>
#include <kmessageboxwrapper.h>
#include <klocale.h>
#include <kdebug.h>
#include <assert.h>

KBuildServiceFactory::KBuildServiceFactory( KSycocaFactory *serviceTypeFactory,
	KBuildServiceGroupFactory *serviceGroupFactory ) :
  KServiceFactory(),
  m_serviceDict(977),
  m_dupeDict(977),
  m_serviceTypeFactory( serviceTypeFactory ),
  m_serviceGroupFactory( serviceGroupFactory )
{
   m_resourceList = new KSycocaResourceList();
//   m_resourceList->add( "apps", "*.desktop" );
//   m_resourceList->add( "apps", "*.kdelnk" );
   m_resourceList->add( "services", "*.desktop" );
   m_resourceList->add( "services", "*.kdelnk" );
}

// return all service types for this factory
// i.e. first arguments to m_resourceList->add() above
QStringList KBuildServiceFactory::resourceTypes()
{
    return QStringList() << "apps" << "services";
}

KBuildServiceFactory::~KBuildServiceFactory()
{
   delete m_resourceList;
}

KService * KBuildServiceFactory::findServiceByName(const QString &_name)
{
   return m_serviceDict[_name];
}


KSycocaEntry *
KBuildServiceFactory::createEntry( const QString& file, const char *resource )
{
  QString name = file;
  int pos = name.findRev('/');
  if (pos != -1)
  {
     name = name.mid(pos+1);
  }

  if (name.isEmpty())
     return 0;

  // Is it a .desktop file?
  if (!name.endsWith(".desktop") && !name.endsWith(".kdelnk"))
      return 0;

  KDesktopFile desktopFile(file, true, resource);

  KService * serv = new KService( &desktopFile );

  if ( serv->isValid() && !serv->isDeleted() ) 
  {
     return serv;
  } else {
     if (!serv->isDeleted())
        kdWarning(7012) << "Invalid Service : " << file << endl;
     delete serv;
     return 0L;
  }
}


void
KBuildServiceFactory::saveHeader(QDataStream &str)
{
   KSycocaFactory::saveHeader(str);

   str << (Q_INT32) m_nameDictOffset;
   str << (Q_INT32) m_relNameDictOffset;
   str << (Q_INT32) m_offerListOffset;
   str << (Q_INT32) m_initListOffset;
   str << (Q_INT32) m_menuIdDictOffset;
}

void
KBuildServiceFactory::save(QDataStream &str)
{
   KSycocaFactory::save(str);

   m_nameDictOffset = str.device()->at();
   m_nameDict->save(str);

   m_relNameDictOffset = str.device()->at();
   m_relNameDict->save(str);

   saveOfferList(str);
   saveInitList(str);

   m_menuIdDictOffset = str.device()->at();
   m_menuIdDict->save(str);

   int endOfFactoryData = str.device()->at();

   // Update header (pass #3)
   saveHeader(str);


   // Seek to end.
   str.device()->at(endOfFactoryData);
}

void
KBuildServiceFactory::saveOfferList(QDataStream &str)
{
   m_offerListOffset = str.device()->at();

   bool isNumber;
   for(QDictIterator<KSycocaEntry::Ptr> itserv ( *m_entryDict );
       itserv.current();
       ++itserv)
   {
      KService *service = (KService *) ((KSycocaEntry *)(*itserv.current()));
      QStringList serviceTypeList = service->serviceTypes();
      KServiceType::List serviceTypes;
      QStringList::ConstIterator it = serviceTypeList.begin();
      for( ; it != serviceTypeList.end(); ++it )
      {
         (*it).toInt(&isNumber);
         if (isNumber)
            continue;
         
         KServiceType::Ptr serviceType = KServiceType::serviceType(*it);
         if (!serviceType)
         {
           kdWarning() << "'"<< service->desktopEntryPath() << "' specifies undefined mimetype/servicetype '"<< (*it) << "'" << endl;
           continue;
         }
         serviceTypes.append(serviceType);
      }
      
      while(serviceTypes.count())
      {
         KServiceType::Ptr serviceType = serviceTypes.first();
         serviceTypes.pop_front();
         
         KServiceType::Ptr parentType = serviceType->parentType();
         if (parentType)
            serviceTypes.append(parentType);
         
         serviceType->addService(service);
      }
   }

   // For each entry in servicetypeFactory
   for(QDictIterator<KSycocaEntry::Ptr> it ( *(m_serviceTypeFactory->entryDict()) );
       it.current();
       ++it)
   {
      // export associated services
      KServiceType *entry = static_cast<KServiceType*>(static_cast<KSycocaEntry*>(*it.current()));
      KService::List services = entry->services();
  
      for(KService::List::ConstIterator it2 = services.begin();
          it2 != services.end(); ++it2)
      {
         KService *service = *it2;
         str << (Q_INT32) entry->offset();
         str << (Q_INT32) service->offset();
      }
   }

   str << (Q_INT32) 0;               // End of list marker (0)
}

void
KBuildServiceFactory::saveInitList(QDataStream &str)
{
   m_initListOffset = str.device()->at();

   KService::List initList;

   for(QDictIterator<KSycocaEntry::Ptr> itserv ( *m_entryDict );
       itserv.current();
       ++itserv)
   {
      KService::Ptr service = (KService *) ((KSycocaEntry *) *itserv.current());
      if ( !service->init().isEmpty() )
      {
          initList.append(service); 
      }
   }
   str << (Q_INT32) initList.count(); // Nr of init services.
   for(KService::List::Iterator it = initList.begin();
       it != initList.end();
       ++it)
   {
      str << (Q_INT32) (*it)->offset();
   }
}

void
KBuildServiceFactory::addEntry(KSycocaEntry *newEntry, const char *resource)
{
   if (m_dupeDict.find(newEntry))
      return;

   KSycocaFactory::addEntry(newEntry, resource);

   KService * service = (KService *) newEntry;
   m_dupeDict.insert(newEntry, service);

   if (!service->isDeleted())
   {
      QString parent = service->parentApp();
      if (!parent.isEmpty())
         m_serviceGroupFactory->addNewChild(parent, resource, service);
   }

   QString name = service->desktopEntryName();
   m_nameDict->add( name, newEntry );
   m_serviceDict.replace(name, service);

   QString relName = service->desktopEntryPath();
   m_relNameDict->add( relName, newEntry );
   QString menuId = service->menuId();
   if (!menuId.isEmpty())
      m_menuIdDict->add( menuId, newEntry );
}
