//this file is part of NeoMule
//Copyright (C)2006 David Xanatos ( Xanatos@Lycos.at / http://neomule.sourceforge.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.

#include "StdAfx.h"
#include "emule.h"
#include "ClientFileStatus.h"
#include "KnownFile.h"
#include "SafeFile.h"
#include "Preferences.h"
#include "otherfunctions.h"
#include "log.h"
#include "packets.h"
#include "NeoOpCodes.h"

// NEO: SCFS - [SmartClientFileStatus] -- Xanatos -->

/////////////////////////////////////////////////////////////
// CClientFileStatus
//

CClientFileStatus::CClientFileStatus(CKnownFile* file)
{
	md4cpy(m_abyFileHash, file->GetFileHash());
	m_nPartCount = file->GetPartCount();
	m_nED2KPartCount = file->GetED2KPartCount();
	m_nFileSize = file->GetFileSize();

	for(uint16 i = 0; i<CFS_COUNT; i++)
		m_abyPartStatus[i] = NULL;
	m_bCompleteSource = false;

	m_strFileName.Empty();
	m_strFileComment.Empty();
	m_uFileRating = 0;

	m_nCompleteSourcesCount = 0;

	m_uSCTpos = 0; // NEO: SCT - [SubChunkTransfer]
}

CClientFileStatus::~CClientFileStatus()
{
	for(uint16 i = 0; i<CFS_COUNT; i++)
		delete[] m_abyPartStatus[i];
	m_BlockMaps.RemoveAll(); // NEO: SCT - [SubChunkTransfer]
}

bool CClientFileStatus::ReadFileStatus(CSafeMemFile* data, EPartStatus type, bool throwError)
{
	UINT nED2KPartCount = data->ReadUInt16();

	if (m_abyPartStatus[type] == NULL) // allocate file status
		m_abyPartStatus[type] = new uint8[m_nPartCount];
	
	if (!nED2KPartCount) // no status
	{
		if(type == CFS_Normal){
			m_bCompleteSource = true;
			// NEO: SCT - [SubChunkTransfer]
			if(thePrefs.UseSubChunkTransfer())
				m_BlockMaps.RemoveAll();
			// NEO: SCT END
		}
		memset(m_abyPartStatus[type],(type == CFS_Normal),m_nPartCount); // set default status state
	}
	else
	{
		if(type == CFS_Normal)
			m_bCompleteSource = false;
		if (m_nED2KPartCount != nED2KPartCount) {
			if (thePrefs.GetVerbose()) {
				DebugLogWarning(_T("FileName: \"%s\""), GetFileName());
				DebugLogWarning(_T("FileStatus: %s"), DbgGetFileStatus(nED2KPartCount, data));
			}
			CString strError;
			strError.Format(_T("ReadFileStatus %u - wrong part number recv=%u  expected=%u  %s"), (UINT)type, nED2KPartCount, m_nED2KPartCount, DbgGetFileInfo(GetFileHash()));
			if(throwError)
				throw strError;
			return false;
		}
		
		const bool bCleanUpSCT = (type != CFS_Incomplete && thePrefs.UseSubChunkTransfer()); // NEO: SCT - [SubChunkTransfer]
		const bool bCleanUpRPS = ((type == CFS_Hiden || type == CFS_Blocked) && m_abyPartStatus[CFS_Normal]); // NEO: RPS - [RealPartStatus]
		const bool bCleanUpAHOS = (bCleanUpRPS && thePrefs.UseAntiHideOS() && m_abyPartStatus[CFS_History]); // NEO: AHOS - [AntiHideOS]
		// read the status
		uint16 done = 0;
		while (done != m_nPartCount){
			uint8 toread = data->ReadUInt8();
			for (sint32 i = 0;i != 8;i++){
				if((m_abyPartStatus[type][done] = ((toread>>i) & 1) ? 1 : 0) == TRUE){
					// NEO: SCT - [SubChunkTransfer]
					if(bCleanUpSCT)
						m_BlockMaps.RemoveKey(done);
					// NEO: SCT END

					// NEO: RPS - [RealPartStatus]
					// We must cleanup the rps status to avoid double counting of parts, for IPS calculation, 
					if(bCleanUpRPS){
						if(m_abyPartStatus[CFS_Normal][done] == TRUE){
							if(type == CFS_Hiden)
								m_abyPartStatus[CFS_Hiden][done] = FALSE; // part is not hidden
							else if(type == CFS_Blocked)
								m_abyPartStatus[CFS_Normal][done] = FALSE; // we should never publish blocked parts, there are *not* downloadable !!!
						}
						if(bCleanUpAHOS && m_abyPartStatus[CFS_History][done] == TRUE)
							m_abyPartStatus[CFS_History][done] = FALSE; // always cleanup AHOS, RPS will take care it present
					}
					// NEO: RPS END
				}
				done++;
				if (done == m_nPartCount)
					break;
			}
		}
	}

	// NEO: AHOS - [AntiHideOS]
	if(type == CFS_Normal && thePrefs.UseAntiHideOS())
		UpdateStatusHistory();
	// NEO: AHOS END

	return true;
}

bool CClientFileStatus::WriteFileStatus(CSafeMemFile* data, EPartStatus type)
{
	if(m_abyPartStatus[type] == NULL)
		return false;

	if(m_bCompleteSource && type == CFS_Normal)
	{
		data->WriteUInt16(0);
	}
	else
	{
		data->WriteUInt16((uint16)m_nED2KPartCount);

		UINT done = 0;
		while (done != m_nED2KPartCount){
			uint8 towrite = 0;
			for (UINT i = 0; i < 8; i++){
				if (m_abyPartStatus[type][i])
					towrite |= (1<<i);
				done++;
				if (done == m_nED2KPartCount)
					break;
			}
			data->WriteUInt8(towrite);
		}
	}
	
	return true;
}

void CClientFileStatus::UpdateStatusHistory()
{
	if (m_abyPartStatus[CFS_History] == NULL){ // allocate file status
		m_abyPartStatus[CFS_History] = new uint8[m_nPartCount];
		memset(m_abyPartStatus[CFS_History],0,m_nPartCount);
	}

	for (UINT i = 0; i < m_nPartCount; i++)
		if (m_abyPartStatus[CFS_Normal][i])
			m_abyPartStatus[CFS_History][i] = 1;
}

void CClientFileStatus::FillDefault()
{
	if (m_abyPartStatus[CFS_Normal] == NULL) // allocate file status
		m_abyPartStatus[CFS_Normal] = new uint8[m_nPartCount];

	memset(m_abyPartStatus[CFS_Normal],1,m_nPartCount); // set default status state
}

// NEO: SCT - [SubChunkTransfer]
bool CClientFileStatus::ReadSubChunkMaps(CSafeMemFile* data)
{
	uint16 count = data->ReadUInt16();
	uint16 part;
	tBlockMap blockmap;
	for (uint16 i = 0; i < count; i++)
	{
		part = data->ReadUInt16();
		data->Read(&blockmap.map,7);
		m_BlockMaps.SetAt(part,blockmap); // copy the map into our global map
	}
	return true;
}

bool CClientFileStatus::GetBlockMap(UINT part, tBlockMap** map)
{
	*map = NULL; // it is important to reset the map pointer, otherwice GetNextEmptyBlockInPart will fail
	CBlockMaps::CPair *pCurVal;
	pCurVal = m_BlockMaps.PLookup((uint16)part);
	if(pCurVal)
		*map = &pCurVal->value; // return reference to object storred in our global map
	return *map != NULL;
}
// NEO: SCT END

// NEO: NPC - [NeoPartCatch]
bool CClientFileStatus::IsPartAvailable(UINT part, int iMode, bool bAll) const
{
	uint8* abyPartStatus = m_abyPartStatus[CFS_Normal];
	if(abyPartStatus && abyPartStatus[part])
		return true;
	switch (iMode){
	case PART_CATCH_REAL:
		abyPartStatus = m_abyPartStatus[CFS_Hiden];
		return (abyPartStatus && abyPartStatus[part]);
	case PART_CATCH_REAL_HIDEN:
		abyPartStatus = m_abyPartStatus[CFS_Hiden];
		if (abyPartStatus && abyPartStatus[part])
			return true;
		abyPartStatus = m_abyPartStatus[CFS_Blocked];
		if (abyPartStatus && abyPartStatus[part])
			return bAll;
	case PART_CATCH_HIDEN:
		abyPartStatus = m_abyPartStatus[CFS_History];
		return (abyPartStatus && abyPartStatus[part]);
	default:
		return false;
	}
}
// NEO: NPC END

// NEO: ICS - [InteligentChunkSelection]
bool CClientFileStatus::IsIncPartAvailable(UINT part) const
{
	uint8* abyPartStatus = m_abyPartStatus[CFS_Incomplete];
	return (abyPartStatus && abyPartStatus[part]);
}

// NEO: ICS END

#ifdef NEO_SS // NEO: NSS - [NeoSourceStorage]
bool CClientFileStatus::WriteFileStatusTag(EPartStatus type, CFileDataIO* file)
{
	CSafeMemFile data(16+16);
	if(!WriteFileStatus(&data,type))
		return false;

	uint8 name;
	switch(type){
	case CFS_Normal:		name = SFT_PART_STATUS; break;
	case CFS_Incomplete:	name = SFT_INC_PART_STATUS; break; // NEO: ICS - [InteligentChunkSelection]
	case CFS_Hiden:			name = SFT_HIDEN_PART_STATUS; break; // NEO: RPS - [RealPartStatus]
	case CFS_Blocked:		name = SFT_BLOCKED_PART_STATUS; break; // NEO: RPS - [RealPartStatus]
	case CFS_History:		name = SFT_SEEN_PART_STATUS; break; // NEO: AHOS - [AntiHideOS]
	default: return false;
	}

	uint32 size = (UINT)data.GetLength();
	BYTE* tmp = data.Detach();
	CTag tag(name,size,tmp);
	free(tmp);
	tag.WriteNewEd2kTag(file);
	return true;
}

bool CClientFileStatus::ReadFileStatusTag(CTag* tag)
{
	if(!tag->IsBlob())
		return false;

	EPartStatus type;
	switch(tag->GetNameID()){
	case SFT_PART_STATUS:			type = CFS_Normal; break;
	case SFT_INC_PART_STATUS:		type = CFS_Incomplete; break; // NEO: ICS - [InteligentChunkSelection]
	case SFT_HIDEN_PART_STATUS:		type = CFS_Hiden; break; // NEO: RPS - [RealPartStatus]
	case SFT_BLOCKED_PART_STATUS:	type = CFS_Blocked; break; // NEO: RPS - [RealPartStatus]
	case SFT_SEEN_PART_STATUS:		type = CFS_History; break; // NEO: AHOS - [AntiHideOS]
	default: return false;
	}

	CSafeMemFile data(tag->GetBlob(),tag->GetBlobSize());
	return ReadFileStatus(&data,type,false);
}
#endif // NEO_SS // NEO: NSS END

// NEO: SCFS END <-- Xanatos --