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

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

#define KNOWN_MET_FILENAME	_T("config\\known.met")


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

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

bool CKnownFileList::Init()
{
	CString fullpath(appdir);
	fullpath += KNOWN_MET_FILENAME;
	CSafeBufferedFile file;
	CFileException fexp;
	if (!file.Open(fullpath,CFile::modeRead|CFile::osSequentialScan|CFile::typeBinary, &fexp)){
		if (fexp.m_cause != CFileException::fileNotFound){
			CString strError(_T("Failed to load ") KNOWN_MET_FILENAME _T(" file"));
			TCHAR szError[MAX_CFEXP_ERRORMSG];
			if (fexp.GetErrorMessage(szError, ELEMENT_COUNT(szError))){
				strError += _T(" - ");
				strError += szError;
			}
			theApp.emuledlg->AddLogLine(true, strError);
		}
		return false;
	}
	setvbuf(file.m_pStream, NULL, _IOFBF, 16384);

	CSingleLock sLock(&list_mut); // declare at outer scope to always have the mutex automatically unlocked..
	try {
		uint8 header;
		file.Read(&header,1);
		if (header != MET_HEADER){
			file.Close();
			return false;
		}
		
		uint32 RecordsNumber;
		file.Read(&RecordsNumber,4);
		sLock.Lock();
		for (uint32 i = 0; i < RecordsNumber; i++) {
			CKnownFile* pRecord =  new CKnownFile();
			pRecord->LoadFromFile(&file);
			Add(pRecord);
		}
		sLock.Unlock();
		file.Close();
	}
	catch(CFileException* error){
		OUTPUT_DEBUG_TRACE();
		if (error->m_cause == CFileException::endOfFile)
			theApp.emuledlg->AddLogLine(true,GetResString(IDS_ERR_SERVERMET_BAD));
		else{
			char buffer[MAX_CFEXP_ERRORMSG];
			error->GetErrorMessage(buffer,MAX_CFEXP_ERRORMSG);
			theApp.emuledlg->AddLogLine(true,GetResString(IDS_ERR_SERVERMET_UNKNOWN),buffer);
		}
		error->Delete();
		return false;
	}

	return true;
}

void CKnownFileList::Save()
{
	CString fullpath(appdir);
	fullpath += KNOWN_MET_FILENAME;
	CSafeBufferedFile file;
	CFileException fexp;
	if (!file.Open(fullpath, CFile::modeWrite|CFile::modeCreate|CFile::typeBinary, &fexp)){
		CString strError(_T("Failed to save ") KNOWN_MET_FILENAME _T(" file"));
		TCHAR szError[MAX_CFEXP_ERRORMSG];
		if (fexp.GetErrorMessage(szError, ELEMENT_COUNT(szError))){
			strError += _T(" - ");
			strError += szError;
		}
		theApp.emuledlg->AddLogLine(true, strError);
		return;
	}
	setvbuf(file.m_pStream, NULL, _IOFBF, 16384);

	try{
		uint8 ucHeader = MET_HEADER;
		file.Write(&ucHeader, 1);

		uint32 RecordsNumber = GetCount();
		file.Write(&RecordsNumber, 4);
		for (uint32 i = 0; i < RecordsNumber; i++)
			ElementAt(i)->WriteToFile(&file);
		file.Close();
	}
	catch(CFileException* error){
		CString strError(_T("Failed to save ") KNOWN_MET_FILENAME _T(" file"));
		TCHAR szError[MAX_CFEXP_ERRORMSG];
		if (error->GetErrorMessage(szError, ELEMENT_COUNT(szError))){
			strError += _T(" - ");
			strError += szError;
		}
		theApp.emuledlg->AddLogLine(true, strError);
		error->Delete();
	}
}

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();
}

CKnownFile* CKnownFileList::FindKnownFileByID(uchar* hash){
	for (int i = 0; i < GetCount(); i++){
		CKnownFile* pCurKnownFile = ElementAt(i);
		if (!md4cmp(pCurKnownFile->GetFileHash(), hash))
			return pCurKnownFile;
	}
	return NULL;
}
