/***************************************************************************
                          cdownloadmanager.h  -  description
                             -------------------
    begin                : Don Mai 16 2002
    copyright            : (C) 2002-2004 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.                                   *
 *                                                                         *
 ***************************************************************************/

#ifndef CDOWNLOADMANAGER_H
#define CDOWNLOADMANAGER_H

/**
  *@author Mathias Küster
  *
  * This handles the queue of files and sources, the list of running
  * transfers, and starting auto searches.
  *
  * The download manager is lacking multi threading, also segment size
  * is fixed, and the number of download slots is not normally limited.
  *
  * Upload bandwith limiting is present, although not present in
  * mainline DC++ at the time of writing this.
  *
  * Calculating the MD5 checksum of the first 10KiB is mostly
  * useless and completely obsolete, but basically very little
  * was ever done to dclib's downloading ability between 0.3.7 and 0.3.22.
  */

#include <dclib/dcos.h>
#include <dclib/core/cmutex.h>
#include <dclib/core/csingleton.h>
#include <dclib/ctransfer.h>

/*
 * eCloseType has moved to here so
 * that cconfig.h does not need to include cdownloadmanager.h
 */
#include <dclib/core/types.h>

#include <dclib/core/cstringlist.h>

#define MAX_FILE_PRIORITY 5

enum eShutdownState {
	essNONE,
	essSHUTDOWN,
	essSHUTDOWNREADY
};

class CUserFileInfo {
public:
	/** */
	CUserFileInfo()
	{
		bMulti = false;
	};
	/** */
	~CUserFileInfo() {};
	/** */
	eTransferWaitState eWaitState;
	/** */
	eTransferFileState eFileState;
	/** */
	CString sLocalFile;
	/** */
	bool bMulti;
};

class CExtraUserSlot {
public:
	/** */
	CExtraUserSlot()
	{
		iSlots = 1;
		bPermanent = false;
	};
	/** */
	~CExtraUserSlot() {};
	/** */
	CString sNick;
	/** */
	CString sHubName;
	/** */
	int iSlots;
	/** */
	bool bPermanent;
};

class DCTransferBanObject {
public:
	/** */
	DCTransferBanObject() {
		m_nRequestCount = 0;
		m_tTime         = 0;
	};
	/** */
	~DCTransferBanObject() {};
	/** */
	CString m_sIP;
	/** */
	unsigned int m_nRequestCount;
	/** */
	time_t m_tTime;
};

class CDownloadManagerInfo : public CDCMessage {
public:
	/** */
	CDownloadManagerInfo() : CDCMessage(DC_MESSAGE_DM_INFO) {
		rate_ul_settings = 0;
		rate_ul_operator = 0;
		rate_ul_user     = 0;
		rate_ul_special  = 0;
		rate_ul_rate_extra = 0;
		rate_dl  = 0;
		slot_max = 0;
		slot_use_settings = 0;
		slot_use_operator = 0;
		slot_use_user     = 0;
		slot_use_special  = 0;
		slot_use_rate_extra = 0;
	};
	/** */
	virtual ~CDownloadManagerInfo() {};
	/** */
	ulonglong Rate() {
		return rate_ul_settings + rate_ul_operator + rate_ul_user + rate_ul_special + rate_ul_rate_extra;
	}
	/** */
	ulonglong rate_ul_settings;
	/** */
	ulonglong rate_ul_operator;
	/** */
	ulonglong rate_ul_user;
	/** */
	ulonglong rate_ul_special;
	/** */
	ulonglong rate_ul_rate_extra;
	/** */
	ulonglong rate_dl;
	/** */
	int slot_max;
	/** the number of normal slots from main config settings used */
	int slot_use_settings;
	/** slot usage for operators (after all other slots used up) */
	int slot_use_operator;
	/** slot usage for users (extra normal slots granted to users, only used if normal slots used up) */
	int slot_use_user;
	/** slot usage for special transfers e.g. minimum filelength (after all normal used up) */
	int slot_use_special;
	/** the number of extra slots that have been given because total upload rate is below a limit */
	int slot_use_rate_extra;

	CDownloadManagerInfo & operator = (const CDownloadManagerInfo & s)
	{
		rate_ul_settings = s.rate_ul_settings;
		rate_ul_operator = s.rate_ul_operator;
		rate_ul_user     = s.rate_ul_user;
		rate_ul_special  = s.rate_ul_special;
		rate_ul_rate_extra = s.rate_ul_rate_extra;
		
		rate_dl  = s.rate_dl;
		slot_max = s.slot_max;
		
		slot_use_settings = s.slot_use_settings;
		slot_use_operator = s.slot_use_operator;
		slot_use_user     = s.slot_use_user;
		slot_use_special  = s.slot_use_special;
		slot_use_rate_extra = s.slot_use_rate_extra;

		return *this;
	};
};

enum eFileManagerStatus {
	efmsNONE=0,
	efmsIDLE,
	efmsCREATESHARELIST,
	efmsCREATESEARCHINDEX,
	efmsCREATEHASHLIST,
	efmsREBUILDLISTS,
	efmsVALIDATELEAVES
};

class CFileManagerInfo : public CDCMessage {
public:
	/** */
	CFileManagerInfo() : CDCMessage(DC_MESSAGE_FM_INFO) {
		m_eFileManagerStatus = efmsNONE;
		m_nProgress = 0;
	};
	/** */
	virtual ~CFileManagerInfo() {};
	/** */
	enum eFileManagerStatus m_eFileManagerStatus;
	/** */
	double m_nProgress;

	CFileManagerInfo & operator = (const CFileManagerInfo & s)
	{
		m_eFileManagerStatus = s.m_eFileManagerStatus;
		m_nProgress          = s.m_nProgress;
		return *this;
	};
};

class CDCMessage;
class CListen;
class CDownloadQueue;
class DCFileChunkObject;
class DCTransferQueueObject;
class DCTransferFileObject;
class DCTransferWait;
class _CCallback0;

class CTransferObject {
public:
	/** */
	CTransferObject() {
		m_pTransfer = 0;
		m_UserDisconnectTimeout = time(0);
	}
	/** */
	~CTransferObject() {
		delete m_pTransfer;
		m_pTransfer = 0;
	}

	/** */
	CTransfer * m_pTransfer;
	/** */
	time_t m_UserDisconnectTimeout;
};

class CDownloadManager : public CSingleton<CDownloadManager> {
public: 
	/** */
	CDownloadManager();
	/** */
	virtual ~CDownloadManager();

	/** load the queue */
	int DLM_LoadQueue();
	/** save the queue */
	int DLM_SaveQueue();
	/** disconnect all transfers for a clean shutdown */
	void DLM_Shutdown();
	/** return the current shutdownstate */
	eShutdownState DLM_ShutdownState() const { return m_eShutdownState; }
	/** add a extra slot to the user */
	void DLM_AddUserSlot( CString nick, CString hubname, int slot, bool permanent = false );
	/** return current used upload slots */
	int DLM_UsedSlots() const { return DownloadManagerInfo.slot_use_settings; }
	/** */
	bool DLM_TransferConnect( CString nick, CString hubname );
	/** */
	bool DLM_TransferClose( ulonglong transferid );
	/** */
	bool DLM_TransferSetRate( ulonglong transferid, ulonglong rate );
	/** */
	bool DLM_TransferGetRate( ulonglong transferid, ulonglong & rate );
	/** */
	CList<CMessageDMTransferObject> * DLM_TransferGetList();

	/** */
	eDirection DLM_TransferDirection( ulonglong transferid );

	/**
	 * For filelists, localpath is abused to set the file/folder to
	 * open the list at (when downloaded from search) and localrootpath
	 * is abused to download a directory.
	 */
	void DLM_QueueAdd( CString nick, CString hubname, CString hubhost,
				CString remotename, CString localname,
				CString localpath, CString localrootpath,
				eltMedium medium, ulonglong size,
				ulonglong startposition,
				ulonglong endposition,
				CString hash,
				bool multi = false );
	/** */
	int DLM_QueueCheck( CString nick, CString hubname, CString hubhost,
				CString remotename, CString localname,
				CString localpath, CString localrootpath,
				eltMedium medium, ulonglong size, CString tth );
	/** change the src-nick/hubname to dst-nick/hubname in the queue */
	bool DLM_QueueEdit( CString srcnick, CString srchubname, CString dstnick, CString dsthubname, CString dsthubhost );
	/** */
	bool DLM_QueuePause( CString nick, CString hubname, CString remotefile, bool pause );
	/** */
	bool DLM_QueueRemove( CString nick, CString hubname, CString remotefile );
	/** */
	bool DLM_QueueRemove( CString localfile );
	/**
	 * 0 = directory removed
	 * 1 = user/hub not found
	 * 2 = no directories queued
	 * 3 = that directory not queued
	 */
	int DLM_QueueRemoveDirectory( CString nick, CString hubname, CString directory );
	/** */
	bool DLM_QueueSetFilePriority( CString nick, CString hubname, CString remotefile, int priority );
	/** */
	bool DLM_QueueGetFileInfo( CString nick, CString hubname, CString hubhost, CString file, CUserFileInfo * UserFileInfo );
	/** */
	DCFileChunkObject * DLM_QueueGetFileChunk( CString file );
	/** */
	bool DLM_QueueUpdateHub( CString nick, CString hubname );
	/** */
	void DLM_QueueGetHub( CString nick, CString hubname, CList<DCHubObject> * list );

	/** */
	bool DLM_AddTransferRequest( CString nick, CString userhost, CString hubname, CString hubhost );
	/** */
	void DLM_AddTransferRequest( CString host, int port, bool crypto, CString hubname, CString hubhost );

	/** */
	bool DLM_GetDownloadManagerInfo( CDownloadManagerInfo * pinfo );
	/** handle search */
	bool DLM_HandleSearch( CMessageSearchResult * MessageSearchResult );

	/** callback function - Called by CManager */
	int Callback();
	/** callback function */
	virtual int DC_DownloadManagerCallBack( CDCMessage * ) { return -1; };

	/**
	 * It's not given to a CCallback it's just directly called by the
	 * listen managers so it can take whatever parameters are necessary.
	 */
	int ListenCallbackHandler( int fd, bool crypto );

protected:
	/** */
	int DM_TransferCallBack( CTransfer * Transfer, CDCMessage * DCMessage );

	/** */
	void SendFileManagerInfo( CFileManagerInfo * info );

private:
	/** */
	friend class CFileManager;

	/** */
	void SendFileInfo( DCTransferQueueObject * TransferObject, DCTransferFileObject * TransferFileItem = 0, bool bRemoveFile = false );
	/** */
	void SendTransferInfo( CTransfer * Transfer, bool remove = false );
	/** */
	void SendSlotInfo( CExtraUserSlot * Object );
	/** */
	void SendLogInfo( CString message, CTransfer * Transfer = 0 );
	/** */
	void SendDownloadManagerInfo( CDownloadManagerInfo * dminfo );
	/** */
	void SendTrafficInfo();

	/** create a messagedmtransferobject from a transfer */
	CMessageDMTransferObject * CreateDMTransferObject( CTransfer * Transfer );
	/** */
	void FileListDone( CTransfer * Transfer, DCTransferFileObject * TransferFileObject );

	/** */
	bool CheckUserSlot( CString nick, CString hubname );
	/** */
	void OptimizeChunks( DCFileChunkObject * FileChunkObject );
	/** */
	bool GetNextChunk( CString file, ulonglong * lstart, ulonglong * lend );
	/** */
	int UpdateChunk( CString file, ulonglong lstart, ulonglong lend, ulonglong lcurrent );
	/** */
	bool GetNewChunkEnd( CString file, ulonglong lstart, ulonglong lend, ulonglong lcurrent, ulonglong * lnstart, ulonglong *lnend );

	/** */
	void UpdateQueueList( time_t ttimeout );
	/** */
	void UpdateTransferList( time_t ttimeout );
	/** */
	void UpdateBanList( time_t ttimeout );

	/** */
	eDirection CheckWaitTransfer( CTransfer * Transfer );
	/** */
	bool UpdateWaitTransfer( CTransfer * Transfer, bool rem = false );
	/** */
	bool ChangeDirection( CTransfer * Transfer );
	/** */
	bool SetNextFile( CTransfer * Transfer );
	/** */
	bool SetDirection( CTransfer * Transfer );
	/** */
	bool SetFile( CTransfer * Transfer );
	/** */
	void UpdateFileState( CTransfer * Transfer, eTransferFileState eState );
	/** */
	bool CheckHash( CTransfer * Transfer );

	/** */
	bool RemoveQueueFile( CString nick, CString hubname, CString remotefile );
	/** */
	bool RemoveQueueFile( CString localfile );

	/** */
	bool InitSearch( time_t ttimeout );
	
	/** */
	ulonglong GetNewID();

	/** */
	eShutdownState m_eShutdownState;

	/** mutex for extra slots list */
	CMutex * m_pExtraSlotsMutex;
	/** */
	CList<CExtraUserSlot> * m_pExtraUserSlotList;

	/** */
	_CCallback0 * m_pCallback;

	/** */
	CDownloadManagerInfo DownloadManagerInfo;
	/** */
	CMutex TransferCallBackMutex;
	/** */
	CMutex LogMutex;
	/** */
	ulonglong m_nID;

	/** */
	time_t m_tDownloadQueueTimeout;
	/** */
	time_t m_tUpdateTransferTimeout;
	/** timeout for a new hubsearch */
	time_t m_tHubSearchTimeout;
	/** timeout for a single search */
	time_t m_tSearchTimeout;
	/**
	 * The last time an additional upload slot was given because
	 * total upload rate was below a limit. This is needed so that
	 * these slots are only given max 1 per minute so that there
	 * is enough time for the new transfer to be included in
	 * the total upload rate.
	 */
	time_t m_tLastRateExtra;
	/**
	 * The time old entries were removed from m_pTransferWaitList.
	 */
	time_t m_tWaitListCleaned;

	/** */
	CDownloadQueue * m_pDownloadQueue;
	/** mutex for transfers list */
	CMutex * m_pTransfersMutex;
	/** */
	CStringList<CTransferObject> * m_pTransferList;
	/** mutex for transfer wait list */
	CMutex * m_pWaitListMutex;
	/** */
	CList<DCTransferWait> * m_pTransferWaitList;
	/** mutex for transfer ban list */
	CMutex * m_pBanListMutex;
	/** */
	CStringList<DCTransferBanObject> * m_pTransferBanList;
	/**
	 * The search result list was never locked/unlocked.
	 */
	CList<CMessageSearchResult> * m_pSearchList;
	/** */
	CList<CDCMessage> * m_pSearchQueryList;
};

#endif
