/***************************************************************************
                           cfilemanager.cpp  -  description
                             -------------------
    begin                : Sat Aug 03 2002
    copyright            : (C) 2002-2005 by Mathias Küster
    email                : mathen@users.berlios.de
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/

#include "cfilemanager.h"

#ifndef WIN32
#include <unistd.h>
#include <stdlib.h>
#endif

#include <string.h>

#include "core/cbytearray.h"
#include "cconfig.h"
#include "core/cdir.h"
#include "core/cfile.h"
#include "cdownloadmanager.h"
#include "dclib.h"
#include "core/cmanager.h"
#include "csearchindex.h"
#include "csharelist.h"
#include "core/cbase32.h"
#include "core/ccallback.h"

#include "hash/compat.h"
#include "hash/MerkleTree.h"

/* for proper case-insensitive searching */
#include "ccasefolder.h"

/** */
CFileManager::CFileManager()
{
	m_pFileNameSet    = 0;
	m_nFileBaseIndex  = 0;
	m_pSearchIndex    = new CSearchIndex();
	m_pShareList      = new CShareList();
	m_pHashMemory     = 0;

	m_pFileManagerInfo = new CFileManagerInfo();
	m_pFileManagerInfo->m_nProgress = 100;

	InitFileTypeList();

	if ( m_pShareList->Load() )
	{
		if ( m_pSearchIndex->LoadedOK() ) // CSearchIndex::LoadIndex() is called in CSearchIndex constructor
		{
			m_pShareList->CreateList(m_pSearchIndex);
		}
		else
		{
			CreateShareList();
		}
		
		if ( CConfig::Instance()->GetAutoRecreateShareList() )
		{
			// this test was always broken before since m_pShareList->CreateList(m_pSearchIndex) had not been called
			// let's just always re-create it if the setting is true
			// it is unlikely but possible that files have changed but the total size is still the same
			//if ( CalcShareSize() != m_pShareList->GetShareSize() )
			//{
				CreateShareList();
			//}
		}
	}
	else
	{
		CreateShareList();
	}

	if ( CConfig::Instance()->GetRecreateShareListTime() != 0 )
	{
		m_tCreateShareListTimeout = time(0)+CConfig::Instance()->GetRecreateShareListTime()*60*60;
	}
	else
	{
		m_tCreateShareListTimeout = 0;
	}

	// init timer
	m_pFileManagerCallback = new CCallback0<CFileManager>( this, &CFileManager::FileManagerCallback );
	CManager::Instance()->Add( m_pFileManagerCallback );
}

/** */
CFileManager::~CFileManager()
{
	Stop();

	SetInstance(0);

	Lock();

	if ( m_pFileManagerCallback )
	{
		CManager::Instance()->Remove(m_pFileManagerCallback);
		delete m_pFileManagerCallback;
	}

	if ( m_pFileNameSet )
	{
		m_pFileNameSet->clear();
		delete m_pFileNameSet;
		m_pFileNameSet = 0;
	}

	delete m_pFileManagerInfo;
	m_pFileManagerInfo = 0;

	m_FileTypeMap.clear();

	delete m_pShareList;
	m_pShareList = 0;

	delete m_pSearchIndex;
	m_pSearchIndex = 0;

	delete m_pHashMemory;
	m_pHashMemory = 0;

	UnLock();
}

/** */
int CFileManager::GetShareBuffer( eShareBufferType type, CByteArray * sharebuffer, bool decompress )
{
	return m_pShareList->GetShareBuffer( type, sharebuffer, decompress );
}

/** */
void CFileManager::GetPartialListing( const CString & dir, CString & result, int depth )
{
	m_pShareList->GetPartialListing( dir, result, depth, m_pSearchIndex );
}

/** */
unsigned long CFileManager::GetShareBufferSize( eShareBufferType type )
{
	return m_pShareList->GetShareBufferSize(type);
}

/** */
ulonglong CFileManager::GetShareSize()
{
	if ( m_pShareList )
		return m_pShareList->GetShareSize();
	else
		return 0;
}

/** */
void CFileManager::InitFileTypeList()
{
	m_FileTypeMap.clear();
	
	// MP3 2
	m_FileTypeMap["A52"]  = eftMP3;
	m_FileTypeMap["AAC"]  = eftMP3;
	m_FileTypeMap["AC3"]  = eftMP3;
	m_FileTypeMap["APE"]  = eftMP3;
	m_FileTypeMap["AIFF"] = eftMP3;
	m_FileTypeMap["AU"]   = eftMP3;
	m_FileTypeMap["DTS"]  = eftMP3;
	m_FileTypeMap["FLA"]  = eftMP3;
	m_FileTypeMap["FLAC"] = eftMP3;
	m_FileTypeMap["MID"]  = eftMP3;
	m_FileTypeMap["MOD"]  = eftMP3;
	m_FileTypeMap["M4A"]  = eftMP3;
	m_FileTypeMap["M4P"]  = eftMP3;
	m_FileTypeMap["MPC"]  = eftMP3;
	m_FileTypeMap["MP1"]  = eftMP3;
	m_FileTypeMap["MP2"]  = eftMP3;
	m_FileTypeMap["MP3"]  = eftMP3;
	m_FileTypeMap["OGG"]  = eftMP3;
	m_FileTypeMap["RA"]   = eftMP3;
	m_FileTypeMap["SHN"]  = eftMP3;
	m_FileTypeMap["SPX"]  = eftMP3;
	m_FileTypeMap["WAV"]  = eftMP3;
	m_FileTypeMap["WMA"]  = eftMP3;
	m_FileTypeMap["WV"]   = eftMP3;

	// ARCHIVE 3
	m_FileTypeMap["7Z"]  = eftARCHIVE;
	m_FileTypeMap["ACE"] = eftARCHIVE;
	m_FileTypeMap["BZ2"] = eftARCHIVE;
	m_FileTypeMap["CAB"] = eftARCHIVE;
	m_FileTypeMap["EX_"] = eftARCHIVE;
	m_FileTypeMap["GZ"]  = eftARCHIVE;
	m_FileTypeMap["LZH"] = eftARCHIVE;
	m_FileTypeMap["RAR"] = eftARCHIVE;
	m_FileTypeMap["RPM"] = eftARCHIVE;
	m_FileTypeMap["TAR"] = eftARCHIVE;
	m_FileTypeMap["TGZ"] = eftARCHIVE;
	m_FileTypeMap["ZIP"] = eftARCHIVE;
	m_FileTypeMap["ZOO"] = eftARCHIVE;
	m_FileTypeMap["Z"]   = eftARCHIVE;

	// DOCUMENT 4
	m_FileTypeMap["CFG"]   = eftDOCUMENT;
	m_FileTypeMap["CONF"]  = eftDOCUMENT;
	m_FileTypeMap["CPP"]   = eftDOCUMENT;
	m_FileTypeMap["CSS"]   = eftDOCUMENT;
	m_FileTypeMap["C"]     = eftDOCUMENT;
	m_FileTypeMap["DIZ"]   = eftDOCUMENT;
	m_FileTypeMap["DOC"]   = eftDOCUMENT;
	m_FileTypeMap["H"]     = eftDOCUMENT;
	m_FileTypeMap["HLP"]   = eftDOCUMENT;
	m_FileTypeMap["HTM"]   = eftDOCUMENT;
	m_FileTypeMap["HTML"]  = eftDOCUMENT;
	m_FileTypeMap["INI"]   = eftDOCUMENT;
	m_FileTypeMap["INF"]   = eftDOCUMENT;
	m_FileTypeMap["LOG"]   = eftDOCUMENT;
	m_FileTypeMap["NFO"]   = eftDOCUMENT;
	m_FileTypeMap["ODG"]   = eftDOCUMENT;
	m_FileTypeMap["ODP"]   = eftDOCUMENT;
	m_FileTypeMap["ODS"]   = eftDOCUMENT;
	m_FileTypeMap["ODT"]   = eftDOCUMENT;
	m_FileTypeMap["PDF"]   = eftDOCUMENT;
	m_FileTypeMap["PHP"]   = eftDOCUMENT;
	m_FileTypeMap["PPT"]   = eftDOCUMENT;
	m_FileTypeMap["PS"]    = eftDOCUMENT;
	m_FileTypeMap["PDF"]   = eftDOCUMENT;
	m_FileTypeMap["SHTML"] = eftDOCUMENT;
	m_FileTypeMap["SXC"]   = eftDOCUMENT;
	m_FileTypeMap["SXD"]   = eftDOCUMENT;
	m_FileTypeMap["SXI"]   = eftDOCUMENT;
	m_FileTypeMap["SXW"]   = eftDOCUMENT;
	m_FileTypeMap["TXT"]   = eftDOCUMENT;
	m_FileTypeMap["RFT"]   = eftDOCUMENT;
	m_FileTypeMap["RDF"]   = eftDOCUMENT;
	m_FileTypeMap["XML"]   = eftDOCUMENT;
	m_FileTypeMap["XLS"]   = eftDOCUMENT;

	// APPL 5
	m_FileTypeMap["BAT"] = eftAPPLICATION;
	m_FileTypeMap["CGI"] = eftAPPLICATION;
	m_FileTypeMap["COM"] = eftAPPLICATION;
	m_FileTypeMap["DLL"] = eftAPPLICATION;
	m_FileTypeMap["EXE"] = eftAPPLICATION;
	m_FileTypeMap["HQX"] = eftAPPLICATION;
	m_FileTypeMap["JS"]  = eftAPPLICATION;
	m_FileTypeMap["SH"]  = eftAPPLICATION;
	m_FileTypeMap["SO"]  = eftAPPLICATION;
	m_FileTypeMap["SYS"] = eftAPPLICATION;
	m_FileTypeMap["VXD"] = eftAPPLICATION;

	// PICTURE 6
	m_FileTypeMap["3DS"]  = eftPICTURE;
	m_FileTypeMap["A11"]  = eftPICTURE;
	m_FileTypeMap["ACB"]  = eftPICTURE;
	m_FileTypeMap["ADC"]  = eftPICTURE;
	m_FileTypeMap["ADI"]  = eftPICTURE;
	m_FileTypeMap["AFI"]  = eftPICTURE;
	m_FileTypeMap["AI"]   = eftPICTURE;
	m_FileTypeMap["AIS"]  = eftPICTURE;
	m_FileTypeMap["ANS"]  = eftPICTURE;
	m_FileTypeMap["ART"]  = eftPICTURE;
	m_FileTypeMap["B8"]   = eftPICTURE;
	m_FileTypeMap["BMP"]  = eftPICTURE;
	m_FileTypeMap["CBM"]  = eftPICTURE;
	m_FileTypeMap["EPS"]  = eftPICTURE;
	m_FileTypeMap["GIF"]  = eftPICTURE;
	m_FileTypeMap["ICO"]  = eftPICTURE;
	m_FileTypeMap["IMG"]  = eftPICTURE;
	m_FileTypeMap["JPEG"] = eftPICTURE;
	m_FileTypeMap["JPG"]  = eftPICTURE;
	m_FileTypeMap["PCT"]  = eftPICTURE;
	m_FileTypeMap["PCX"]  = eftPICTURE;
	m_FileTypeMap["PIC"]  = eftPICTURE;
	m_FileTypeMap["PICT"] = eftPICTURE;
	m_FileTypeMap["PNG"]  = eftPICTURE;
	m_FileTypeMap["PS"]   = eftPICTURE;
	m_FileTypeMap["PSP"]  = eftPICTURE;
	m_FileTypeMap["RLE"]  = eftPICTURE;
	m_FileTypeMap["TGA"]  = eftPICTURE;
	m_FileTypeMap["TIF"]  = eftPICTURE;
	m_FileTypeMap["TIFF"] = eftPICTURE;
	m_FileTypeMap["XPM"]  = eftPICTURE;

	// VIDEO 7
	m_FileTypeMap["AVI"]   = eftVIDEO;
	m_FileTypeMap["ASF"]   = eftVIDEO;
	m_FileTypeMap["ASX"]   = eftVIDEO;
	m_FileTypeMap["DAT"]   = eftVIDEO;
	m_FileTypeMap["DIVX"]  = eftVIDEO;
	m_FileTypeMap["DV"]    = eftVIDEO;
	m_FileTypeMap["FLV"]   = eftVIDEO;
	m_FileTypeMap["M1V"]   = eftVIDEO;
	m_FileTypeMap["M2V"]   = eftVIDEO;
	m_FileTypeMap["M4V"]   = eftVIDEO;
	m_FileTypeMap["MKV"]   = eftVIDEO;
	m_FileTypeMap["MOV"]   = eftVIDEO;
	m_FileTypeMap["MOVIE"] = eftVIDEO;
	m_FileTypeMap["MP4"]   = eftVIDEO;
	m_FileTypeMap["MPEG"]  = eftVIDEO;
	m_FileTypeMap["MPEG1"] = eftVIDEO;
	m_FileTypeMap["MPEG2"] = eftVIDEO;
	m_FileTypeMap["MPEG4"] = eftVIDEO;
	m_FileTypeMap["MPG"]   = eftVIDEO;
	m_FileTypeMap["OGM"]   = eftVIDEO;
	m_FileTypeMap["PXP"]   = eftVIDEO;
	m_FileTypeMap["QT"]    = eftVIDEO;
	m_FileTypeMap["RM"]    = eftVIDEO;
	m_FileTypeMap["RMVB"]  = eftVIDEO;
	m_FileTypeMap["VIV"]   = eftVIDEO;
	m_FileTypeMap["VOB"]   = eftVIDEO;
	m_FileTypeMap["WMV"]   = eftVIDEO;
}

/** return the filetype */
eFileTypes CFileManager::GetFileType( CString file )
{
	// set the filetype
	CString ext = CDir::Extension(file).ToUpper();

	if ( ext.NotEmpty() )
	{
		FileTypeMap::const_iterator it = m_FileTypeMap.find(ext);
		
		if ( it != m_FileTypeMap.end() )
		{
			return it->second;
		}
		
#if 0
		printf("unknown extension: %s\n",ext.Data());
#endif
	}

	return eftUNKNOWN;
}

/** */
std::set<unsigned long> * CFileManager::Search( unsigned int maxresults, std::list<CString> * words )
{
	CString filename;
	struct filebaseobject fbo;
	std::list<CString>::const_iterator it;
	bool notfound = false;
	unsigned int count = 0;
	std::set<unsigned long> * results = new std::set<unsigned long>;
	
	/* CSearchIndex::IndexCount() does a division */
	const unsigned long indexcount = m_pSearchIndex->IndexCount();
	
	for ( unsigned long fbi = 0; fbi < indexcount; ++fbi )
	{
		/* for speed the return value or if filename is empty are not checked */
		m_pSearchIndex->GetCaseFoldedName(fbi,&fbo,filename);
		
		notfound = false;
		for ( it = words->begin(); it != words->end(); ++it )
		{
			if ( filename.Find(*it) == -1 )
			{
				notfound = true;
				break;
			}
		}
		
		if ( notfound )
		{
			continue;
		}
		
		results->insert(fbi);
		++count;
		if ( count == maxresults )
		{
			break;
		}
	}
	
	return results;
}

/** */
std::set<unsigned long> * CFileManager::Search( unsigned int maxresults, std::list<CString> * words, eFileTypes etype )
{
	CString filename;
	struct filebaseobject fbo;
	std::list<CString>::const_iterator it;
	bool notfound = false;
	unsigned int count = 0;
	std::set<unsigned long> * results = new std::set<unsigned long>;
	
	/* stupid compiler warning */
	const unsigned int type = (unsigned int) etype;
	
	/* CSearchIndex::IndexCount() does a division */
	const unsigned long indexcount = m_pSearchIndex->IndexCount();
	
	for ( unsigned long fbi = 0; fbi < indexcount; ++fbi )
	{
		/* for speed the return value or if filename is empty are not checked */
		m_pSearchIndex->GetCaseFoldedName(fbi,&fbo,filename);
		
		if ( fbo.m_eFileType != type )
		{
			continue;
		}
		
		notfound = false;
		for ( it = words->begin(); it != words->end(); ++it )
		{
			if ( filename.Find(*it) == -1 )
			{
				notfound = true;
				break;
			}
		}
		
		if ( notfound )
		{
			continue;
		}
		
		results->insert(fbi);
		++count;
		if ( count == maxresults )
		{
			break;
		}
	}
	
	return results;
}

/** */
std::set<unsigned long> * CFileManager::SearchAtLeast( unsigned int maxresults, std::list<CString> * words, ulonglong size )
{
	CString filename;
	struct filebaseobject fbo;
	std::list<CString>::const_iterator it;
	bool notfound = false;
	unsigned int count = 0;
	std::set<unsigned long> * results = new std::set<unsigned long>;
	
	/* CSearchIndex::IndexCount() does a division */
	const unsigned long indexcount = m_pSearchIndex->IndexCount();
	
	for ( unsigned long fbi = 0; fbi < indexcount; ++fbi )
	{
		/* for speed the return value or if filename is empty are not checked */
		m_pSearchIndex->GetCaseFoldedName(fbi,&fbo,filename);
		
		if ( fbo.m_nSize < size )
		{
			continue;
		}
		
		notfound = false;
		for ( it = words->begin(); it != words->end(); ++it )
		{
			if ( filename.Find(*it) == -1 )
			{
				notfound = true;
				break;
			}
		}
		
		if ( notfound )
		{
			continue;
		}
		
		results->insert(fbi);
		++count;
		if ( count == maxresults )
		{
			break;
		}
	}
	
	return results;
}

/** */
std::set<unsigned long> * CFileManager::SearchAtLeast( unsigned int maxresults, std::list<CString> * words, ulonglong size, eFileTypes etype )
{
	CString filename;
	struct filebaseobject fbo;
	std::list<CString>::const_iterator it;
	bool notfound = false;
	unsigned int count = 0;
	std::set<unsigned long> * results = new std::set<unsigned long>;
	
	/* stupid compiler warning */
	const unsigned int type = (unsigned int) etype;
	
	/* CSearchIndex::IndexCount() does a division */
	const unsigned long indexcount = m_pSearchIndex->IndexCount();
	
	for ( unsigned long fbi = 0; fbi < indexcount; ++fbi )
	{
		/* for speed the return value or if filename is empty are not checked */
		m_pSearchIndex->GetCaseFoldedName(fbi,&fbo,filename);
		
		if ( fbo.m_eFileType != type )
		{
			continue;
		}
		
		if ( fbo.m_nSize < size )
		{
			continue;
		}
		
		notfound = false;
		for ( it = words->begin(); it != words->end(); ++it )
		{
			if ( filename.Find(*it) == -1 )
			{
				notfound = true;
				break;
			}
		}
		
		if ( notfound )
		{
			continue;
		}
		
		results->insert(fbi);
		++count;
		if ( count == maxresults )
		{
			break;
		}
	}
	
	return results;
}

/** */
std::set<unsigned long> * CFileManager::SearchAtMost( unsigned int maxresults, std::list<CString> * words, ulonglong size )
{
	CString filename;
	struct filebaseobject fbo;
	std::list<CString>::const_iterator it;
	bool notfound = false;
	unsigned int count = 0;
	std::set<unsigned long> * results = new std::set<unsigned long>;
	
	/* CSearchIndex::IndexCount() does a division */
	const unsigned long indexcount = m_pSearchIndex->IndexCount();
	
	for ( unsigned long fbi = 0; fbi < indexcount; ++fbi )
	{
		/* for speed the return value or if filename is empty are not checked */
		m_pSearchIndex->GetCaseFoldedName(fbi,&fbo,filename);
		
		if ( fbo.m_nSize > size )
		{
			continue;
		}
		
		notfound = false;
		for ( it = words->begin(); it != words->end(); ++it )
		{
			if ( filename.Find(*it) == -1 )
			{
				notfound = true;
				break;
			}
		}
		
		if ( notfound )
		{
			continue;
		}
		
		results->insert(fbi);
		++count;
		if ( count == maxresults )
		{
			break;
		}
	}
	
	return results;
}

/** */
std::set<unsigned long> * CFileManager::SearchAtMost( unsigned int maxresults, std::list<CString> * words, ulonglong size, eFileTypes etype )
{
	CString filename;
	struct filebaseobject fbo;
	std::list<CString>::const_iterator it;
	bool notfound = false;
	unsigned int count = 0;
	std::set<unsigned long> * results = new std::set<unsigned long>;
	
	/* stupid compiler warning */
	const unsigned int type = (unsigned int) etype;
	
	/* CSearchIndex::IndexCount() does a division */
	const unsigned long indexcount = m_pSearchIndex->IndexCount();
	
	for ( unsigned long fbi = 0; fbi < indexcount; ++fbi )
	{
		/* for speed the return value or if filename is empty are not checked */
		m_pSearchIndex->GetCaseFoldedName(fbi,&fbo,filename);
		
		if ( fbo.m_eFileType != type )
		{
			continue;
		}
		
		if ( fbo.m_nSize > size )
		{
			continue;
		}
		
		notfound = false;
		for ( it = words->begin(); it != words->end(); ++it )
		{
			if ( filename.Find(*it) == -1 )
			{
				notfound = true;
				break;
			}
		}
		
		if ( notfound )
		{
			continue;
		}
		
		results->insert(fbi);
		++count;
		if ( count == maxresults )
		{
			break;
		}
	}
	
	return results;
}

/** */
std::set<unsigned long> * CFileManager::SearchHash( CString s )
{
	CByteArray dst;
	
	if ( !m_pSearchIndex )
		return 0;

	//if ( m_pFileManagerInfo->m_eFileManagerStatus != efmsNONE )
	//	return 0;
	
	if ( CBase32::Decode(&dst,&s) != dcpp::TigerTree::BYTES )
		return 0;

	return m_pSearchIndex->SearchHash(dst.Data());
}

/** */
CString CFileManager::GetHash( unsigned long hbi )
{
	if ( !m_pSearchIndex )
		return CString();

	//if ( m_pFileManagerInfo->m_eFileManagerStatus != efmsNONE )
	//	return CString();
	
	return m_pSearchIndex->GetHash(hbi);
}

/** */
bool CFileManager::GetFileBaseObject( unsigned long index, struct filebaseobject * fbo, CString & filename )
{
	if ( !m_pSearchIndex )
		return false;

	//if ( m_pFileManagerInfo->m_eFileManagerStatus != efmsNONE )
	//	return false;

	return m_pSearchIndex->GetFileBaseObject(index,fbo,filename);
}

/** */
bool CFileManager::CreateShareList()
{
	bool err = false;

	if ( m_pFileManagerInfo->m_eFileManagerStatus != efmsNONE )
		return err;
	
	Lock();

	if ( Start() == -1 )
	{
		UnLock();
		return err;
	}
	
	/* CConfig::GetSharedFolders clears the list */

	m_pShareFolder = 0;
	m_sShareIndexBuffer.Empty();

	if ( CConfig::Instance()->GetSharedFolders(&m_SharedFolders) > 0 )
	{
		m_pSearchIndex->PrepareUpdate();
		
		if ( m_pFileNameSet )
		{
			m_pFileNameSet->clear();
			delete m_pFileNameSet;
		}
		m_pFileNameSet  = new FileNameSet();

		m_pFileManagerInfo->m_nProgress = 0;
		m_pFileManagerInfo->m_eFileManagerStatus = efmsCREATESHARELIST;

		if ( CDownloadManager::Instance() )
			CDownloadManager::Instance()->SendFileManagerInfo(m_pFileManagerInfo);

		err = true;
	}
	else
	{
		m_pShareList->SetIndexBuffer( m_sShareIndexBuffer );
		m_pShareList->CreateList(m_pSearchIndex);

		m_pSearchIndex->SaveIndex();
		
		UnLock();
		Stop();
		
		return err;
	}

	UnLock();

	return err;
}

void CFileManager::CreateSearchIndex()
{
	if ( m_pFileManagerInfo->m_eFileManagerStatus == efmsIDLE )
	{
		m_nFileBaseIndex = 0;

		m_pFileManagerInfo->m_nProgress = 0;
		m_pFileManagerInfo->m_eFileManagerStatus = efmsCREATESEARCHINDEX;

		Start();
	}
}

/** */
bool CFileManager::CreateHashList()
{
	bool res = false;

	if ( m_pFileManagerInfo->m_eFileManagerStatus == efmsIDLE )
	{
		m_nFileBaseIndex = 0;

		m_pFileManagerInfo->m_nProgress = 0;
		m_pFileManagerInfo->m_eFileManagerStatus = efmsCREATEHASHLIST;

		Start();
		
		res = true;
	}

	return res;
}

/** */
void CFileManager::ThreadCreateSearchIndex()
{
	int k;
	CString filename,folded;
	struct filebaseobject fbo;
	double percent;
	CCaseFolder casefolder;

	for(k=0;k<100;k++)
	{
		filename.Empty();
		m_pSearchIndex->GetFileBaseObjectDuringUpdate(m_nFileBaseIndex,&fbo,filename);
		if ( filename.NotEmpty() )
		{
			if ( casefolder.Fold( filename, folded ) == false )
			{
				// failed, fallback
				folded = filename.ToLower();
			}
			
			m_pSearchIndex->AddSearchIndex(folded);

			m_nFileBaseIndex++;
		}
		else
		{
			DPRINTF("case folded names list created\n");

			// not yet, only do it once after hash list refresh finished
			//m_pSearchIndex->SaveIndex();

			m_pFileManagerInfo->m_nProgress = 100;
			m_pFileManagerInfo->m_eFileManagerStatus = efmsIDLE;

			if ( CDownloadManager::Instance() )
				CDownloadManager::Instance()->SendFileManagerInfo(m_pFileManagerInfo);

			CreateHashList();

			return;
		}
	}

	if ( m_pSearchIndex->IndexCountDuringUpdate() > 0 )
	{
		percent = (m_nFileBaseIndex*100.0)/m_pSearchIndex->IndexCountDuringUpdate();

		if ( m_pFileManagerInfo->m_nProgress != percent )
		{
			m_pFileManagerInfo->m_nProgress = percent;

			if ( CDownloadManager::Instance() )
				CDownloadManager::Instance()->SendFileManagerInfo(m_pFileManagerInfo);
		}
	}
}

/** */
void CFileManager::ThreadCreateShareList()
{
	CString s,s1;
	CDir dir;

	if ( (m_pShareFolder=m_SharedFolders.Next(m_pShareFolder)) != 0 )
	{
		// directory
		s = m_pShareFolder->m_sPath;

		// is valid ?
		if ( dir.cd(s) )
		{
			s  = dir.Path();
			s1 = dir.DirName();

			if ( s1.NotEmpty() )
			{
				s = s.Left( s.Length()-s1.Length() );
			}

			ThreadCreateShareList( 0, CString(), CString(), 100/m_SharedFolders.Count() );
		}
		else
		{
			printf("Can't change dir: '%s'\n",s.Data());
		}
	}
	else
	{
		m_pFileManagerInfo->m_nProgress = 100;
		DPRINTF("finished listing files\n");

		m_pShareList->SetIndexBuffer( m_sShareIndexBuffer );
		
		/* free no longer required large string */
		m_sShareIndexBuffer.Empty();
		
		// m_pShareList->CreateList will be called after hashing finishes
		// don't call it here to avoid serving up filelists without TTHs in
		//m_pShareList->CreateList(m_pSearchIndex);

		if ( m_pFileNameSet )
		{
			m_pFileNameSet->clear();
			delete m_pFileNameSet;
			m_pFileNameSet = 0;
		}

		m_pFileManagerInfo->m_eFileManagerStatus = efmsIDLE;

		if ( CDownloadManager::Instance() )
			CDownloadManager::Instance()->SendFileManagerInfo(m_pFileManagerInfo);

		CreateSearchIndex();

		if ( CDownloadManager::Instance() )
			CDownloadManager::Instance()->SendFileManagerInfo(m_pFileManagerInfo);
	}
}

/** */
void CFileManager::ThreadCreateShareList( int depth, CString curr, CString rel, double percent )
{
	CDir dir;
	CList<CFileInfo> list;
	CString d,s;
	CString path;
	CString aliaspath;
	CFileInfo *fileinfo=0;
	int x,z;
	unsigned long index;

	if ( Stopped() )
		return;

	d  = m_pShareFolder->m_sPath;
	d += DIRSEPARATOR;
	d += curr;
	d += DIRSEPARATOR;
	d += rel;
	path = CDir::SimplePath(d);
	
	d  = m_pShareFolder->m_sAlias;
	d += DIRSEPARATOR;
	d += curr;
	d += DIRSEPARATOR;
	d += rel;
	aliaspath = CDir::SimplePath(d);
	
	if ( CConfig::Instance()->GetDontShareDotFiles() && (rel.NotEmpty() && (rel.Data()[0] == '.')) )
	{
		DPRINTF( "Not sharing folder \"%s\" due to dot files setting.\n", path.Data() );
		return;
	}

	dir.SetPath(path);

	d.Empty();
	z = depth;

//	printf("%s\n",curr.Data());

	// create depth string
	for(x=depth;x>0;x--) d+="\x9";
	z++;
	// use alias
	if ( depth == 0 )
	{
		m_sShareIndexBuffer += m_pShareFolder->m_sAlias;
		m_sShareIndexBuffer += "\xd\xa";
	}
	else
	{
		m_sShareIndexBuffer += d;
		m_sShareIndexBuffer += rel;
		m_sShareIndexBuffer += "\xd\xa";
	}
	d += "\x9";

	// add folder so it can be a search result
	fileinfo = new CFileInfo();
	
	if ( depth == 0 )
	{
		fileinfo->name = m_pShareFolder->m_sAlias;
	}
	else
	{
		fileinfo->name = rel;
	}
	fileinfo->size = 0;
	fileinfo->m_bSymlink = false;
	fileinfo->st_a_time = 0;
	fileinfo->st_m_time = 0;
	fileinfo->st_c_time = 0;
	
	if ( depth == 0 )
	{
		m_pSearchIndex->AddIndex( fileinfo, DIRSEPARATOR, eftFOLDER );
	}
	else
	{
		m_pSearchIndex->AddIndex( fileinfo, CDir::SimplePath(m_pShareFolder->m_sAlias+DIRSEPARATOR+curr), eftFOLDER );
	}
	
	delete fileinfo;
	fileinfo = 0;

	// get all files
	if ( dir.ReadEntrys( CDir::Files, &list ) )
	{
		// add files
		fileinfo = 0;
		while( (fileinfo=list.Next(fileinfo)) != 0 )
		{
			if ( Stopped() )
				return;

			if ( fileinfo->name.NotEmpty() )
			{
				if ( CConfig::Instance()->GetDontShareDotFiles() && (fileinfo->name.Data()[0] == '.') )
				{
					DPRINTF( "Not sharing file \"%s\" due to dot files setting.\n", fileinfo->name.Data() );
					continue;
				}
				
				if ( fileinfo->m_bSymlink )
					s = dir.Canonicalize(path+DIRSEPARATOR+fileinfo->name);
				else
					s = CDir::SimplePath(path+DIRSEPARATOR+fileinfo->name);

				if ( !s.IsEmpty() )
				{
					std::pair<FileNameSet::iterator, bool> ret = m_pFileNameSet->insert(s);
					if ( ret.second )
					{
						if ( dclibVerbose() > 1 )
							printf("CreateShareList: Add file: '%s/%s'\n",path.Data(),fileinfo->name.Data());

						// add file to the file manager ...
						index = m_pSearchIndex->AddIndex( fileinfo, aliaspath, GetFileType(fileinfo->name) );
						
						m_sShareIndexBuffer += d;
						m_sShareIndexBuffer += fileinfo->name;
						m_sShareIndexBuffer += '|';
						m_sShareIndexBuffer += CString::number(index);
						m_sShareIndexBuffer += "\xd\xa";
					}
					else
					{
						DPRINTF("CreateShareList: file '%s/%s' already shared\n",path.Data(),fileinfo->name.Data());
					}
				}
			}
		}
	}

	if ( CDownloadManager::Instance() )
		CDownloadManager::Instance()->SendFileManagerInfo(m_pFileManagerInfo);

	// get all dirs
	if ( dir.ReadEntrys( CDir::Dirs, &list ) )
	{
		if ( list.Count() > 0 )
			percent /= list.Count();
		else
			m_pFileManagerInfo->m_nProgress += percent;

		// add dir
		fileinfo = 0;
		while( (fileinfo=list.Next(fileinfo)) != 0 )
		{
			if ( Stopped() )
				return;

			if ( (fileinfo->name != ".") && (fileinfo->name != "..") )
			{
				if ( fileinfo->m_bSymlink )
					s = dir.Canonicalize(path+DIRSEPARATOR+fileinfo->name);
				else
					s = CDir::SimplePath(path+DIRSEPARATOR+fileinfo->name);

				if ( !s.IsEmpty() )
				{
					std::pair<FileNameSet::iterator, bool> ret = m_pFileNameSet->insert(s);
					if ( ret.second )
					{
						if ( dclibVerbose() > 0 )
							printf("CreateShareList: Add path: '%s/%s'\n",path.Data(),fileinfo->name.Data());

						ThreadCreateShareList(z,curr+DIRSEPARATOR+rel,fileinfo->name,percent);
					}
					else
					{
						m_pFileManagerInfo->m_nProgress += percent;
						DPRINTF("CreateShareList: dir '%s/%s' already shared\n",path.Data(),fileinfo->name.Data());
					}

				}
			}
			else
				m_pFileManagerInfo->m_nProgress += percent;
		}
	}
}

/** */
ulonglong CFileManager::CalcShareSize()
{
	CString s,s1;
	CDir dir;
	ulonglong iSSize;

	iSSize = 0;
	m_pShareFolder=0;

	if ( CConfig::Instance()->GetSharedFolders(&m_SharedFolders) == 0 )
	{
		printf("No share folderfound !");
		return 0;
	}

	while ( (m_pShareFolder=m_SharedFolders.Next(m_pShareFolder)) != 0 )
	{
		// directory
		s = m_pShareFolder->m_sPath;

		// is valid ?
		if ( dir.cd(s) )
		{
			s  = dir.Path();
			s1 = dir.DirName();

			if ( s1.NotEmpty() )
			{
				s = s.Left( s.Length()-s1.Length() );
			}

			iSSize += CalcShareSize( 0, s, s1, CString() );
		}
		else
		{
			printf("Can't change to dir: '%s'\n",s.Data());
		}
	}

	return iSSize;
}


/** */
ulonglong CFileManager::CalcShareSize( int depth, CString base, CString curr, CString relpath )
{
	ulonglong lShareSize;
	CDir dir;
	CList<CFileInfo> list;
	CString d,s;
	CString path;
	CString rpath;
	CFileInfo *fileinfo;
	int x,z;

	lShareSize = 0;

	if ( relpath.IsEmpty() )
	{
		rpath = curr;
	}
	else
	{
		rpath = relpath;
		rpath += DIRSEPARATOR;
		rpath += curr;
	}

	if ( base.IsEmpty() )
	{
		path = curr;
	}
	else
	{
		path = base;
		path += DIRSEPARATOR;
		path += curr;
	}

	dir.SetPath(path);

	d.Empty();
	z = depth;

	// create depth string
	if ( curr.NotEmpty() )
	{
		for(x=depth;x>0;x--) d+="\x9";
		z++;
		d += "\x9";
	}

	// get all files
	if ( dir.ReadEntrys( CDir::Files, &list ) )
	{
		fileinfo = 0;
		while( (fileinfo=list.Next(fileinfo)) != 0 )
		{
			if ( fileinfo->name.NotEmpty() )
			{
				if ( fileinfo->m_bSymlink )
					s = dir.Canonicalize(path+DIRSEPARATOR+fileinfo->name);
				else
					s = CDir::SimplePath(path+DIRSEPARATOR+fileinfo->name);

				if ( s.NotEmpty() )
				{
					lShareSize += fileinfo->size;
				}
			}
		}
	}

	// get all dirs
	if ( dir.ReadEntrys( CDir::Dirs, &list ) )
	{
		fileinfo = 0;
		while( (fileinfo=list.Next(fileinfo)) != 0 )
		{
			if ( (fileinfo->name != ".") && (fileinfo->name != "..") )
			{
				if ( fileinfo->m_bSymlink )
					s = dir.Canonicalize(path+DIRSEPARATOR+fileinfo->name);
				else
					s = CDir::SimplePath(path+DIRSEPARATOR+fileinfo->name);

				if ( s.NotEmpty() )
				{
					lShareSize += CalcShareSize(z,path,fileinfo->name,rpath);
				}
			}
		}
	}
	return lShareSize;
}

/** */
void CFileManager::ThreadCreateHashList()
{
	CString s,pa;
	struct filebaseobject fbo;
	unsigned long hbi;
	int len;
	CFile f;
	CDir cdir;
	double percent;

	if ( !m_pHashMemory )
	{
		m_pHashMemory = new CByteArray();
		m_pHashMemory->SetSize(1024*1024);
	}
	
	if ( m_pSearchIndex->GetFileBaseObjectDuringUpdate(m_nFileBaseIndex,&fbo,s) )
	{
		pa = CConfig::Instance()->AliasToPath(s);
		
		if ( pa.IsEmpty() )
		{
			m_nFileBaseIndex++;
			return;
		}
		
		// check if we have the hash
		if ( m_pSearchIndex->FindHashBaseIndexDuringUpdate(&fbo,&hbi) )
		{
			if ( dclibVerbose() > 1 )
				printf("hash found, no changes in file %s\n", pa.Data());
			fbo.m_nHashIndex = hbi;
			m_pSearchIndex->UpdateIndex(m_nFileBaseIndex,&fbo);
		}
		else
		{
			if ( (CConfig::Instance()->GetDisableHashList() == false) &&
			     (cdir.IsDir( pa, false ) == false) )
			{
				if ( dclibVerbose() > 0 )
					printf("calculate new hash for %s\n", pa.Data());
				
				if ( f.Open( pa, IO_RAW | IO_READONLY ) )
				{
					dcpp::TigerTree hasher( std::max( dcpp::TigerTree::calcBlockSize(cdir.getFileSize(pa,false),10) , ((int64_t) 64*1024) ) );

					while ( (len = f.Read( (char*)m_pHashMemory->Data(), 1024*1024)) > 0 )
					{
						hasher.update(m_pHashMemory->Data(),len);
				
						if ( Stopped() )
							break;
					}
					
					f.Close();
			
					if ( !Stopped() )
					{
						hasher.finalize();
						ByteVector leaves = hasher.getLeafData();
						m_pSearchIndex->AddHashIndex(m_nFileBaseIndex,
							hasher.getRoot().data,
							&leaves[0],leaves.size());
					}
					else
					{
						printf("create hash stopped\n");
						m_pFileManagerInfo->m_eFileManagerStatus = efmsIDLE;
					}
				}
			}
		}
		
		m_nFileBaseIndex++;
	
		if ( m_pSearchIndex->IndexCountDuringUpdate() > 0 )
		{
			percent = (m_nFileBaseIndex*100.0)/m_pSearchIndex->IndexCountDuringUpdate();

			if ( (percent-m_pFileManagerInfo->m_nProgress) > 1.0 )
			{
				m_pFileManagerInfo->m_nProgress = percent;

				if ( CDownloadManager::Instance() )
					CDownloadManager::Instance()->SendFileManagerInfo(m_pFileManagerInfo);
			}
		}
	}
	else
	{
		m_pFileManagerInfo->m_eFileManagerStatus = efmsIDLE;
	}
	
	if ( m_pFileManagerInfo->m_eFileManagerStatus == efmsIDLE )
	{
		DPRINTF("finished hashing files\n");
		m_pFileManagerInfo->m_nProgress = 100;

		if ( CDownloadManager::Instance() )
			CDownloadManager::Instance()->SendFileManagerInfo(m_pFileManagerInfo);

		delete m_pHashMemory;
		m_pHashMemory = 0;

		m_pSearchIndex->FinishUpdate();
		m_pSearchIndex->SaveIndex();
		
		m_pShareList->CreateList(m_pSearchIndex);
		
		DPRINTF("sharelist generated\n");
		
		m_pFileManagerInfo->m_eFileManagerStatus = efmsNONE;
		if ( CDownloadManager::Instance() )
			CDownloadManager::Instance()->SendFileManagerInfo(m_pFileManagerInfo);

		Stop(false);
	}
}

/** */
void CFileManager::Thread()
{
	int wait = 50;

	Lock();

	switch(m_pFileManagerInfo->m_eFileManagerStatus)
	{
		case efmsCREATESHARELIST:
			ThreadCreateShareList();
			break;
		case efmsCREATESEARCHINDEX:
			ThreadCreateSearchIndex();
			wait = 0;
			break;
		case efmsCREATEHASHLIST:
			ThreadCreateHashList();
			wait = 0;
			break;
		case efmsREBUILDLISTS:
			ThreadRebuildLists();
			wait = 0;
			break;
		case efmsVALIDATELEAVES:
			ThreadValidateLeaves();
			wait = 0;
			break;
		default:
			break;
	}

	UnLock();

	NanoSleep(wait);
}

/** */
int CFileManager::FileManagerCallback()
{
	if ( CConfig::Instance()->GetRecreateShareListTime() != 0 )
	{
		if ( m_tCreateShareListTimeout != 0 )
		{
			if ( time(0) > m_tCreateShareListTimeout )
			{
				CreateShareList();

				m_tCreateShareListTimeout = time(0)+CConfig::Instance()->GetRecreateShareListTime()*60*60;
			}
		}
		else
		{
			m_tCreateShareListTimeout = time(0)+CConfig::Instance()->GetRecreateShareListTime()*60*60;
		}
	}
	else
	{
		m_tCreateShareListTimeout = 0;
	}

	return 0;
}

/** */
CString CFileManager::GetFileName( unsigned long fboid )
{
	struct filebaseobject fbo;
	CString filename;

	if ( GetFileBaseObject(fboid,&fbo,filename) )
	{
		return filename;
	}
	else
	{
		return CString();
	}
}

/** */
bool CFileManager::IsCreateShareListRunning()
{
	if (m_pFileManagerInfo->m_eFileManagerStatus == efmsNONE)
	{
		return false;
	}
	else
	{
		return true;
	}
}

/** */
CByteArray * CFileManager::GetHashLeaves( CString tth )
{
	return m_pSearchIndex->GetHashLeaves( tth );
}

/** */
bool CFileManager::RebuildLists()
{
	bool err = false;
	
	//DPRINTF("CFileManager::RebuildLists() called\n");
	
	if ( m_pFileManagerInfo->m_eFileManagerStatus != efmsNONE )
	{
		return err;
	}
	
	Lock();
	
	m_pFileManagerInfo->m_eFileManagerStatus = efmsREBUILDLISTS;
	
	Start();
	
	err = true;
	
	UnLock();
	
	return err;
}

/** */
void CFileManager::ThreadRebuildLists()
{
	//DPRINTF("CFileManager::ThreadRebuildLists() called\n");
	
	// CSearchIndex has direct access to the byte arrays so do it there
	if ( m_pSearchIndex->RebuildLists() == 0 )
	{
		m_pSearchIndex->SaveIndex();
		m_pFileManagerInfo->m_eFileManagerStatus = efmsNONE;
		Stop(false);
		return;
	}
	
	// recreate search index (also redoes hash lists)
	// needed bits copied out of CFileManager::CreateShareList
	// the mutex is already locked
	// the thread is already running
	// we already know m_eFileManagerStatus
	
	m_pShareFolder = 0;
	m_sShareIndexBuffer.Empty();
	
	if ( CConfig::Instance()->GetSharedFolders(&m_SharedFolders) > 0 )
	{
		m_pSearchIndex->PrepareUpdate();
		
		if ( m_pFileNameSet )
		{
			m_pFileNameSet->clear();
			delete m_pFileNameSet;
		}
		m_pFileNameSet = new FileNameSet;
		
		m_pFileManagerInfo->m_nProgress = 0;
		m_pFileManagerInfo->m_eFileManagerStatus = efmsCREATESHARELIST;
		
		if ( CDownloadManager::Instance() )
			CDownloadManager::Instance()->SendFileManagerInfo(m_pFileManagerInfo);	
	}
	else
	{
		m_pShareList->SetIndexBuffer( m_sShareIndexBuffer );
		m_pShareList->CreateList(m_pSearchIndex);

		m_pSearchIndex->SaveIndex();
		
		m_pFileManagerInfo->m_eFileManagerStatus = efmsNONE;
		
		Stop(false);
	}
	//DPRINTF("CFileManager::ThreadRebuildLists() finished\n");
}

/** */
bool CFileManager::ValidateLeaves()
{
	bool ok = false;
	
	if ( m_pFileManagerInfo->m_eFileManagerStatus != efmsNONE )
	{
		return ok;
	}
	
	Lock();
	
	m_pFileManagerInfo->m_eFileManagerStatus = efmsVALIDATELEAVES;
	
	Start();
	
	ok = true;
	
	UnLock();
	
	return ok;
}

/** */
void CFileManager::ThreadValidateLeaves()
{
	if ( m_pSearchIndex->ValidateHashLeaves() == 0 )
	{
		m_pFileManagerInfo->m_eFileManagerStatus = efmsNONE;
		Stop(false);
		return;
	}
	else
	{
		/* start CreateShareList */
		m_pShareFolder = 0;
		m_sShareIndexBuffer.Empty();
		
		if ( CConfig::Instance()->GetSharedFolders(&m_SharedFolders) > 0 )
		{
			m_pSearchIndex->PrepareUpdate();
			
			if ( m_pFileNameSet )
			{
				m_pFileNameSet->clear();
				delete m_pFileNameSet;
			}
			m_pFileNameSet = new FileNameSet;
			
			m_pFileManagerInfo->m_nProgress = 0;
			m_pFileManagerInfo->m_eFileManagerStatus = efmsCREATESHARELIST;
			
			if ( CDownloadManager::Instance() )
				CDownloadManager::Instance()->SendFileManagerInfo(m_pFileManagerInfo);	
		}
		else
		{
			m_pShareList->SetIndexBuffer( m_sShareIndexBuffer );
			m_pShareList->CreateList(m_pSearchIndex);
			
			m_pSearchIndex->SaveIndex();
			
			m_pFileManagerInfo->m_eFileManagerStatus = efmsNONE;
			
			Stop(false);
		}
	}
}
