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


#include "StdAfx.h"
#include "knownfilelist.h"
#include "opcodes.h"
#include "emule.h"

CKnownFileList::CKnownFileList(char* in_appdir)
{
	appdir = in_appdir;
	accepted = 0;
	requested = 0;
	transfered = 0;
	Init();
}

CKnownFileList::~CKnownFileList()
{
	Clear();
}

bool CKnownFileList::Init()
{
	CKnownFile* Record = 0; 
	CSafeFile file;
	try {
		char* fullpath = new char[strlen(appdir)+MAX_PATH];
		strcpy(fullpath,appdir);
		strcat(fullpath,"known.met");
		if (!file.Open(fullpath,CFile::modeRead|CFile::osSequentialScan)){
			delete[] fullpath;
			return false;
		}
		delete[] fullpath;
		
		uint8 header;
		file.Read(&header,1);
		if (header != MET_HEADER){
			file.Close();
			return false;
		}
		
		// TODO: Hmm... This looks dangerous; lock the mutex, and if we get a file exception we don't unlock it...
		CSingleLock sLock(&list_mut,true); // to make sure that its thread-safe
		uint32 RecordsNumber;
		file.Read(&RecordsNumber,4);
		for (uint32 i = 0; i != RecordsNumber; i++) {
			Record =  new CKnownFile();
			Record->LoadFromFile(&file);
			Add(Record);
		}
		sLock.Unlock();
		file.Close();
		return true;
	}

	// TODO: we can't call AddLogLine here because the main dlg isn't created yet....
	catch(CFileException* error){
		OUTPUT_DEBUG_TRACE();
		if (error->m_cause == CFileException::endOfFile)
			theApp.emuledlg->AddLogLine(true,GetResString(IDS_ERR_SERVERMET_BAD)); //CFile exception: endOfFile, File known.met, OS error information = 0. Crash on start...
		else{
			char buffer[150];
			error->GetErrorMessage(buffer,150);
			theApp.emuledlg->AddLogLine(true,GetResString(IDS_ERR_SERVERMET_UNKNOWN),buffer);
		}
		error->Delete();
		return false;
	}
}

void CKnownFileList::Save()
{
	FILE* file = NULL;
	char* fullpath = new char[strlen(appdir)+MAX_PATH];
	strcpy(fullpath,appdir);
	strcat(fullpath,"known.met");
	if (!(file = fopen(fullpath, "wb"))){
		delete[] fullpath;	
		return;
	}
	delete[] fullpath;

	// Added by Tarod
	// We need to do it thread safe because now is called under CUploadQueue::RemoveFromUploadQueue()
	CSingleLock sLock(&list_mut, true);

	fputc(MET_HEADER,file);
	uint32 RecordsNumber = GetCount();
	fwrite(&RecordsNumber,4,1,file);

	// Old write routine
	//for (uint32 i = 0; i < RecordsNumber; i++) {
	//	ElementAt(i)->WriteToFile(file);
	//}

	// This routine was changed to take in account possible NULL pointers
	// I think is not needed anymore because now is thread safe
	uint32 i = 0;
	while (i < RecordsNumber) {
		if (ElementAt(i) == NULL) { // if is NULL is unuseful so we remove it!
			RecordsNumber--;
			RemoveAt(i);
		} else { // element is not NULL, write it an go for next one
			((CKnownFile*)ElementAt(i))->WriteToFile(file);
			i++;
	}
	}

	// Maybe we have less records than before, rewrite number of records
	fseek(file, 1L, SEEK_SET);
	fwrite(&RecordsNumber, 4, 1, file);

	sLock.Unlock(); // free mutex before closing
	fclose(file);
}

void CKnownFileList::Clear()
{
	for (int i = 0; i != GetSize();i++)
		safe_delete(this->ElementAt(i));
	RemoveAll();
	SetSize(0);
}

CKnownFile* CKnownFileList::FindKnownFile(char* filename,uint32 in_date,uint32 in_size)
{
	for (int i = 0;i != this->GetCount();i++){
		if (ElementAt(i)->GetFileDate() == in_date && ElementAt(i)->GetFileSize() == in_size && (!strcmp(filename,ElementAt(i)->GetFileName())))
			return ElementAt(i);
	}
	return 0;
}

void CKnownFileList::SafeAddKFile(CKnownFile* toadd)
{
	CSingleLock sLock(&list_mut,true);
	Add(toadd);
	sLock.Unlock();
}
