/*
    This file is part of libkabc.
    Copyright (c) 2001 Cornelius Schumacher <schumacher@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 as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    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., 59 Temple Place - Suite 330,
    Boston, MA 02111-1307, USA.
*/

/*
Enhanced Version of the file for platform independent KDE tools.
Copyright (c) 2004 Ulf Schenk

$Id$
*/

#include <sys/types.h>
#include <sys/stat.h>
#ifndef _WIN32_
#include <unistd.h>
#endif

#include <qfile.h>
#include <qfileinfo.h>
#include <qregexp.h>
#include <qtimer.h>

#include <kapplication.h>
#include <kconfig.h>
#include <kdebug.h>
#include <klocale.h>
//US #include <ksavefile.h>
#include <kstandarddirs.h>

#include "formatfactory.h"

#include "resource.h"
#include "resourcefileconfig.h"
#include "stdaddressbook.h"

//US #include "../../formats/vcardformatplugin2.h"
//US #include "../../formats/binaryformat.h"


#include "resourcefile.h"

using namespace KABC;

extern "C"
{
//US  void *init_kabc_file()
  void *init_microkabc_file()
  {
    return new KRES::PluginFactory<ResourceFile,ResourceFileConfig>();
  }
}


ResourceFile::ResourceFile( const KConfig *config )
  : Resource( config ) , mFormat( 0 )
{
  QString fileName, formatName;

  KConfig *cfg = (KConfig *)config;
  if ( cfg ) {
    fileName = cfg->readEntry( "FileName", StdAddressBook::fileName() );
    formatName = cfg->readEntry( "FileFormat", "vcard" );
//  qDebug("ResourceFile::ResourceFile : 1 %s, %s", fileName.latin1(), formatName.latin1() );
  } else {
    fileName = StdAddressBook::fileName();
    formatName = "vcard";
//  qDebug("ResourceFile::ResourceFile : 2 %s, %s", fileName.latin1(), formatName.latin1() );
  }

  init( fileName, formatName );
}

ResourceFile::ResourceFile( const QString &fileName,
                            const QString &formatName )
  : Resource( 0 )
{
//  qDebug("ResourceFile::ResourceFile : 3 %s, %s", fileName.latin1(), formatName.latin1());
  init( fileName, formatName );
}

void ResourceFile::init( const QString &fileName, const QString &formatName )
{
  mFormatName = formatName;

  FormatFactory *factory = FormatFactory::self();
  mFormat = factory->format( mFormatName );

  if ( !mFormat ) {
    mFormatName = "vcard";
    mFormat = factory->format( mFormatName );
  }

/*US
//US  qDebug("ResourceFile::init initialized with format %s ", formatName.latin1());
  if (mFormatName == "vcard") {
    mFormat = new VCardFormatPlugin2();
//    qDebug("ResourceFile::init format VCardFormatPlugin2");
  }
  else if (mFormatName == "binary") {
    mFormat = new BinaryFormat();
//    qDebug("ResourceFile::init format BinaryFormat");
  }
  else
    qDebug("ResourceFile::init format unknown !!! %s ", formatName.latin1());
*/

/*US we have no KDirWatch. SO simulate the signals from inside the apropriate methods
  connect( &mDirWatch, SIGNAL( dirty(const QString&) ), SLOT( fileChanged() ) );
  connect( &mDirWatch, SIGNAL( created(const QString&) ), SLOT( fileChanged() ) );
  connect( &mDirWatch, SIGNAL( deleted(const QString&) ), SLOT( fileChanged() ) );
*/

  setFileName( fileName );
}

ResourceFile::~ResourceFile()
{
  delete mFormat;
  mFormat = 0;
}

void ResourceFile::writeConfig( KConfig *config )
{
  Resource::writeConfig( config );

  config->writeEntry( "FileName", mFileName );
  config->writeEntry( "FileFormat", mFormatName );
  
//    qDebug("ResourceFile::writeConfig format %s, %s", mFileName.latin1(), mFormatName.latin1());
  
}

Ticket *ResourceFile::requestSaveTicket()
{
  kdDebug(5700) << "ResourceFile::requestSaveTicket()" << endl;

  if ( !addressBook() ) return 0;

  if ( !lock( mFileName ) ) {
    kdDebug(5700) << "ResourceFile::requestSaveTicket(): Unable to lock file '"
                  << mFileName << "'" << endl;
    return 0;
  }
  return createTicket( this );
}


bool ResourceFile::doOpen()
{
  QFile file( mFileName );

  if ( !file.exists() ) {
    // try to create the file
    bool ok = file.open( IO_WriteOnly );
    if ( ok )
      file.close();

    return ok;
  } else {
    if ( !file.open( IO_ReadWrite ) )
      return false;

    if ( file.size() == 0 ) {
      file.close();
      return true;
    }

    bool ok = mFormat->checkFormat( &file );
    file.close();

    return ok;
  }
}

void ResourceFile::doClose()
{
}

bool ResourceFile::load()
{
  kdDebug(5700) << "ResourceFile::load(): '" << mFileName << "'" << endl;

  QFile file( mFileName );
  if ( !file.open( IO_ReadOnly ) ) {
    addressBook()->error( i18n( "Unable to open file '%1'." ).arg( mFileName ) );
    return false;
  }

//    qDebug("ResourceFile::load format %s, %s", mFileName.latin1(), mFormatName.latin1());
  
  return mFormat->loadAll( addressBook(), this, &file );
}

bool ResourceFile::save( Ticket *ticket )
{
//    qDebug("ResourceFile::save format %s, %s", mFileName.latin1(), mFormatName.latin1());
  kdDebug(5700) << "ResourceFile::save()" << endl;

  // create backup file
  QString extension = "_" + QString::number( QDate::currentDate().dayOfWeek() );
   
/*US we use a simpler method to create a backupfile
    
  (void) KSaveFile::backupFile( mFileName, QString::null
  ,extension );

  KSaveFile saveFile( mFileName );
  bool ok = false;
  if ( saveFile.status() == 0 && saveFile.file() )
  {
    mFormat->saveAll( addressBook(), this, saveFile.file() );
    ok = saveFile.close();
  }
*/

//US ToDo: write backupfile  
  QFile info;
  info.setName( mFileName );
  bool ok = info.open( IO_WriteOnly );
  if ( ok ) {
    mFormat->saveAll( addressBook(), this, &info );
    
    info.close();
    ok = true;
  }
  else {
  
  }
  
  if ( !ok )
    addressBook()->error( i18n( "Unable to save file '%1'." ).arg( mFileName ) );
  
  delete ticket;
  unlock( mFileName );
  
  return ok;

  qDebug("ResourceFile::save has to be changed");
  return true;
}

bool ResourceFile::lock( const QString &fileName )
{
  kdDebug(5700) << "ResourceFile::lock()" << endl;

  QString fn = fileName;

//US change the implementation how the lockfilename is getting created
//US  fn.replace( QRegExp("/"), "_" );
//US  QString lockName = locateLocal( "data", "kabc/lock/" + fn + ".lock" );
  
  KURL url(fn);  
  QString lockName = locateLocal( "data", "kabc/lock/" + url.fileName() + ".lock" );
  
  kdDebug(5700) << "-- lock name: " << lockName << endl;

  if (QFile::exists( lockName )) return false;

  QString lockUniqueName;
  lockUniqueName = fn + KApplication::randomString( 8 );
  
  url = lockUniqueName;  
//US  mLockUniqueName = locateLocal( "data", "kabc/lock/" + lockUniqueName );
  mLockUniqueName = locateLocal( "data", "kabc/lock/" + url.fileName() );
  kdDebug(5700) << "-- lock unique name: " << mLockUniqueName << endl;

  // Create unique file
  QFile file( mLockUniqueName );
  file.open( IO_WriteOnly );
  file.close();

  // Create lock file
  int result = ::link( QFile::encodeName( mLockUniqueName ),
                       QFile::encodeName( lockName ) );

  if ( result == 0 ) {
    addressBook()->emitAddressBookLocked();
    return true;
  }

  // TODO: check stat

  return false;
}

void ResourceFile::unlock( const QString &fileName )
{
  QString fn = fileName;
//US change the implementation how the lockfilename is getting created
//US  fn.replace( QRegExp( "/" ), "_" );
//US  QString lockName = locateLocal( "data", "kabc/lock/" + fn + ".lock" );
//US  QString lockName = fn + ".lock";
  KURL url(fn);  
  QString lockName = locateLocal( "data", "kabc/lock/" + url.fileName() + ".lock" );
  
  QFile::remove( lockName );
  QFile::remove( mLockUniqueName );
  addressBook()->emitAddressBookUnlocked();
}

void ResourceFile::setFileName( const QString &fileName )
{
/*US ToDo: no synchronization so far. Has to be changed in the future
  mDirWatch.stopScan();
  mDirWatch.removeFile( mFileName );
*/
  mFileName = fileName;

  
/*US ToDo: no synchronization so far. Has to be changed in the future
  mDirWatch.addFile( mFileName );
  mDirWatch.startScan();
*/
//US simulate KDirWatch event  
  fileChanged();
}

QString ResourceFile::fileName() const
{
  return mFileName;
}

void ResourceFile::setFormat( const QString &format )
{
  mFormatName = format;
  delete mFormat;

  FormatFactory *factory = FormatFactory::self();
  mFormat = factory->format( mFormatName );
/*US  
//qDebug("ResourceFile::setFormat initialized with format %s ", format.latin1());
  if (mFormatName == "vcard") {
    mFormat = new VCardFormatPlugin2();
//    qDebug("ResourceFile::setFormat format %s", mFormatName.latin1());
  }
  else if (mFormatName == "binary") {
    mFormat = new BinaryFormat();
//    qDebug("ResourceFile::setFormat format %s", mFormatName.latin1());
  }
  else
    qDebug("ResourceFile::setFormat format unknown !!! %s ", format.latin1());
*/
  
}

QString ResourceFile::format() const
{
  return mFormatName;
}

void ResourceFile::fileChanged()
{
  // There is a small theoretical chance that KDirWatch calls us before
  // we are fully constructed
  if (!addressBook())
    return;
  load();
  addressBook()->emitAddressBookChanged();
}

void ResourceFile::removeAddressee( const Addressee &addr )
{
  QFile::remove( QFile::encodeName( locateLocal( "data", "kabc/photos/" ) + addr.uid() ) );
  QFile::remove( QFile::encodeName( locateLocal( "data", "kabc/logos/" ) + addr.uid() ) );
  QFile::remove( QFile::encodeName( locateLocal( "data", "kabc/sounds/" ) + addr.uid() ) );
}

void ResourceFile::cleanUp()
{
  unlock( mFileName );
}

//US #include "resourcefile.moc"