//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"

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

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

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

// clientsoft
#define SO_EMULE			0
#define SO_CDONKEY			1
#define SO_EDONKEYHYBRID	50
#define	SO_EDONKEY			51
#define SO_MLDONKEY			52
#define SO_OLDEMULE			53
#define SO_UNKNOWN			54

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);
	~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			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; }
	uint32			GetVersion()				{return m_nClientVersion;}
	uint8			GetMuleVersion()			{return m_byEmuleVersion;}
	bool			ExtProtocolAvailable()		{return m_bEmuleProtocol;}
	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;}
	float			GetCompression()	{return (float)compressiongain/notcompressed*100.0f;} // Add rod show compression
	void			ResetCompressionGain() {compressiongain = 0; notcompressed=1;} // Add show compression

	void			RequestSharedFileList();
	void			ProcessSharedFileList(char* pachPacket, uint32 nSize);
	
	void			SetUserName(char* pszNewName);
	uint8			GetClientSoft()				{return m_clientSoft;}
	void			ReGetClientSoft();
	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
	uint32	compressiongain; /// Add show compression
	uint32  notcompressed; // 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());}
	sint32			GetDatarate()				{return m_nUpDatarate;}	
	uint32			GetScore(bool sysvalue, bool isdownloading = false, bool onlybasevalue = false);
	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, bool& wantsMoreBandwidth);
	void			ClearUploadBlockRequests(bool flushRestInSplittedPackage = true);
	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();
	uint32			GetSessionUp()			{return m_nTransferedUp - m_nCurSessionUp;}
	void			ResetSessionUp()		{m_nCurSessionUp = m_nTransferedUp;} 
	void			ProcessUpFileStatus(char* packet,uint32 size);
	uint16			GetUpPartCount()			{return m_nUpPartCount;}
	void			DrawUpStatusBar(CDC* dc, RECT* rect, bool onlygreyrect, bool  bFlat);
    void            SetLastGotULData()         { m_LastGotULData = ::GetTickCount(); };
    DWORD           GetLastGotULData()         { return m_LastGotULData; };
    bool CUpDownClient::HasReleasePrio();
    void CUpDownClient::ClearUploadDoneBlocks();
    float CUpDownClient::GetCombinedFilePrioAndCredit();

	//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);
	sint32			CalculateDownloadRate();
	uint16			GetAvailablePartCount();
	bool			SwapToAnotherFile(bool bIgnoreNoNeeded = false);
	void			UDPReaskACK(uint16 nNewQR);
	void			UDPReaskFNF();
	void			UDPReaskForDownload();
	bool			IsSourceRequestAllowed();
	
	CPartFile*		reqfile;
	int				sourcesslot;

	//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);
	void UpdateDisplayedInfo(boolean force=false);

    uint16 CUpDownClient::GetPriorPartNumber() { return m_iPriorPartNumber; }
    void CUpDownClient::SetPriorPartNumber(uint16 priorPartNumber) { m_iPriorPartNumber = priorPartNumber; m_bHasPriorPart = true; }
    bool CUpDownClient::GetHasPriorPart() { return m_bHasPriorPart; }
    void CUpDownClient::ClearPriorPartNumber() { m_bHasPriorPart = false; }
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;
	sint32	m_nUpDatarate;
	//uint32	dataratems;
	//uint32	m_cSendblock;
	uint8	m_byEmuleVersion;
	uint8	m_byDataCompVer;
	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;
	uint8	m_clientSoft;
	uint32	m_dwLastSourceRequest;
	uint32	m_dwLastSourceAnswer;
	bool	m_bFileListRequested;
	bool	m_bFriendSlot;
	bool	m_bCommentDirty;
	bool	m_bIsHybrid;
	uint32	m_byCompatableClient;
	CTypedPtrList<CPtrList, Packet*>				 m_WaitingPackets_list;
	DWORD	m_lastRefreshedDLDisplay;

	//upload
	void CreateStandartPackets(byte* data,uint32 togo, Requested_Block_Struct* currentblock);
	void CreatePackedPackets(byte* data,uint32 togo, Requested_Block_Struct* currentblock);
    void CUpDownClient::splitAndAddPacketsToSendQueue(Packet* packet);
	bool		m_bBanned;
	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;
	bool		m_bUsedComprUp;	//only used for interface output
	uint32		m_nCurSessionUp;
	uint16		m_nUpPartCount;
	static		CBarShader s_UpStatusBar;
	public:
	uint8*		m_abyUpPartStatus;
	//uint32		m_nSumForAvgUpDataRate;

	private:
	CList<int,int>									 m_AvarageUDR_list;
	CList<DWORD,DWORD>                               m_avarage_UL_tick_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;

    DWORD       m_lastRefreshedULDisplay;
    uint32 m_LastGotULData;
    
    //download
    int CUpDownClient::GetFilePrioAsNumber();

	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;
	uint32		m_nSumForAvgDownDataRate;

	CList<int,int>									 m_AvarageDDR_list;
	CList<DWORD,DWORD>                               m_avarage_DL_tick_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;

    DWORD       m_lastCalculatedDownloadRate;

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

    uint16 m_iPriorPartNumber;
    bool m_bHasPriorPart;
};
