//this file is part of eMule
//Copyright (C)2002 Merkur ( merkur-@users.sourceforge.net / http://www.emule-project.net )
//
//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.
//
//This program is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU General Public License for more details.
//
//You should have received a copy of the GNU General Public License
//along with this program; if not, write to the Free Software
//Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

#pragma once
#include "types.h"
#include "listensocket.h"
#include "partfile.h"
#include "clientcredits.h"
#include "safefile.h"
#include "BarShader.h"
#include "StoredSources.h" // Added by Tarod (LS tweak)

// uploadstate
#define US_NONE				0
#define	US_PENDING			1
#define	US_ONUPLOADQUEUE	2
#define	US_UPLOADING		4
#define US_BANNED			6
#define US_ERROR			7
#define	US_WAITCALLBACK		8
#define	US_CONNECTING		9
#define	US_LOWTOLOWIP		10

// downloadstate
#define	DS_NONE				0
#define	DS_DOWNLOADING		1
#define	DS_ONQUEUE			2
#define	DS_CONNECTING		3
#define	DS_WAITCALLBACK		4
#define	DS_NONEEDEDPARTS	5
#define DS_BANNED			6
#define DS_ERROR			7
#define	DS_CONNECTED		9
#define	DS_LOWTOLOWIP		10
#define	DS_REQHASHSET		11
#define	DS_TOOMANYCONNS		12

// m_byChatstate
#define	MS_NONE				0
#define	MS_CHATTING			1
#define	MS_CONNECTING		2
#define	MS_UNABLETOCONNECT	3

// clientsoft
#define SO_UNKNOWN			0
#define SO_EMULE			1
#define	SO_EDONKEY			2
#define SO_MLDONKEY			3
#define SO_OLDEMULE			4

class CClientReqSocket;
class CFriend;

class CUpDownClient
{
	friend class CUploadQueue;
public:
	//base
	CUpDownClient(CClientReqSocket* sender = 0);
	CUpDownClient(uint16 in_port, uint32 in_userid, uint32 in_serverup, uint16 in_serverport,CPartFile* in_reqfile, CSourceEntry* source_entry = (CSourceEntry*)NULL);
	~CUpDownClient();
	void			Disconnected();
	void			TryToConnect(bool bIgnoreMaxCon = false);
	void			ConnectionEstablished();
	uint32			GetUserID()					{return m_nUserID;}
	char*			GetUserName()				{return m_pszUsername;}
	uint32			GetIP()						{return m_dwUserIP;}
	bool			HasLowID()					{return (m_nUserID < 16777216);}
	char*			GetFullIP()					{return m_szFullUserIP;}
	uint32			GetUserPort()				{return m_nUserPort;}
	uint32			GetTransferedUp()			{return m_nTransferedUp;}
	uint32			GetSessionUp() const		{return m_nSessionUp;} // Added by Tarod -VQB Add UL Session Control
	uint32			GetTransferedDown()			{return m_nTransferedDown;}
	uint32			GetServerIP()				{return m_dwServerIP;}
	uint16			GetServerPort()				{return m_nServerPort;}
	uchar*			GetUserHash()				{return (uchar*)m_achUserHash;}
	bool			HasValidHash()				{return ((int*)m_achUserHash)[0] != 0 || ((int*)m_achUserHash[1]) != 0 ||
												        ((int*)m_achUserHash)[2] != 0 || ((int*)m_achUserHash[3]) != 0; }
	uint8			GetVersion()				{return m_nClientVersion;}
	const CSourceEntry*	const GetSourceEntry() const	{return m_sourceEntry;} // Added by Tarod to remove dupes
	uint8			GetMuleVersion()			{return m_byEmuleVersion;}
	bool			ExtProtocolAvailable()		{return m_bEmuleProtocol;}
	bool			IsTarodVersion()				{return (m_byDataTarodVer == 1 || m_byDataTarodVer == 2);} // Added by Tarod
	uint32			GetTarodVersionNumber() const { return m_TarodVersionNumber; }
	bool			IsEmuleClient()				{return m_byEmuleVersion;}
	CClientCredits* Credits()					{return credits;}
	bool			IsBanned()					{return (m_bBanned && m_nDownloadState != DS_DOWNLOADING);}
	char*			GetClientFilename()			{return m_pszClientFilename;}
	bool			SupportsUDP()				{return m_byUDPVer != 0 && m_nUDPPort != 0;}
	uint16			GetUDPPort()				{return m_nUDPPort;}
	void			SetUDPPort(uint16 nPort)	{ m_nUDPPort = nPort; }
	uint8			GetUDPVersion()				{return m_byUDPVer;}
	uint8			GetExtendedRequestsVersion(){return m_byExtendedRequestsVer;}
	bool			IsFriend()					{return m_Friend != NULL;}

	GetSessionUp()								{return m_nSessionUp;} // VQB Add UL Session Control (Rectencle)
	void			ClearSessionUp()			{m_nSessionUp = 0;} // VQB Clear UL Session Control (Rectencle)
	bool			PrefetchedAlready()			{return m_nPrefetchedAlready;} // Rectencle Connection-Prefetching
	void			ClearPrefetchedAlready()	{m_nPrefetchedAlready = false;} // Rectencle Connection-Prefetching
	void			SetPrefetchedAlready()		{m_nPrefetchedAlready = true;} // Rectencle Connection-Prefetching
	uint32			GetFileSize()				{return m_pszFileSize;} //Rectencle Connection-Prefetching
	uint32			GetUploadTime()				{return m_dwUploadTime;} //Rectencle Connection-Prefetching

	void			RequestSharedFileList();
	void			ProcessSharedFileList(char* pachPacket, uint32 nSize);
	
	void			SetUserName(char* pszNewName);
	uint8			GetClientSoft();
	void			ProcessHelloAnswer(char* pachPacket, uint32 nSize);
	void			ProcessHelloPacket(char* pachPacket, uint32 nSize);
	void			SendHelloAnswer();
	void			SendHelloPacket();
	void			SendMuleInfoPacket(bool bAnswer);
	void			ProcessMuleInfoPacket(char* pachPacket, uint32 nSize);
	void			ProcessMuleCommentPacket(char* pachPacket, uint32 nSize);
	bool			Compare(CUpDownClient* tocomp);
	void			SetLastSrcReqTime()			{m_dwLastSourceRequest = ::GetTickCount();}
	void			SetLastSrcAnswerTime()		{m_dwLastSourceAnswer = ::GetTickCount();}
	uint32			GetLastSrcReqTime()			{return m_dwLastSourceRequest;}
	uint32			GetLastSrcAnswerTime()		{return m_dwLastSourceAnswer;}
	bool			GetFriendSlot()				{return m_bFriendSlot;}
	void			SetFriendSlot(bool bNV)		{m_bFriendSlot = bNV;}
	void			SetCommentDirty(bool bDirty = true) {m_bCommentDirty = bDirty;}
	uint8			GetSourceExchangeVersion()	{return m_bySourceExchangeVer;}

	CClientReqSocket*	socket;
	CClientCredits*		credits;
	CFriend*			m_Friend;
	//upload
	float			GetCompression()	{return (float)compressiongain/notcompressed*100.0f;} // Add rod show compression
	void			ResetCompressionGain() {compressiongain = 0; notcompressed=1;} // Add show compression

	uint8			GetUploadState()			{return m_byUploadState;}
	void			SetUploadState(uint8 news)	{m_byUploadState = news;}
	uint32			GetWaitStartTime()			{return m_dwWaitTime;}
	uint32			GetWaitTime()				{return m_dwUploadTime-m_dwWaitTime;}
	bool			IsDownloading()				{return (m_byUploadState == US_UPLOADING);}
	bool			HasBlocks()					{return !(m_BlockSend_queue.IsEmpty() && m_BlockRequests_queue.IsEmpty());}
	uint32			GetDatarate()				{return m_nUpDatarate;}	
	uint32			GetScore(bool sysvalue, bool isdownloading = false, bool onlybasevalue = false);
	float			GetRareFilePushRatio(void) ; // Added by Tarod
	float			GetSmallFilePushRatio(void) ; // Added by Tarod
	void			AddReqBlock(Requested_Block_Struct* reqblock);
	bool			CreateNextBlockPackage();
	void			SetUpStartTime(uint32 dwTime = 0);
	uint32			GetUpStartTimeDelay()		{return ::GetTickCount() - m_dwUploadTime;}
	void			SetWaitStartTime(uint32 dwTime = 0);
	void			SendHashsetPacket(char* forfileid);
	uchar			reqfileid[16];
	uint32			SendBlockData(uint32 maxammount);
	void			ClearUploadBlockRequests();
	void			SendRankingInfo();
	void			SendCommentInfo(CKnownFile *file);
	uint32			GetLastAskedDelay();
	void			AddRequestCount(uchar* fileid);
	bool 			IsDifferentPartBlock();
	void			UnBan();
	void			Ban();
	uint32			GetBanTime()				{return m_dwBanTime;}
	uint32			GetAskedCount()				{return m_cAsked;}
	void			AddAskedCount()				{m_cAsked++;}
	void			SetAskedCount( uint32 m_cInAsked)				{m_cAsked = m_cInAsked;}
	void			FlushSendBlocks();			// call this when you stop upload, or the socket might be not able to send
	void			SetLastUpRequest()			{m_dwLastUpRequest = ::GetTickCount();}
	uint32			GetLastUpRequest()			{return m_dwLastUpRequest;}
	void			UDPFileReasked();
	//void			ClearSessionUp()			{m_nSessionUp = 0;} // VQB

	//download
	uint32			GetAskedCountDown()				{return m_cDownAsked;} //<<--
	void			AddAskedCountDown()				{m_cDownAsked++;}
	void			SetAskedCountDown( uint32 m_cInDownAsked)				{m_cDownAsked = m_cInDownAsked;}
	uint8			GetDownloadState()			{return m_nDownloadState;}
	void			SetDownloadState(uint8 byNewState);
	uint32			GetLastAskedTime()			{return m_dwLastAskedTime;}
	bool			IsPartAvailable(uint16 iPart)	{return	( (iPart >= m_nPartCount) || (!m_abyPartStatus) )? 0:m_abyPartStatus[iPart];} 	
	uint8*			GetPartStatus()				{return m_abyPartStatus;}
	uint32			GetDownloadDatarate()		{return m_nDownDatarate;}
	uint16			GetRemoteQueueRank()		{return m_nRemoteQueueRank;}
	void			SetRemoteQueueFull( bool flag )	{m_bRemoteQueueFull = flag;}
	bool			IsRemoteQueueFull()			{return m_bRemoteQueueFull;}
	void			SetRemoteQueueRank(uint16 nr);
	void			DrawStatusBar(CDC* dc, RECT* rect, bool onlygreyrect, bool  bFlat);
	void			AskForDownload();
	void			SendFileRequest();
	void			ProcessFileInfo(char* packet,uint32 size);
	void			ProcessFileStatus(char* packet,uint32 size);
	void			ProcessHashSet(char* packet,uint32 size);
	bool			AddRequestForAnotherFile(CPartFile* file);
	void			SendBlockRequests();
	void			ProcessBlockPacket(char* packet, uint32 size, bool packed = false);
	uint32			CalculateDownloadRate();
	uint16			GetAvailablePartCount();
	bool			SwapToAnotherFile(bool bIgnoreNoNeeded = false, bool bManualChange = false);
	void			HandoverToAnotherFile(CPartFile* file); // Added by Tarod [Ambdribant]
	void			UDPReaskACK(uint16 nNewQR);
	void			UDPReaskFNF();
	void			UDPReaskForDownload();
	bool			IsSourceRequestAllowed();
	
	CPartFile*		reqfile;

	//returns the count of avaible parts that this user has but we not have
	uint16	GetAvailableOfNeededPartsCount(); //added by Mr.Fry

	//chat
	uint8			GetChatState()			{return m_byChatstate;}
	void			SetChatState(uint8 nNewS){m_byChatstate = nNewS;}
   //File Comment 
   CString         GetFileComment()         {return m_strComment;} 
   void		   SetFileComment(char *desc) {m_strComment.Format("%s",desc);}
   uint8       GetFileRate()            {return m_iRate;}
   void		   SetFileRate(int8 iNewRate) {m_iRate=iNewRate;}

	// Barry - Process zip file as it arrives, don't need to wait until end of block
	int unzip(Pending_Block_Struct *block, BYTE *zipped, uint32 lenZipped, BYTE **unzipped, uint32 *lenUnzipped, bool recursive = false);
	// Barry - Sets string to show parts downloading, eg NNNYNNNNYYNYN
	void ShowDownloadingParts(CString *partsYN);

private:
	// base
	void	Init();
	void	ProcessHelloTypePacket(CSafeMemFile* data);
	void	SendHelloTypePacket(CMemFile* data);
	bool	m_bIsBotuser;
//	bool	isfriend;
	uint32	m_dwUserIP;
	uint32	m_dwServerIP;
	uint32	m_nUserID;
	int16	m_nUserPort;
	int16	m_nServerPort;
	uint32	m_nClientVersion;
	uint32	m_nUpDatarate;
	uint32	dataratems;
	uint32	m_cSendblock;
	uint8	m_byEmuleVersion;
	uint8	m_byDataCompVer;
	uint8	m_byDataTarodVer; // Added by Tarod (just curiosity)
	uint32	m_TarodVersionNumber; // Added by Tarod
	bool	m_bEmuleProtocol;
	char*	m_pszUsername;
	char	m_szFullUserIP[21];
	char	m_achUserHash[16];
	uint16	m_nUDPPort;
	uint8	m_byUDPVer;
	uint8	m_bySourceExchangeVer;
	uint8	m_byAcceptCommentVer;
	uint8	m_byExtendedRequestsVer;
	uint8	m_cFailed;
	uint32	m_dwLastSourceRequest;
	uint32	m_dwLastSourceAnswer;
	bool	m_bFileListRequested;
	bool	m_bFriendSlot;
	bool	m_bCommentDirty;
	CTypedPtrList<CPtrList, Packet*>				 m_WaitingPackets_list;
	CSourceEntry*	m_sourceEntry;	// Added by Tarod (LS tweak)

	//upload
	void CreateStandartPackets(byte* data,uint32 togo, Requested_Block_Struct* currentblock);
	void CreatePackedPackets(byte* data,uint32 togo, Requested_Block_Struct* currentblock);
	bool		m_bBanned;
	uint32	compressiongain; /// Add show compression
	uint32  notcompressed; // Add show compression

	uint32		m_nTransferedUp;
	uint8		m_byUploadState;
	uint32		m_dwWaitTime;
	uint32		m_dwUploadTime;
	uint32		m_nMaxSendAllowed;
	uint32		m_nAvUpDatarate;
	uint32		m_cAsked;
	uint32		m_dwLastUpRequest;
	uint32		m_dwBanTime;
	int16		m_filepriority;	// Added by Tarod (herbert code)
	bool		m_bUsedComprUp;	//only used for interface output
	
	uint32		m_nSessionUp; // VQB add for UL slot control (Rectencle)
	bool		m_nPrefetchedAlready; // Rectencle Connection-Prefetching
	uint32		m_pszFileSize; //Rectencle Connection-Prefetching

	void		SendMsgBeingBanned(); //Rectencle

	CList<int,int>									 m_AvarageUDR_list;
	CTypedPtrList<CPtrList, Packet*>				 m_BlockSend_queue;
	CTypedPtrList<CPtrList, Requested_Block_Struct*> m_BlockRequests_queue;
	CTypedPtrList<CPtrList, Requested_Block_Struct*> m_DoneBlocks_list;
	CTypedPtrList<CPtrList, Requested_File_Struct*>	 m_RequestedFiles_list;
	//download
	bool		m_bRemoteQueueFull;
	bool		usedcompressiondown; //only used for interface output
	uint8		m_nDownloadState;
	uint16		m_nPartCount;
	uint32		m_cDownAsked;
	uint8*		m_abyPartStatus;
	uint32		m_dwLastAskedTime;
	char*		m_pszClientFilename;
	uint32		m_nTransferedDown;
	uint32      m_nLastBlockOffset;   // Patch for show parts that you download [Cax2]
	uint32		m_nDownDatarate;
	uint32		m_nDownDataRateMS;
	uint32		m_nAvDownDatarate;
	uint16		m_cShowDR;
	uint32		m_dwLastBlockReceived;
	uint16		m_nRemoteQueueRank;
	bool		m_bCompleteSource;
	bool		m_bReaskPending;
	bool		m_bUDPPending;
	CList<int,int>									 m_AvarageDDR_list;
	CTypedPtrList<CPtrList, Pending_Block_Struct*>	 m_PendingBlocks_list;
	CTypedPtrList<CPtrList, Requested_Block_Struct*> m_DownloadBlocks_list;
	CTypedPtrList<CPtrList, CPartFile*>				 m_OtherRequests_list;
	CTypedPtrList<CPtrList, CPartFile*>				 m_OtherNoNeeded_list;

	static CBarShader s_StatusBar;
	// chat
	uint8 m_byChatstate;
	CString m_strComment;
	int8 m_iRate; 
};
