// parts of this file are based on work from pan One (http://home-3.tiscali.nl/~meost/pms/)
//
// This file is part of the aMule Project
//
// Copyright (c) 2003-2004 aMule Project ( http://www.amule-project.net )
// 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.


#include "sys.h"
#include <sys/types.h>
#include <sys/stat.h>
	
#include "KnownFile.h"		// Interface declarations.
#include "otherfunctions.h"	// Needed for nstrdup
#include "ini2.h"		// Needed for CIni
#include "UploadQueue.h"	// Needed for CUploadQueue
#include "CMemFile.h"		// Needed for CMemFile
#include "SharedFilesCtrl.h"	// Needed for CSharedFilesCtrl
#include "SharedFilesWnd.h"	// Needed for CSharedFilesWnd
#include "amuleDlg.h"		// Needed for CamuleDlg
#include "updownclient.h"	// Needed for CUpDownClient
#include "BarShader.h"		// Needed for CBarShader
#include "packets.h"		// Needed for CTag
#include "PartFile.h"		// Needed for PR_HIGH
#include "Preferences.h"	// Needed for CPreferences
#include "SharedFileList.h"	// Needed for CSharedFileList
#include "KnownFileList.h"	// Needed for CKnownFileList
#include "CamuleAppBase.h"	// Needed for theApp

static void MD4Transform(uint32 Hash[4], uint32 x[16]);

// SLUGFILLER: heapsortCompletesrc
static void HeapSort(CArray<uint16,uint16> &count, int32 first, int32 last){
	int32 r;
	for ( r = first; !(r & 0x80000000) && (r<<1) < last; ){
		uint32 r2 = (r<<1)+1;
		if (r2 != last)
			if (count[r2] < count[r2+1])
				r2++;
		if (count[r] < count[r2]){
			uint16 t = count[r2];
			count[r2] = count[r];
			count[r] = t;
			r = r2;
		}
		else
			break;
	}
}
// SLUGFILLER: heapsortCompletesrc

void CFileStatistic::AddRequest(){
	requested++;
	alltimerequested++;
	theApp.knownfiles->requested++;
	theApp.sharedfiles->UpdateItem(fileParent);
}
	
void CFileStatistic::AddAccepted(){
	accepted++;
	alltimeaccepted++;
	theApp.knownfiles->accepted++;
	theApp.sharedfiles->UpdateItem(fileParent);
}
	
void CFileStatistic::AddTransferred(uint64 bytes){
	transfered += bytes;
	alltimetransferred += bytes;
	theApp.knownfiles->transfered += bytes;
	theApp.sharedfiles->UpdateItem(fileParent);
}

CKnownFile::CKnownFile(){
	m_iFileType = 2;
	directory = NULL;
	m_pszFileName = 0;
	m_nFileSize = 0;
	date = 0;
	m_iPermissions = PERM_ALL;
	statistic.fileParent=this;
	if (theApp.glob_prefs->GetNewAutoUp()){
		m_iUpPriority = PR_HIGH;
		m_bAutoUpPriority = true;
	} else {
		m_iUpPriority = PR_NORMAL;
		m_bAutoUpPriority = false;
	}
	m_iQueuedCount = 0;
	m_bCommentLoaded=false;
	m_iRate=0;
	m_strComment="";
	m_iPartCount=0;
	m_PublishedED2K = false;
	
	m_nCompleteSourcesTime = time(NULL);

}

CKnownFile::~CKnownFile(){
	for (int i = 0; i != hashlist.GetSize(); i++)
		if (hashlist[i])
			delete[] hashlist[i];
	for (int i = 0; i != taglist.GetSize(); i++)
		delete taglist[i];
//	if (filename)	// done by CAbstractFile destructor
//		delete[] filename;
	if (directory)
		delete[] directory;
	m_AvailPartFrequency.RemoveAll();
}

CBarShader CKnownFile::s_ShareStatusBar(16);

void CKnownFile::DrawShareStatusBar(wxDC* dc, wxRect rect, bool onlygreyrect, bool  bFlat) {
/*
   DWORD crBoth; 
   DWORD crNeither; 
   DWORD crClientOnly; 
   DWORD crPending;
   DWORD crNextPending;

   if(bFlat) { 
		crBoth = RGB(0, 150, 0);
		crNeither = RGB(224, 224, 224);
		crClientOnly = RGB(0, 0, 0);
		crPending = RGB(255,208,0);
		crNextPending = RGB(255,255,100);
   } else {
		crBoth = RGB(0, 192, 0);
		crNeither = RGB(240, 240, 240);
		crClientOnly = RGB(104, 104, 104);
		crPending = RGB(255, 208, 0);
		crNextPending = RGB(255,255,100);
   } 
 */

   s_ShareStatusBar.SetFileSize(GetFileSize()); 
   s_ShareStatusBar.SetHeight(rect.GetHeight()); 
   s_ShareStatusBar.SetWidth(rect.GetWidth()); 
   s_ShareStatusBar.Fill(RGB(255,0,0));

   if (!onlygreyrect && !m_AvailPartFrequency.IsEmpty()) {
   	for (int i = 0;i != GetPartCount();i++)
   		if(m_AvailPartFrequency[i] > 0 ){
   			DWORD color = RGB(0, (210-(22*(m_AvailPartFrequency[i]-1)) <  0)? 0:210-(22*(m_AvailPartFrequency[i]-1)), 255);
			s_ShareStatusBar.FillRange(PARTSIZE*(i),PARTSIZE*(i+1),color);
		}
   }
   s_ShareStatusBar.Draw(dc, rect.GetLeft(), rect.GetTop(), bFlat);
}

void CKnownFile::NewAvailPartsInfo(){
	
	// Cache part count
	uint16 partcount = GetPartCount();
	bool flag = (time(NULL) - m_nCompleteSourcesTime > 0); 
	
	CArray<uint16,uint16> count;	// SLUGFILLER: heapsortCompletesrc
	count.SetSize(0, m_ClientUploadList.GetSize());

	if(m_AvailPartFrequency.GetSize() < partcount)
	{
		m_AvailPartFrequency.SetSize(partcount);
	}

	// Reset part counters
	for(int i = 0; i < partcount; i++)
	{
		m_AvailPartFrequency[i] = 0;
	}
	CUpDownClient* cur_src;
	if(this->IsPartFile())
	{
		cur_src = NULL;
	}

	uint16 cur_count = 0;
	for (POSITION pos = m_ClientUploadList.GetHeadPosition(); pos != 0; )
	{
		cur_src = m_ClientUploadList.GetNext(pos);
		for (uint16 i = 0; i < partcount; i++)
		{
			if (cur_src->IsUpPartAvailable(i))
			{
				m_AvailPartFrequency[i] +=1;
			}
		}
		cur_count= cur_src->GetUpCompleteSourcesCount();
		if ( flag && cur_count )
		{
			count.Add(cur_count);
		}
	}

	if(flag)
	{
		m_nCompleteSourcesCount = m_nCompleteSourcesCountLo = m_nCompleteSourcesCountHi = 0;
	
		for (uint16 i = 0; i < partcount; i++)
		{
			if( !i )
			{
				m_nCompleteSourcesCount = m_AvailPartFrequency[i];
			}
			else if( m_nCompleteSourcesCount > m_AvailPartFrequency[i])
			{
				m_nCompleteSourcesCount = m_AvailPartFrequency[i];
			}
		}
	
		if (m_nCompleteSourcesCount)
		{
			count.Add(m_nCompleteSourcesCount);
		}
	
		count.FreeExtra();
	
		int32 n = count.GetSize();
		if (n > 0)
		{
			// SLUGFILLER: heapsortCompletesrc
			int32 r;
			for (r = n/2; r--; )
				HeapSort(count, r, n-1);
			for (r = n; --r; ){
				uint16 t = count[r];
				count[r] = count[0];
				count[0] = t;
				HeapSort(count, 0, r-1);
			}
			// SLUGFILLER: heapsortCompletesrc
			// calculate range
			int32 i= n >> 1;		// (n / 2)
			int32 j= (n * 3) >> 2;	// (n * 3) / 4
			int32 k= (n * 7) >> 3;	// (n * 7) / 8
			if (n < 5)
			{
				m_nCompleteSourcesCount= count.GetAt(i);
				m_nCompleteSourcesCountLo= 0;
				m_nCompleteSourcesCountHi= m_nCompleteSourcesCount;
			}
			else if (n < 10)
			{
				m_nCompleteSourcesCount= count.GetAt(i);
				m_nCompleteSourcesCountLo= count.GetAt(i - 1);
				m_nCompleteSourcesCountHi= count.GetAt(i + 1);
			}
			else if (n < 20)
			{
				m_nCompleteSourcesCount= count.GetAt(i);
				m_nCompleteSourcesCountLo= count.GetAt(i);
				m_nCompleteSourcesCountHi= count.GetAt(j);
			}
			else
			{
				m_nCompleteSourcesCount= count.GetAt(j);
				m_nCompleteSourcesCountLo= m_nCompleteSourcesCount;
				m_nCompleteSourcesCountHi= count.GetAt(k);
			}
		}
		m_nCompleteSourcesTime = time(NULL) + (60);
	}
//	if (theApp.amuledlg->sharedfileswnd->m_hWnd)
		theApp.amuledlg->sharedfileswnd->sharedfilesctrl->UpdateItem(this);
}

void CKnownFile::AddUploadingClient(CUpDownClient* client){
	POSITION pos = m_ClientUploadList.Find(client); // to be sure
	if(pos == NULL){
		m_ClientUploadList.AddTail(client);
	}
}

void CKnownFile::RemoveUploadingClient(CUpDownClient* client){
	POSITION pos = m_ClientUploadList.Find(client); // to be sure
	if(pos != NULL){
		m_ClientUploadList.RemoveAt(pos);
	}
}

void CKnownFile::SetPath(LPCTSTR path){
	if (directory)
		delete[] directory;
	directory = nstrdup(path);
}

void CKnownFile::SetFilePath(LPCTSTR pszFilePath)
{
	m_strFilePath = pszFilePath;
}

bool CKnownFile::CreateFromFile(char* in_directory,char* in_filename, volatile int const * notify){
	// TODO Errorhandling
	//first create the filehashset
	// open file
	directory = nstrdup(in_directory);

	char* namebuffer = new char[strlen(in_directory)+strlen(in_filename)+2];
	sprintf(namebuffer,"%s/%s",in_directory,in_filename);
	FILE* file = fopen(namebuffer,"r");
	delete[] namebuffer;
	if (!file) {
		printf("%s/%s ei aukea\n",in_directory,in_filename);
		return false;
	}
	// set filesize + name
	m_pszFileName = nstrdup(in_filename);
	//filesize =_filelength(file->_file);
	long curpos=ftell(file);
	fseek(file,0,SEEK_END);
	m_nFileSize=ftell(file);
	fseek(file,curpos,SEEK_SET);
	m_AvailPartFrequency.SetSize(GetPartCount());
	for (uint32 i=0; i != GetPartCount(); i++)
		m_AvailPartFrequency.Add(0);

	// create hashset
	uint32 togo = m_nFileSize;
	uint16 hashcount;
	for (hashcount = 0;togo >= PARTSIZE;) {
		uchar* newhash = new uchar[16];
		CreateHashFromFile(file,PARTSIZE,newhash);
		hashlist.Add(newhash);
		togo -= PARTSIZE;
		hashcount++;
		if ( notify && *notify ) {
			fclose(file);
			return false;
		}
	}
	uchar* lasthash = new uchar[16];
	memset(lasthash,0,16);
	CreateHashFromFile(file,togo,lasthash);
	if (!hashcount){
		memcpy(m_abyFileHash,lasthash,16);
		delete[] lasthash; // i_a: memleak 
	} 
	else {
		hashlist.Add(lasthash);
		uchar* buffer = new uchar[hashlist.GetCount()*16];
		for (int i = 0;i != hashlist.GetCount();i++)
			memcpy(buffer+(i*16),hashlist[i],16);
		CreateHashFromString(buffer,hashlist.GetCount()*16,m_abyFileHash);
		delete[] buffer;
	}
	// TODO: Add filetags

	// set lastwrite date
	struct stat fileinfo;
	fstat(fileno(file),&fileinfo);
	date = fileinfo.st_mtime;
	//finished
	fclose(file);
	return true;	
}


// needed for memfiles. its probably better to switch everything to CFile...
bool CKnownFile::LoadHashsetFromFile(CFile* file, bool checkhash){
	uchar checkid[16];
	file->Read(&checkid,16);
	uint16	parts;
	file->Read(&parts,2);
	for (int i = 0; i != parts; i++){
		uchar* cur_hash = new uchar[16];
		file->Read(cur_hash,16);
		hashlist.Add(cur_hash);
	}
	if (!checkhash){
		memcpy(m_abyFileHash,checkid,16);
		return true;
	}
	// trust noone ;-)

	if (!hashlist.IsEmpty()){
		uchar* buffer = new uchar[hashlist.GetCount()*16];
		for (int i = 0;i != hashlist.GetCount();i++)
			memcpy(buffer+(i*16),hashlist[i],16);
		CreateHashFromString(buffer,hashlist.GetCount()*16,checkid);
		delete[] buffer;
	}
	if (!memcmp(m_abyFileHash,checkid,16))
		return true;
	else{
		for (int i = 0; i != hashlist.GetSize(); i++)
			delete[] hashlist[i];
		hashlist.RemoveAll();
		return false;
	}
}

bool CKnownFile::LoadTagsFromFile(CFile* file){
	try
	{
	    uint32 tagcount;
	    file->Read(&tagcount,4);
		for (uint32 j = 0; j < tagcount;j++){
		try {
		    CTag* newtag = new CTag(file);
		    switch(newtag->tag->specialtag){
			case FT_FILENAME:{
				m_pszFileName = nstrdup(newtag->tag->stringvalue);
				delete newtag;
				break;
			}
			case FT_FILESIZE:{
				m_nFileSize = newtag->tag->intvalue;
				m_AvailPartFrequency.SetSize(GetPartCount());
				for (uint32 i=0; i != GetPartCount(); i++)
					m_AvailPartFrequency.Add(0);
				delete newtag;
				break;
			}
			case FT_ATTRANSFERED:{
				statistic.alltimetransferred = newtag->tag->intvalue;
				delete newtag;
				break;
			}
			case FT_ATTRANSFEREDHI:{
				uint32 hi,low;
				low=statistic.alltimetransferred;
				hi = newtag->tag->intvalue;
				uint64 hi2;
				hi2=hi;
				hi2=hi2<<32;
				statistic.alltimetransferred=low+hi2;
				delete newtag;
				break;
			}			
			case FT_ATREQUESTED:{
				statistic.alltimerequested = newtag->tag->intvalue;
				delete newtag;
				break;
			}
 			case FT_ATACCEPTED:{
				statistic.alltimeaccepted = newtag->tag->intvalue;
				delete newtag;
				break;
			}
			case FT_OLDDLPRIORITY:	// for compatibility only
			case FT_OLDULPRIORITY:	// for compatibility only
			case FT_DLPRIORITY:	// for compatibility only
			case FT_ULPRIORITY:{
				uint8 autoprio = PR_AUTO;
				m_iUpPriority = newtag->tag->intvalue;
				if( m_iUpPriority == autoprio ){
					m_iUpPriority = PR_HIGH;
					m_bAutoUpPriority = true;
				}
				else
					m_bAutoUpPriority = false;
				delete newtag;
				break;
			}
			case FT_PERMISSIONS:{
				m_iPermissions = newtag->tag->intvalue;
				delete newtag;
				break;
			}
			default:
				taglist.Add(newtag);
		    }
		}
		catch ( CStrangePacket )
		{ }
	    }
	}
	catch ( CInvalidPacket e )
	{
		printf("Problem reading known file - %s\n", e.what());
		return false;
	}
	return true;
}

bool CKnownFile::LoadDateFromFile(CFile* file){
	return (4 == file->Read(&date,4));
}

bool CKnownFile::LoadFromFile(CFile* file){
	return (LoadDateFromFile(file) && LoadHashsetFromFile(file,false) && LoadTagsFromFile(file));
}

bool CKnownFile::WriteToFile(FILE* file){
	// date
	fwrite(&date,4,1,file);
	// hashset
	fwrite(&m_abyFileHash,16,1,file);
	uint16 parts = hashlist.GetCount();
	fwrite(&parts,2,1,file);
	for (int i = 0; i != parts; i++)
		fwrite(hashlist[i],16,1,file);

	//tags
	const int iFixedTags = 8;
	uint32 tagcount = iFixedTags;
	// Float meta tags are currently not written. All older eMule versions < 0.28a have 
	// a bug in the meta tag reading+writing code. To achive maximum backward 
	// compatibility for met files with older eMule versions we just don't write float 
	// tags. This is OK, because we (eMule) do not use float tags. The only float tags 
	// we may have to handle is the '# Sent' tag from the Hybrid, which is pretty 
	// useless but may be received from us via the servers.
	// 
	// The code for writing the float tags SHOULD BE ENABLED in SOME MONTHS (after most 
	// people are using the newer eMule versions which do not write broken float tags).	
	for (int j = 0; j < taglist.GetCount(); j++){
		if (taglist[j]->tag->type == 2 || taglist[j]->tag->type == 3)
			tagcount++;
	}
	// standard tags
	fwrite(&tagcount,4,1,file);
	
	
	CTag(FT_FILENAME,    m_pszFileName                   ).WriteTagToFile(file);
	CTag(FT_FILESIZE,    m_nFileSize                     ).WriteTagToFile(file);
	// statistic
	// Kry - New "all time transfered" using uint64, from eMule 0.30e
	// The lack of this stuff might be the reason we're NOT getting
	// a nice persistent transfered bytes for each fila across relaunchs.
	uint32 tran;
	tran=statistic.alltimetransferred;
	CTag attag1(FT_ATTRANSFERED, tran);
	attag1.WriteTagToFile(file);
	tran=statistic.alltimetransferred>>32;
	CTag attag4(FT_ATTRANSFEREDHI, tran);
	attag4.WriteTagToFile(file);

	CTag attag2(FT_ATREQUESTED, statistic.GetAllTimeRequests());
	attag2.WriteTagToFile(file);
	
	CTag attag3(FT_ATACCEPTED, statistic.GetAllTimeAccepts());
	attag3.WriteTagToFile(file);

	// priority N permission
	CTag priotag(FT_ULPRIORITY, IsAutoUpPriority() ? PR_AUTO : m_iUpPriority);
	priotag.WriteTagToFile(file);

	CTag permtag(FT_PERMISSIONS, m_iPermissions);
	permtag.WriteTagToFile(file);

	//other tags
	for (int j = 0; j < taglist.GetCount(); j++){
		if (taglist[j]->tag->type == 2 || taglist[j]->tag->type == 3)
			taglist[j]->WriteTagToFile(file);
	}
	
	return ferror(file);
}

void CKnownFile::CreateHashFromInput(FILE* file,CFile* file2, int Length, uchar* Output, uchar* in_string) {
	// time critial
	bool PaddingStarted = false;
	uint32 Hash[4];
	Hash[0] = 0x67452301;
	Hash[1] = 0xEFCDAB89;
	Hash[2] = 0x98BADCFE;
	Hash[3] = 0x10325476;
	CMemFile* data = 0;
	if (in_string)
		data = new CMemFile(in_string,Length);
	uint32 Required = Length;
	uchar   X[64*128];  
	while (Required >= 64){
        uint32 len = Required / 64; 
        if (len > sizeof(X)/(64 * sizeof(X[0]))) 
             len = sizeof(X)/(64 * sizeof(X[0])); 
		if (in_string)
			data->Read(&X,len*64);
		else if (file)
            fread(&X,len*64,1,file); 
		else if (file2)
			file2->Read(&X,len*64);
		for (uint32 i = 0; i < len; i++) 
        { 
           MD4Transform(Hash, (uint32*)(X + i*64)); 
        }
		Required -= len*64;
	}
	// bytes to read
	Required = Length % 64;
	if (Required != 0){
		if (in_string)
			data->Read(&X,Required);
		else if (file)
			fread(&X,Required,1,file);
		else if (file2)
			file2->Read(&X,Required);
	}
	// in byte scale 512 = 64, 448 = 56
	if (Required >= 56){
		X[Required] = 0x80;
		PaddingStarted = TRUE;
		memset(&X[Required + 1], 0, 63 - Required);
		MD4Transform(Hash, (uint32*)X);
		Required = 0;
	}
	if (!PaddingStarted)
		X[Required++] = 0x80;
	memset(&X[Required], 0, 64 - Required);
	// add size (convert to bits)
	uint32 Length2 = Length >> 29;
	Length <<= 3;
	memcpy(&X[56], &Length, 4);
	memcpy(&X[60], &Length2, 4);
	MD4Transform(Hash, (uint32*)X);
	md4cpy(Output, Hash);
	delete data;
}
uchar* CKnownFile::GetPartHash(uint16 part){
	if (part >= hashlist.GetCount())
		return 0;
	return hashlist[part];
}

static void MD4Transform(uint32 Hash[4], uint32 x[16])
{
  uint32 a = Hash[0];
  uint32 b = Hash[1];
  uint32 c = Hash[2];
  uint32 d = Hash[3];

  /* Round 1 */
  MD4_FF(a, b, c, d, x[ 0], S11); // 01
  MD4_FF(d, a, b, c, x[ 1], S12); // 02
  MD4_FF(c, d, a, b, x[ 2], S13); // 03
  MD4_FF(b, c, d, a, x[ 3], S14); // 04
  MD4_FF(a, b, c, d, x[ 4], S11); // 05
  MD4_FF(d, a, b, c, x[ 5], S12); // 06
  MD4_FF(c, d, a, b, x[ 6], S13); // 07
  MD4_FF(b, c, d, a, x[ 7], S14); // 08
  MD4_FF(a, b, c, d, x[ 8], S11); // 09
  MD4_FF(d, a, b, c, x[ 9], S12); // 10
  MD4_FF(c, d, a, b, x[10], S13); // 11
  MD4_FF(b, c, d, a, x[11], S14); // 12
  MD4_FF(a, b, c, d, x[12], S11); // 13
  MD4_FF(d, a, b, c, x[13], S12); // 14
  MD4_FF(c, d, a, b, x[14], S13); // 15
  MD4_FF(b, c, d, a, x[15], S14); // 16

  /* Round 2 */
  MD4_GG(a, b, c, d, x[ 0], S21); // 17
  MD4_GG(d, a, b, c, x[ 4], S22); // 18
  MD4_GG(c, d, a, b, x[ 8], S23); // 19
  MD4_GG(b, c, d, a, x[12], S24); // 20
  MD4_GG(a, b, c, d, x[ 1], S21); // 21
  MD4_GG(d, a, b, c, x[ 5], S22); // 22
  MD4_GG(c, d, a, b, x[ 9], S23); // 23
  MD4_GG(b, c, d, a, x[13], S24); // 24
  MD4_GG(a, b, c, d, x[ 2], S21); // 25
  MD4_GG(d, a, b, c, x[ 6], S22); // 26
  MD4_GG(c, d, a, b, x[10], S23); // 27
  MD4_GG(b, c, d, a, x[14], S24); // 28
  MD4_GG(a, b, c, d, x[ 3], S21); // 29
  MD4_GG(d, a, b, c, x[ 7], S22); // 30
  MD4_GG(c, d, a, b, x[11], S23); // 31
  MD4_GG(b, c, d, a, x[15], S24); // 32

  /* Round 3 */
  MD4_HH(a, b, c, d, x[ 0], S31); // 33
  MD4_HH(d, a, b, c, x[ 8], S32); // 34
  MD4_HH(c, d, a, b, x[ 4], S33); // 35
  MD4_HH(b, c, d, a, x[12], S34); // 36
  MD4_HH(a, b, c, d, x[ 2], S31); // 37
  MD4_HH(d, a, b, c, x[10], S32); // 38
  MD4_HH(c, d, a, b, x[ 6], S33); // 39
  MD4_HH(b, c, d, a, x[14], S34); // 40
  MD4_HH(a, b, c, d, x[ 1], S31); // 41
  MD4_HH(d, a, b, c, x[ 9], S32); // 42
  MD4_HH(c, d, a, b, x[ 5], S33); // 43
  MD4_HH(b, c, d, a, x[13], S34); // 44
  MD4_HH(a, b, c, d, x[ 3], S31); // 45
  MD4_HH(d, a, b, c, x[11], S32); // 46
  MD4_HH(c, d, a, b, x[ 7], S33); // 47
  MD4_HH(b, c, d, a, x[15], S34); // 48

  Hash[0] += a;
  Hash[1] += b;
  Hash[2] += c;
  Hash[3] += d;
}

// Adde by Tarod [Juanjo]
void CAbstractFile::SetFileName(LPCTSTR NewName) 
{ 
   if (m_pszFileName != NULL) { 
      delete[] m_pszFileName; 
      m_pszFileName = new char[strlen(NewName) + 1]; 
      
	  wxString filenametemp;
	  filenametemp=NewName;//.Format("%s",NewName);
	  filenametemp.Replace("\\","-");
	  filenametemp.Replace(">","-");
	  filenametemp.Replace("<","-");
	  filenametemp.Replace("*","-");
	  filenametemp.Replace(":","-");
	  filenametemp.Replace("?","-");

	  sprintf(m_pszFileName, "%s", filenametemp.GetData());
   } 
} 
//End by Tarod

Packet*	CKnownFile::CreateSrcInfoPacket(CUpDownClient* forClient){
	CTypedPtrList<CPtrList, CUpDownClient*> srclist;
	//theApp.uploadqueue->FindSourcesForFileById(&srclist, forClient->reqfileid); //should we use "filehash"?
	theApp.uploadqueue->FindSourcesForFileById(&srclist, forClient->GetUploadFileID()); //should we use "filehash"?

	if(srclist.IsEmpty())
		return 0;

	CMemFile data;
	uint16 nCount = 0;

	//data.Write(forClient->reqfileid, 16);
	data.Write(forClient->GetUploadFileID(), 16);
	data.Write(&nCount, 2);

	//uint32 lastRequest = forClient->GetLastSrcReqTime();
	//we are only taking 30 random sources since we can't be sure if they have parts we need
	//this is hard coded because its a temp solution until next(?) version
	srand(time(NULL));
	for(int i = 0; i < 30; i++) {
		int victim = ((rand() >> 7) % srclist.GetSize());
		POSITION pos = srclist.FindIndex(victim);
		CUpDownClient *cur_src = srclist.GetAt(pos);
		if(!cur_src->HasLowID() && cur_src != forClient) {
			nCount++;
			uint32 dwID = cur_src->GetUserID();
			uint16 nPort = cur_src->GetUserPort();
			uint32 dwServerIP = cur_src->GetServerIP();
			uint16 nServerPort = cur_src->GetServerPort();
			data.Write(&dwID, 4);
			data.Write(&nPort, 2);
			data.Write(&dwServerIP, 4);
			data.Write(&nServerPort, 2);
			if (forClient->GetSourceExchangeVersion() > 1)
				data.Write(cur_src->GetUserHash(), 16);
		}

		srclist.RemoveAt(pos);
		if(srclist.GetSize() == 0)
			break;
	}
	if (!nCount)
		return 0;
	data.Seek(16);
	data.Write(&nCount,2);

	Packet* result = new Packet(&data, OP_EMULEPROT);
	result->opcode = OP_ANSWERSOURCES;
	if (nCount > 28)
		result->PackPacket();
	return result;
}

// Updates priority of file if autopriority is activated
//void CKnownFile::UpdateUploadAutoPriority(void)
void CKnownFile::UpdateAutoUpPriority(void)
{
		if (!this->IsAutoUpPriority())
			return;

		if ( GetQueuedCount() > 20 ) {
			if ( GetUpPriority() != PR_LOW ) {
				SetUpPriority(PR_LOW, false);
				theApp.amuledlg->sharedfileswnd->sharedfilesctrl->UpdateItem(this);
			}
			return;
		}

		if ( GetQueuedCount() > 1 ) {
			if ( GetUpPriority() != PR_NORMAL ) {
				SetUpPriority(PR_NORMAL, false);
				theApp.amuledlg->sharedfileswnd->sharedfilesctrl->UpdateItem(this);
		}
		return;
	}
	if ( GetUpPriority() != PR_HIGH ) {
		SetUpPriority(PR_HIGH, false);
		theApp.amuledlg->sharedfileswnd->sharedfilesctrl->UpdateItem(this);
	}
}

//For File Comment // 
void CKnownFile::LoadComment(){ 
   char buffer[100]; 
   char* fullpath = new char[strlen(theApp.glob_prefs->GetAppDir())+13]; 
   sprintf(fullpath,"%sfileinfo.ini",theApp.glob_prefs->GetAppDir()); 
   
   buffer[0] = 0;
   for (uint16 i = 0;i != 16;i++) 
      sprintf(buffer,"%s%02X",buffer,m_abyFileHash[i]); 
    
   wxString fp(fullpath), br(buffer);
   CIni ini(fp, br);
   m_strComment = ini.GetString("Comment").GetData(); 
   m_iRate = ini.GetInt("Rate", 0);//For rate
   m_bCommentLoaded=true;
   delete[] fullpath;
    
}    

void CKnownFile::SetFileComment(CString strNewComment){ 
   char buffer[100]; 
   char* fullpath = new char[strlen(theApp.glob_prefs->GetAppDir())+13]; 
   sprintf(fullpath,"%sfileinfo.ini",theApp.glob_prefs->GetAppDir()); 
       
   buffer[0] = 0; 
   for (uint16 i = 0;i != 16;i++) 
      sprintf(buffer,"%s%02X",buffer,m_abyFileHash[i]); 
    
   wxString fp(fullpath), br(buffer);
   CIni ini(fp, br);
    
   ini.WriteString ("Comment", strNewComment); 
   m_strComment = strNewComment;
   delete[] fullpath;
   
   CTypedPtrList<CPtrList, CUpDownClient*> srclist;
   theApp.uploadqueue->FindSourcesForFileById(&srclist, this->GetFileHash());

   for (POSITION pos = srclist.GetHeadPosition();pos != 0;srclist.GetNext(pos)){
	CUpDownClient *cur_src = srclist.GetAt(pos);
	cur_src->SetCommentDirty();
   }
   
}
// For File rate 
void CKnownFile::SetFileRate(int8 iNewRate){ 
   char buffer[100]; 
   char* fullpath = new char[strlen(theApp.glob_prefs->GetAppDir())+13]; 
   sprintf(fullpath,"%sfileinfo.ini",theApp.glob_prefs->GetAppDir()); 
       
   buffer[0] = 0; 
   for (uint16 i = 0;i != 16;i++) 
      sprintf(buffer,"%s%02X",buffer,m_abyFileHash[i]); 
    
   wxString fp(fullpath), br(buffer);
   CIni ini(fp, br); 
    
   ini.WriteInt ("Rate", iNewRate); 
   m_iRate = iNewRate; 
   delete[] fullpath;

  CTypedPtrList<CPtrList, CUpDownClient*> srclist;
  theApp.uploadqueue->FindSourcesForFileById(&srclist, this->GetFileHash());
  for (POSITION pos = srclist.GetHeadPosition();pos != 0;srclist.GetNext(pos)){
	CUpDownClient *cur_src = srclist.GetAt(pos);
	cur_src->SetCommentDirty();
  }
} 

void CKnownFile::SetUpPriority(uint8 iNewUpPriority, bool m_bsave){
	m_iUpPriority = iNewUpPriority;
	if( this->IsPartFile() && m_bsave )
		((CPartFile*)this)->SavePartFile();
}

void CKnownFile::SetPublishedED2K(bool val){
	m_PublishedED2K = val;
	theApp.amuledlg->sharedfileswnd->sharedfilesctrl->UpdateItem(this);
}
