/***************************************************************************
                          cdownloadqueue.cpp  -  description
                             -------------------
    begin                : Don Mai 16 2002
    copyright            : (C) 2002-2003 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 "cdownloadqueue.h"

#include <stdio.h>

#include "core/cmutex.h"
#include "cconnectionmanager.h"

/** */
CDownloadQueue::CDownloadQueue()
{
	pQueueMutex  = new CMutex();
	pQueue       = new CStringList<CStringList<DCTransferQueueObject> >();
	pChunksMutex = new CMutex();
	pChunkList   = new CStringList<DCFileChunkObject>();
}

/** */
CDownloadQueue::~CDownloadQueue()
{
	delete pQueue;
	pQueue = 0;

	delete pQueueMutex;
	pQueueMutex = 0;

	delete pChunkList;
	pChunkList = 0;
	
	delete pChunksMutex;
	pChunksMutex = 0;
}

/** */
CStringList<DCTransferQueueObject> * CDownloadQueue::GetUserHubList( CString nick )
{
	CStringList<DCTransferQueueObject> * StringList = 0;

	if ( pQueue->Get( nick, &StringList ) == 0 )
	{
		return StringList;
	}

	return 0;
}

/** */
bool CDownloadQueue::RenameNick( CString srcnick, CString dstnick, CString srchubname, CString dsthubname )
{
	CStringList<DCTransferQueueObject> * StringList = 0;
	DCTransferQueueObject * TransferObject = 0;
	bool res = false;

	// get the transferobject
	if ( (TransferObject = GetUserTransferObject( srcnick, srchubname, CString() )) != 0 )
	{
		// made changes
		TransferObject->sNick    = dstnick;
		TransferObject->sHubName = dsthubname;

		// update the lists
		if ( pQueue->Get( srcnick, &StringList ) == 0 )
		{
			StringList->Remove( srchubname );

			if ( StringList->Count() == 0 )
				pQueue->Del(srcnick);

			if ( pQueue->Get( dstnick, &StringList ) != 0 )
			{
				StringList = new CStringList<DCTransferQueueObject>();
				pQueue->Add( dstnick, StringList );
			}

			StringList->Add( dsthubname, TransferObject );

			res = true;
		}
	}

	return res;
}

/** */
DCTransferQueueObject * CDownloadQueue::GetUserTransferObject( CString nick, CString hubname, CString /* hubhost */ )
{
	CStringList<DCTransferQueueObject> * StringList = 0;
	DCTransferQueueObject * TransferObject = 0;

	if ( (StringList = GetUserHubList( nick )) != 0 )
	{
		if ( StringList->Get( hubname, &TransferObject ) == 0 )
		{
			return TransferObject;
		}
		else if ( CConnectionManager::Instance() )
		{
			CString name, host, ip;
			if ( CConnectionManager::Instance()->GetHubDetails( hubname, name, host, ip ) )
			{
				if ( StringList->Get( ip, &TransferObject ) == 0 )
				{
					return TransferObject;
				}
				else if ( StringList->Get( name, &TransferObject ) == 0 )
				{
					return TransferObject;
				}
				else if ( StringList->Get( host, &TransferObject ) == 0 )
				{
					return TransferObject;
				}
			}
		}
	}

	return 0;
}

/** */
DCTransferFileObject * CDownloadQueue::GetUserFileObject( CString nick, CString hubname, CString hubhost, CString remotename )
{
	DCTransferFileObject * TransferFileObject = 0;
	DCTransferQueueObject * TransferObject = 0;

	if ( (TransferObject = GetUserTransferObject( nick, hubname, hubhost )) != 0 )
	{
		if ( TransferObject->pTransferFileList.Get( remotename, &TransferFileObject ) == 0 )
		{
			return TransferFileObject;
		}
	}

	return 0;
}

/** */
bool CDownloadQueue::DelUserFileObject( CString nick, CString hubname, CString hubhost, CString remotefile )
{
	bool res = false,err;
	CStringList<DCTransferQueueObject> * StringList = 0;
	DCTransferQueueObject * TransferObject = 0;
	DCTransferFileObject * TransferFileObject = 0;

	if ( (TransferObject = GetUserTransferObject( nick, hubname, hubhost ) ) != 0 )
	{
		if ( remotefile.NotEmpty() )
		{
			// remove a single file
			if ( TransferObject->pTransferFileList.Get( remotefile, &TransferFileObject ) == 0 )
			{
				if ( TransferFileObject->m_eState == etfsTRANSFER )
				{
					printf("warning ! file transfer is running ...\n");
				}
				else
				{
					RemoveChunk(TransferFileObject->m_sLocalFile);
					TransferObject->pTransferFileList.Del(remotefile);
					res = true;
				}
			}
		}
		else
		{
			// remove a file tree
			TransferFileObject = 0;
			err = false;
			while ( TransferObject->pTransferFileList.Next( &TransferFileObject ) )
			{
				if ( TransferFileObject->m_eState == etfsTRANSFER )
				{
					printf("warning ! file transfer is running ...\n");
					err = true;
					break;
				}
				else
				{
					RemoveChunk(TransferFileObject->m_sLocalFile);
				}
			}

			if ( err == false )
			{
				if ( (StringList = GetUserHubList(nick)) != 0 )
				{
					StringList->Del(hubname);

					if ( StringList->Count() == 0 )
					{
						pQueue->Del(nick);
					}

					res = true;
				}
			}
		}
	}
	else
	{
		printf("warning ! queue not found ...\n");
	}

	return res;
}

/** */
bool CDownloadQueue::RemoveChunk( CString localfile )
{
	bool res = false;
	DCFileChunkObject * FileChunkObject = 0;

	// update file chunks
	pChunksMutex->Lock();

	if ( pChunkList->Get( localfile, &FileChunkObject ) == 0 )
	{
		res = true;

		FileChunkObject->m_nReferenceCount--;
		if ( FileChunkObject->m_nReferenceCount == 0 )
		{
			pChunkList->Del(localfile);
		}
	}

	pChunksMutex->UnLock();

	return res;
}

/** */
DCFileChunkObject * CDownloadQueue::GetFileChunkObject( CString file )
{
	DCFileChunkObject * FileChunkObject = 0;

	pChunkList->Get( file, &FileChunkObject );

	return FileChunkObject;
}

/** */
DCTransferFileObject::DCTransferFileObject()
{
	m_eMedium        = eltNONE;
	m_eState         = etfsNONE;
	m_nSize          = 0;
	m_bMulti         = false;
	m_nPriority      = 0;
	m_pDirList       = 0;
}

/** */
DCTransferFileObject::DCTransferFileObject( const DCTransferFileObject & other )
{
	m_stHash         = other.m_stHash;
	m_sHash          = other.m_sHash;
	m_eMedium        = other.m_eMedium;
	m_sRemoteFile    = other.m_sRemoteFile;
	m_sLocalFile     = other.m_sLocalFile;
	m_sLocalPath     = other.m_sLocalPath;
	m_sLocalFileName = other.m_sLocalFileName;
	m_eState         = other.m_eState;
	m_nSize          = other.m_nSize;
	m_bMulti         = other.m_bMulti;
	m_nPriority      = other.m_nPriority;
	m_sJumpTo        = other.m_sJumpTo;
	
	if ( other.m_pDirList )
	{
		m_pDirList = new std::list<CString>();
		*m_pDirList = *(other.m_pDirList);
	}
	else
	{
		m_pDirList = 0;
	}
}


/** */
DCTransferFileObject::~DCTransferFileObject()
{
	delete m_pDirList;
	m_pDirList = 0;
}

/** */
void DCTransferFileObject::copy( DCTransferFileObject * TransferFileObject )
{
	if( !TransferFileObject )
	{
		return;
	}
	
	m_stHash         = TransferFileObject->m_stHash;
	m_sHash          = TransferFileObject->m_sHash;
	m_eMedium        = TransferFileObject->m_eMedium;
	m_sRemoteFile    = TransferFileObject->m_sRemoteFile;
	m_sLocalFile     = TransferFileObject->m_sLocalFile;
	m_sLocalPath     = TransferFileObject->m_sLocalPath;
	m_sLocalFileName = TransferFileObject->m_sLocalFileName;
	m_eState         = TransferFileObject->m_eState;
	m_nSize          = TransferFileObject->m_nSize;
	m_bMulti         = TransferFileObject->m_bMulti;
	m_nPriority      = TransferFileObject->m_nPriority;
	m_sJumpTo        = TransferFileObject->m_sJumpTo;
	
	if ( TransferFileObject->m_pDirList )
	{
		if ( !m_pDirList )
		{
			m_pDirList = new std::list<CString>();
		}
		*m_pDirList = *(TransferFileObject->m_pDirList);
	}
	else if ( m_pDirList )
	{
		delete m_pDirList;
		m_pDirList = 0;
	}
}

/** */
DCFileChunkObject::DCFileChunkObject()
{
	m_bMulti          = false;
	m_nSize           = 0;
	m_nSizeDone       = 0;
	m_nReferenceCount = 0;
}

/** */
DCFileChunkObject::DCFileChunkObject( const DCFileChunkObject & other )
{
	m_sLocalFile      = other.m_sLocalFile;
	m_stHash          = other.m_stHash;
	m_sHash           = other.m_sHash;
	m_bMulti          = other.m_bMulti;
	m_nSize           = other.m_nSize;
	m_nSizeDone       = other.m_nSizeDone;
	m_nReferenceCount = other.m_nReferenceCount;
	
	DCChunkObject * ChunkObject = 0;
	/* FIXME remove casting away the const once CList is fixed / removed */
	while ( (ChunkObject=((DCFileChunkObject&)other).m_Chunks.Next(ChunkObject)) != 0 )
	{
		m_Chunks.Add( new DCChunkObject(ChunkObject) );
	}
}

/** */
DCFileChunkObject::DCFileChunkObject( const DCFileChunkObject * other )
{
	m_sLocalFile      = other->m_sLocalFile;
	m_stHash          = other->m_stHash;
	m_sHash           = other->m_sHash;
	m_bMulti          = other->m_bMulti;
	m_nSize           = other->m_nSize;
	m_nSizeDone       = other->m_nSizeDone;
	m_nReferenceCount = other->m_nReferenceCount;
	
	DCChunkObject * ChunkObject = 0;
	/* FIXME remove casting away the const once CList is fixed / removed */
	while ( (ChunkObject=((DCFileChunkObject*)other)->m_Chunks.Next(ChunkObject)) != 0 )
	{
		m_Chunks.Add( new DCChunkObject(ChunkObject) );
	}
}

/** */
DCFileChunkObject::~DCFileChunkObject()
{
	// nothing due to CList auto-delete
}

/** */
bool DCTransferWait::Match( const CString & nick, const CString & userhost )
{
	if ( sNick.NotEmpty() )
	{
		if ( sNick == nick )
		{
			if ( sUserHost.NotEmpty() )
			{
				if ( sUserHost == userhost )
				{
					// printf("DCTransferWait::Match: matched nick and userhost\n");
					return true;
				}
			}
			else
			{
				sUserHost = userhost;
				// printf("DCTransferWait::Match: set userhost for %s to %s\n",sNick.Data(),sUserHost.Data());
				return true;
			}
		}
	}
	else if ( sUserHost.NotEmpty() )
	{
		if ( sUserHost == userhost )
		{
			sNick = nick;
			// printf("DCTransferWait::Match: set nick for %s to %s\n",sUserHost.Data(),sNick.Data());
			return true;
		}
	}
	
	return false;
}
