/***************************************************************************
                          ctransfer.h  -  description
                             -------------------
    begin                : Fri Feb 22 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 CTRANSFER_H
#define CTRANSFER_H

/**
  *@author Mathias Küster
  *
  * This class handles a file transfer, reading or writing to the file,
  * possibly doing zlib compression or decompression.
  *
  * Perhaps the worst thing missing from this class, and dclib overall, is
  * that there is no verification of the data received using the TTH leaf data.
  *
  * The TTH leaf data should basically be a list of TTHs, for sections of the
  * file, you can check the leaf data matches the root (by hashing it), and
  * then hash the data as it is received.
  *
  * This class is an awful mess mainly for all the obsolete protocol command
  * support, but also due to compressed transfers (most things are not even
  * compressible anyway).
  */

#include <dclib/dcos.h>
#include <dclib/core/cstring.h>
#include <dclib/core/ccallback.h>
#include <dclib/core/cfile.h>
#include <dclib/core/czlib.h>
#include <dclib/cdcproto.h>
#include <dclib/dcobject.h>

#ifndef WIN32
#include <sys/time.h>
#endif

class CByteArray;

enum eTransferState {
	etsNONE,
	etsREADY,
	etsIDLE
};

/*
 * Warning: these are not in order the types of slot are allocated.
 * Changes for 0.3.22:
 * - total upload rate dependant extra normal slots added (O: in DC++ tag)
 * - if a user is granted a slot, the user may still get a normal slot,
 *   and this still uses up the granted slot
 * - operator slots were fixed, and now may actually be allocated after special slots
 *
 * Previously operator slots were broken (never given), which is lucky
 * because they would have prevented operators getting small files when
 * normal users could. Also slots granted to users caused that
 * user to not use a normal slot. I think the idea of slots granted to
 * users is to allow friends to get files when normal slots are used, not to
 * allow normal users to get files while your friends are busy downloading.
 *
 * SETTINGS, RATE_EXTRA and USER are all normal slots allowing all uploads,
 * but SPECIAL is limited and OPERATOR more limited. So 3 types of slot
 * but 5 ways for some kind of slot to be given (3,1,1 for normal,mini,op).
 */
enum eTransferType {
	ettNONE,	/* unknown */
	ettSETTINGS,	/* normal slot from settings slot limit */
	ettOPERATOR,	/* 1 of 4 slots for operators to get the filelist */
	ettUSER,	/* normal slot granted to a user */
	ettSPECIAL,	/* 1 of 4 slots for small files,filelist,leaves */
	ettRATE_EXTRA	/* normal slot allocated because total upload rate was below minimum */
};

typedef struct sAverageTransferRate {
	struct timeval tv[10];
	ulonglong transfered[10];
	int index;
} sAverageTransferRate;

class CTransfer : public CDCProto {
public:
	/** */
	CTransfer( bool listener = false );
	/** */
	virtual ~CTransfer();

	/** callback function */
	virtual int DC_CallBack( CDCMessage * ) { return -1; };
	/** */
	virtual void DataAvailable( const char * buffer, int len );
	/** */
	virtual void DataSend();
	/** */
	virtual void DataTimeout();
	/** */
	virtual void ConnectionState( eConnectionState state );
	/** */
	virtual void Notify();

	/** */
	void SetCallBackFunction( _CCallback2<CTransfer, CDCMessage*> * callback );

	/** */
	int StartDownload( CString dstfile, ulonglong startposition, ulonglong endposition, ulonglong size, ulonglong chunksize, CString srcfile, CString adcTTH = CString() );
	/** */
	int StartUpload( CString dstfile, ulonglong length, ulonglong pos, ulonglong chunksize, CString srcfile, bool uget = false, bool adcget = false, CString adcTTH = CString(), bool compress = false );

	/** */
	int CallBack_SendObject( CDCMessage * DCMessage );
	/** */
	int CallBack_SendError( CString msg );

	/** */
	ulonglong GetTransferrate();
	/** */
	ulonglong GetBytesForTransferrate( ulonglong rate );
	/** */
	void AddTraffic( long n );
	/** */
	ulonglong GetTraffic();

	/** */
	int HandleMessage( char * c, int len );

	/** */
	void CloseFile() { m_File.Close(); };
	/** */
	ulonglong GetTransferID() const;
	/** */
	void SetTransferID( ulonglong transferid );
	/** */
	CString GetDstFilename() const;
	/** */
	void SetDstFilename( CString filename );
	/** */
	CString GetSrcFilename() const;
	/** */
	void SetSrcFilename( CString filename );
	/** */
	CString GetDstNick() const;
	/** */
	CString GetHubHost() const;
	/** */
	void SetHubHost( CString hubhost );
	/** */
	CString GetHubName() const;
	/** */
	void SetHubName( CString hubname );
	/** */
	eltMedium GetMedium() const;
	/** */
	void SetMedium( eltMedium medium );
	/** */
	long GetBuffer( CByteArray *ba );
	/** */
	bool SaveBufferToFile( const CString & filename );
	/** */
	void SetBuffer( CByteArray *ba );
	/**
	 * This is to avoid creating an intermediate CByteArray for ADCGET list transfers.
	 */
	void ClearAndAppendBuffer( const unsigned char * data, const unsigned long length );

	/** */
	eDirection GetSrcDirection() const;
	/** */
	void SetSrcDirection( eDirection direction );
	/** */
	eDirection GetDstDirection() const;
	/** */
	void SetDstDirection( eDirection direction );

	/** */
	int GetSrcLevel() const;
	/** */
	int GetDstLevel() const;
	/** */
	eConnectionState GetMode() const;
	/** */
	void SetMode( eConnectionState mode );
	/** */
	ulonglong GetStartPosition() const;
	/** */
	void SetStartPosition( ulonglong pos );
	/** */
	ulonglong GetEndPosition() const;
	/** */
	void SetEndPosition( ulonglong pos );
	/** */
	ulonglong GetLength() const;
	/** */
	void SetLength( ulonglong length );
	/** */
	ulonglong GetTransfered() const;
	/** */
	void SetTransfered( ulonglong n ) { m_nTransfered = n; };
	/** */
	ulonglong GetChunkSize() const { return m_nChunkSize; };
	/** */
	void SetChunkSize( ulonglong n ) { m_nChunkSize = n; };
	/** */
	void InitTime();
	/** */
	void SetNick( CString nick );
	/** */
	CString GetNick();
	/** */
	void SetRate( ulonglong rate );
	/** */
	ulonglong GetRate() const;
	/** */
	bool IsIdle() const;

	/** */
	void SetDone( eTransferState e );
	/** */
	eTransferState GetDone() const;
	/** */
	time_t GetStartTime() const;
	/** */
	void SetStartTime( time_t t );

	/** */
	eTransferType GetTransferType() const;
	/** */
	void SetTransferType( eTransferType type );

	/** */
	CMessageSupports GetSupport() { return m_MessageSupports; }
	/** */
	bool GetEncrypted();
	/** */
	bool SupportsChunks() const;
	
	/** */
	CString GetTTH() const;
	/** */
	void SetTTH( const CString & newtth );
	
	/** */
	bool IsListener() const { return m_bListener; };

private:
	/** */
	bool DoInitDownload();
	/** */
	void DoInitUpload();
	/** */
	int HandleFileTransfer( const char * buffer, int len );
	/** */
	int HandleBufferTransfer( const char * buffer, int len );
	/** */
	int HandleControlTransfer( const char * buffer, int len );
	/** Send SSL messages to GUI */
	void SendSSLInfo();

	/** */
	int m_nDataType;
	/** */
	ulonglong m_nTransferID;
	/** */
	CString sNick;
	/** */
	CString sBuffer;
	/** remote filesize */
	ulonglong m_nFileSize;
	/** start transfer from this fileposition */
	ulonglong m_nStartPosition;
	/** */
	ulonglong m_nEndPosition;
	/** bytes transfered */
	ulonglong m_nTransfered;
	/** wanted bytes */
	ulonglong m_nChunkSize;	
	/** */
	bool m_bListener;
	/** */
	long m_nFileBufferPos;
	/** */
	long m_nFileBufferSize;
	/** */
	ulonglong m_nTransferRate;
	/** */
	eConnectionState eMode;
	/** */
	eltMedium eMedium;
	/** */
	bool m_bIdle;
	/** */
	CFile m_File;
	/** */
	CByteArray * pByteArray;
	/** local filename */
	CString sSrcFilename;
	/** remote filename */
	CString sDstFilename;
	/** local direction */
	eDirection eSrcDirection;
	/** remote direction */
	eDirection eDstDirection;
	/** remote nick */
	CString sDstNick;
	/** local level */
	int m_nSrcLevel;
	/** local level */
	int m_nDstLevel;
	/** identifier hubname */
	CString sHubName;
	/** identifier hubhost:hubport */
	CString sHubHost;
	/** */
	struct timeval starttime;
	/** */
	ulonglong m_nPendingSendBytes;
	/** */
	CMutex TransferMutex;
	/** callback function */
	_CCallback2<CTransfer, CDCMessage*> * pCallback;
	/** */
	eTransferState m_eTransferState;
	/** */
	eTransferType m_eTransferType;
	/** */
	CMessageSupports m_MessageSupports;
	/** Disable XML filelists for DC++ >= 0.307 and <= 0.403 */
	bool m_bDisableXML;
	/** */
	struct sAverageTransferRate m_AverageTransferRate;
	/** */
	CZLib m_ZLib;
	/** used to compress uploads */
	CDeflater m_Deflater;
	/** size of read buffer */
	long m_nReadFileBufferSize;
	/** last return value from czlib/cdeflater */
	int m_nZlibStatus;
	/** file TTH */
	CString m_sTTH;
};

/** */
inline CString CTransfer::GetDstFilename() const
{ return sDstFilename; }
/** */
inline void CTransfer::SetDstFilename( CString filename )
{ TransferMutex.Lock(); sDstFilename=filename; TransferMutex.UnLock(); }
/** */
inline CString CTransfer::GetSrcFilename() const
{ return sSrcFilename; }
/** */
inline void CTransfer::SetSrcFilename( CString filename )
{ TransferMutex.Lock(); sSrcFilename=filename; TransferMutex.UnLock(); }
/** */
inline CString CTransfer::GetDstNick() const
{ return sDstNick; }
/** */
inline CString CTransfer::GetHubHost() const
{ return sHubHost; }
/** */
inline void CTransfer::SetHubHost( CString hubhost )
{ TransferMutex.Lock(); sHubHost=hubhost; TransferMutex.UnLock(); }
/** */
inline CString CTransfer::GetHubName() const
{ return sHubName; }
/** */
inline void CTransfer::SetHubName( CString hubname )
{ TransferMutex.Lock(); sHubName=hubname; TransferMutex.UnLock(); }
/** */
inline void CTransfer::SetMedium( eltMedium medium )
{ TransferMutex.Lock(); eMedium=medium; TransferMutex.UnLock(); }
/** */
inline eltMedium CTransfer::GetMedium() const
{ return eMedium; }
/** */
inline eDirection CTransfer::GetSrcDirection() const
{ return eSrcDirection; }
/** */
inline void CTransfer::SetSrcDirection( eDirection direction )
{ TransferMutex.Lock(); eSrcDirection = direction; TransferMutex.UnLock(); }
/** */
inline eDirection CTransfer::GetDstDirection() const
{ return eDstDirection; }
/** */
inline void CTransfer::SetDstDirection( eDirection direction )
{ TransferMutex.Lock(); eDstDirection = direction; TransferMutex.UnLock(); }
/** */
inline int CTransfer::GetSrcLevel() const
{ return m_nSrcLevel; }
/** */
inline int CTransfer::GetDstLevel() const
{ return m_nDstLevel; }
/** */
inline eConnectionState CTransfer::GetMode() const
{ return eMode; }
/** */
inline void CTransfer::SetMode( eConnectionState mode )
{ TransferMutex.Lock(); eMode = mode; TransferMutex.UnLock(); }
/** */
inline ulonglong CTransfer::GetStartPosition() const
{ return m_nStartPosition; }
/** */
inline void CTransfer::SetStartPosition( ulonglong pos )
{ TransferMutex.Lock(); m_nStartPosition=pos; TransferMutex.UnLock(); }
/** */
inline ulonglong CTransfer::GetEndPosition() const
{ return m_nEndPosition; }
/** */
inline void CTransfer::SetEndPosition( ulonglong pos )
{ TransferMutex.Lock(); m_nEndPosition=pos; TransferMutex.UnLock(); }
/** */
inline ulonglong CTransfer::GetLength() const
{ return m_nFileSize; }
/** */
inline void CTransfer::SetLength( ulonglong length )
{ TransferMutex.Lock(); m_nFileSize = length; TransferMutex.UnLock(); }
/** */
inline ulonglong CTransfer::GetTransfered() const
{ return m_nTransfered; }
/** */
inline void CTransfer::SetNick(CString nick)
{ TransferMutex.Lock(); sNick=nick; TransferMutex.UnLock(); }
/** */
inline CString CTransfer::GetNick()
{ return sNick; }
/** */
inline void CTransfer::SetRate( ulonglong rate )
{ TransferMutex.Lock(); m_nTransferRate=rate; TransferMutex.UnLock(); }
/** */
inline ulonglong CTransfer::GetRate() const
{ return m_nTransferRate; }
/** */
inline bool CTransfer::IsIdle() const
{ return m_bIdle; }
/** */
inline void CTransfer::SetCallBackFunction( _CCallback2<CTransfer, CDCMessage*> * callback )
{ TransferMutex.Lock(); delete pCallback; pCallback = callback; TransferMutex.UnLock(); }
/** */
inline void CTransfer::SetDone( eTransferState e )
{ TransferMutex.Lock(); m_eTransferState = e; TransferMutex.UnLock(); }
/** */
inline eTransferState CTransfer::GetDone() const
{ return m_eTransferState; }
/** */
inline time_t CTransfer::GetStartTime() const
{ return starttime.tv_sec; }
/** */
inline void CTransfer::SetStartTime( time_t t )
{ TransferMutex.Lock(); starttime.tv_sec = t; TransferMutex.UnLock(); }
/** */
inline ulonglong CTransfer::GetTransferID() const
{ return m_nTransferID; }
/** */
inline void CTransfer::SetTransferID( ulonglong transferid )
{ TransferMutex.Lock(); m_nTransferID = transferid; TransferMutex.UnLock(); }
/** */
inline eTransferType CTransfer::GetTransferType() const
{ return m_eTransferType; }
/** */
inline void CTransfer::SetTransferType( eTransferType type )
{ TransferMutex.Lock(); m_eTransferType = type; TransferMutex.UnLock(); }
/** */
inline CString CTransfer::GetTTH() const
{ return m_sTTH; }
/** */
inline void CTransfer::SetTTH( const CString & newtth )
{ TransferMutex.Lock(); m_sTTH = newtth; TransferMutex.UnLock(); }

#endif
