/* 
 *
 * $Id: k3bfileitem.cpp 619556 2007-01-03 17:38:12Z trueg $
 * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
 *
 * This file is part of the K3b project.
 * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.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 of the License, or
 * (at your option) any later version.
 * See the file "COPYING" for the exact licensing terms.
 */

#include <config.h>
#include <k3bglobals.h>

#include "k3bfileitem.h"
#include "k3bdatadoc.h"
#include "k3bdiritem.h"
#include "k3bisooptions.h"
#include <k3bglobals.h>

#include <tqfileinfo.h>
#include <tqstring.h>
#include <tqstringlist.h>
#include <tqregexp.h>
#include <tqfile.h>

#include <kurl.h>
#include <kdebug.h>

#include <errno.h>
#include <string.h>


bool operator==( const K3bFileItem::Id& id1, const K3bFileItem::Id& id2 )
{
  return ( id1.device == id2.device && id1.inode == id2.inode );
}


bool operator<( const K3bFileItem::Id& id1, const K3bFileItem::Id& id2 )
{
  if( id1.device == id2.device )
    return ( id1.inode < id2.inode );
  else
    return ( id1.device < id2.device );
}


bool operator>( const K3bFileItem::Id& id1, const K3bFileItem::Id& id2 )
{
  return !( id2 < id1 || id1 == id2 );
}



K3bFileItem::K3bFileItem( const TQString& filePath, K3bDataDoc* doc, K3bDirItem* dir, const TQString& k3bName, int flags )
  : K3bDataItem( doc, dir, flags ),
    m_replacedItemFromOldSession(0),
    m_localPath(filePath)
{
  if( k3bName.isEmpty() )
    m_k3bName = filePath.section( '/', -1 );
  else
    m_k3bName = k3bName;

  // we determine the size here to avoid problems with removed or renamed files
  // we need to use lstat here since for symlinks both TDE and TQt return the size of the file pointed to
  // instead the size of the link.
  k3b_struct_stat statBuf;
  if( k3b_lstat( TQFile::encodeName(filePath), &statBuf ) ) {
    m_size = K3b::filesize( filePath );
    m_id.inode = 0;
    m_id.device = 0;
    m_bSymLink = false;

    kdError() << "(KFileItem) lstat failed: " << strerror(errno) << endl;

    // since we have no proper inode info, disable the inode caching in the doc
    if( doc ) {
      K3bIsoOptions o( doc->isoOptions() );
      o.setDoNotCacheInodes( true );
      doc->setIsoOptions( o );
    }
  }
  else {
    m_size = (TDEIO::filesize_t)statBuf.st_size;

    m_bSymLink = S_ISLNK(statBuf.st_mode);

    //
    // integrate the device number into the inode since files on different
    // devices may have the same inode number!
    //
    m_id.inode = statBuf.st_ino;
    m_id.device = statBuf.st_dev;
  }

  m_idFollowed = m_id;
  m_sizeFollowed = m_size;

  if( isSymLink() ) {
    k3b_struct_stat statBuf;
    if( k3b_stat( TQFile::encodeName(filePath), &statBuf ) == 0 ) {
      m_idFollowed.inode = statBuf.st_ino;
      m_idFollowed.device = statBuf.st_dev;

      m_sizeFollowed = (TDEIO::filesize_t)statBuf.st_size;
    }
  }

  // add automagically like a qlistviewitem
  if( parent() )
    parent()->addDataItem( this );
}


K3bFileItem::K3bFileItem( const k3b_struct_stat* stat, 
			  const k3b_struct_stat* followedStat,
			  const TQString& filePath, K3bDataDoc* doc, K3bDirItem* dir, const TQString& k3bName )
  : K3bDataItem( doc, dir ),
    m_replacedItemFromOldSession(0),
    m_localPath(filePath)
{
  if( k3bName.isEmpty() )
    m_k3bName = filePath.section( '/', -1 );
  else
    m_k3bName = k3bName;

  m_size = (TDEIO::filesize_t)stat->st_size;
  m_bSymLink = S_ISLNK(stat->st_mode);

  //
  // integrate the device number into the inode since files on different
  // devices may have the same inode number!
  //
  m_id.inode = stat->st_ino;
  m_id.device = stat->st_dev;
  
  if( isSymLink() ) {
    m_idFollowed.inode = followedStat->st_ino;
    m_idFollowed.device = followedStat->st_dev;
    
    m_sizeFollowed = (TDEIO::filesize_t)followedStat->st_size;
  }
  else {
    m_idFollowed = m_id;
    m_sizeFollowed = m_size;
  }

  if( parent() )
    parent()->addDataItem( this );
}


K3bFileItem::K3bFileItem( const K3bFileItem& item )
  : K3bDataItem( item ),
    m_replacedItemFromOldSession(0),
    m_size( item.m_size ),
    m_sizeFollowed( item.m_sizeFollowed ),
    m_id( item.m_id ),
    m_idFollowed( item.m_idFollowed ),
    m_localPath( item.m_localPath ),
    m_bSymLink( item.m_bSymLink )
{
}


K3bFileItem::~K3bFileItem()
{
  // remove this from parentdir
  take();
}


K3bDataItem* K3bFileItem::copy() const
{
  return new K3bFileItem( *this );
}


TDEIO::filesize_t K3bFileItem::itemSize( bool followSymlinks ) const
{
  if( followSymlinks )
    return m_sizeFollowed;
  else
    return m_size;
}


K3bFileItem::Id K3bFileItem::localId() const
{
  return localId( doc() ? doc()->isoOptions().followSymbolicLinks() || !doc()->isoOptions().createRockRidge() : false );
}


K3bFileItem::Id K3bFileItem::localId( bool followSymlinks ) const
{
  if( followSymlinks )
    return m_idFollowed;
  else
    return m_id;
}


bool K3bFileItem::exists() const
{
  return true;
}

TQString K3bFileItem::absIsoPath()
{
  //	return m_dir->absIsoPath() + m_isoName;
  return TQString();
}


TQString K3bFileItem::localPath() const
{
  return m_localPath;
}

K3bDirItem* K3bFileItem::getDirItem() const
{
  return getParent();
}


bool K3bFileItem::isSymLink() const
{
  return m_bSymLink;
}


TQString K3bFileItem::linkDest() const
{
  return TQFileInfo( localPath() ).readLink();
}


bool K3bFileItem::isValid() const
{
  if( isSymLink() ) {

    // this link is not valid if we cannot follow it if we want to
    if( doc()->isoOptions().followSymbolicLinks() ) {
      return TQFile::exists( K3b::resolveLink( localPath() ) );
    }

    TQString dest = linkDest();

    if( dest[0] == '/' )
      return false;  // absolut links can never be part of the compilation!

    // parse the link
    K3bDirItem* dir = getParent();

    TQStringList tokens = TQStringList::split( TQRegExp("/+"), dest );  // two slashes or more do the same as one does!

    unsigned int i = 0;
    while( i < tokens.size() ) {
      if( tokens[i] == "." ) {
	// ignore it
      }
      else if( tokens[i] == ".." ) {
	// change the directory
	dir = dir->parent();
	if( dir == 0 )
	  return false;
      }
      else {
	// search for the item in dir
	K3bDataItem* d = dir->find( tokens[i] );
	if( d == 0 )
	  return false;

	if( d->isDir() ) {
	  // change directory
	  dir = (K3bDirItem*)d;
	}
	else {
	  if( i+1 != tokens.size() )
	    return false;  // if di is a file we need to be at the last token
	  else
	    return (dest[dest.length()-1] != '/');   // if the link destination ends with a slash
                                            	   // it can only point to a directory!
	}
      }

      i++;
    }

    return true;
  }
  else
    return true;
}
