// 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 <cmath>
#include <ctime>
#include <sys/types.h>		//
#include <sys/stat.h>		// These three are needed for open(2)
#include <fcntl.h>		//
#include <unistd.h>		// Needed for close(2)
#include <wx/utils.h>
#include <wx/intl.h>		// Needed for _
#include "ClientCredits.h"	// Interface declarations
#include "GetTickCount.h"	// Needed for GetTickCount
#include "opcodes.h"		// Needed for CREDITFILE_VERSION
#include "amuleDlg.h"		// Needed for CamuleDlg
#include "CamuleAppBase.h"	// Needed for theApp
#include "Preferences.h"	// Needed for CPreferences
#include "SafeFile.h"		// Needed for CSafeFile
#include "otherfunctions.h"	// Needed for GetTickCount

CClientCredits::CClientCredits(CreditStruct* in_credits)
{
	m_pCredits = in_credits;
}

CClientCredits::CClientCredits(uchar* key)
{
	m_pCredits = new CreditStruct;
	memset(m_pCredits,0,sizeof(CreditStruct));
	memcpy(m_pCredits->abyKey,key,16);
}

CClientCredits::~CClientCredits()
{
	delete m_pCredits;
}

float CClientCredits::GetScoreRatio()
{
	if(!m_pCredits) {
		return 1;
	}
	if (m_pCredits->nDownloaded < 1000000) {
		return 1;
	}
	float result = 0;
	if (!m_pCredits->nUploaded) {
		result = 10;
	} else {
		result = (float)(m_pCredits->nDownloaded*2)/m_pCredits->nUploaded;
	}
	float result2 = 0;
	result2 = (float)(m_pCredits->nDownloaded/1048576);
	result2 += 2;
	result2 = (double)sqrt((double)result2);
	if (result > result2) {
		result = result2;
	}
	if (result < 1) {
		return 1;
	} else if (result > 10) {
		return 10;
	}
	return result;
}


CClientCreditsList::CClientCreditsList(CPreferences* in_prefs)
{
	m_pAppPrefs = in_prefs;
	m_nLastSaved = ::GetTickCount();
	LoadList();
}

CClientCreditsList::~CClientCreditsList()
{
	SaveList();
	CClientCredits* cur_credit;
	POSITION pos = m_mapClients.GetStartPosition();
	while (pos){
		m_mapClients.GetNextAssoc(pos, cur_credit);
		delete cur_credit;
	}
	m_mapClients.RemoveAll();
}

void CClientCreditsList::LoadList()
{
	CSafeFile file;
	wxString strFileName = m_pAppPrefs->GetAppDir() + wxString("clients.met");
	if (!::wxFileExists(strFileName)) {
		theApp.amuledlg->AddLogLine(true, CString(_("Failed to load creditfile")));
		return;
	}
	uint8 version;
	file.Open(strFileName);
	file.Read(&version, 1);
	if (version != CREDITFILE_VERSION) {
		theApp.amuledlg->AddLogLine(false, CString(_("Creditfile is out of date and will be replaced")));
		file.Close();
		return;
	}
	// everything is ok, lets see if the backup exist...
	CString strBakFileName;
	strBakFileName.Format("%sclients.met.BAK", m_pAppPrefs->GetAppDir());
	
	bool bCreateBackup = TRUE;
	//HANDLE hBakFile = ::CreateFile(strBakFileName, GENERIC_READ, FILE_SHARE_READ, NULL,
	int hBakFile=open(strBakFileName.GetData(),O_RDONLY);
	//OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	if (hBakFile != (-1)) {
		// Ok, the backup exist, get the size
		// dwBakFileSize = ::GetFileSize(hBakFile, NULL); //debug
		struct stat sbuf;
		fstat(hBakFile,&sbuf);
		if (sbuf.st_size > (DWORD)file.Length()) {
			// the size of the backup was larger then the org. file, something is wrong here, don't overwrite old backup..
			bCreateBackup = FALSE;
		}
		// else: backup is smaller or the same size as org. file, proceed with copying of file
		close(hBakFile);
	}
	//else: the backup doesn't exist, create it
	if (bCreateBackup) {
		file.Close(); // close the file before copying
		//bool bResult = ::CopyFile(strFileName, strBakFileName, FALSE);
		// safe? you bet it is
		if (!wxCopyFile(strFileName,strBakFileName)) {
			printf("info: Could not create backup file '%s'\n", strFileName.c_str());
		}
		//DWORD dwError;
		//if (!bResult)
		//{
		//	dwError = ::GetLastError(); // debug
		//	theApp.amuledlg->AddLogLine(false, CString(_("Failed to make backup of creditfile")));
		//}
		// reopen file
		if (!file.Open(strFileName, CFile::read)) {
			theApp.amuledlg->AddLogLine(true, CString(_("Failed to load creditfile")));
			return;
		}
		// file.Seek(1); //, CFile::begin); // set filepointer
		file.Seek(1);
	}
	uint32 count;
	file.Read(&count, 4);
	m_mapClients.InitHashTable(count+5000); // TODO: should be prime number... and 20% larger
	for (uint32 i = 0; i != count; i++) {
		CreditStruct* newcstruct = new CreditStruct;
		memset(newcstruct, 0, sizeof(CreditStruct));
		file.Read(newcstruct, sizeof(CreditStruct));
		CClientCredits* newcredits = new CClientCredits(newcstruct);
		m_mapClients.SetAt(CCKey(newcredits->GetKey()), newcredits);
		if(!(i%165)) {
			// do not hog the UI
			theApp.Yield();
		}
	}
	file.Close();
	theApp.amuledlg->AddLogLine(false, CString(_("Creditfile loaded, %u clients are known")), count);
}

void CClientCreditsList::SaveList()
{
	uint32 count = m_mapClients.GetCount();
	BYTE* pBuffer = new BYTE[count*sizeof(CreditStruct)];
	wxString name = m_pAppPrefs->GetAppDir() + wxString("clients.met");
	CFile file;

	if (!file.Create(name,TRUE)) //Open(name, CFile::modeWrite|CFile::modeCreate))
	{
		theApp.amuledlg->AddLogLine(true, CString(_("Failed to save creditfile")));
		return;
	}

	CClientCredits* cur_credit;
	POSITION pos = m_mapClients.GetStartPosition();
	count = 0;
	while (pos)
	{
		m_mapClients.GetNextAssoc(pos, cur_credit);
		if (cur_credit->GetDataStruct()->nUploaded + cur_credit->GetDataStruct()->nDownloaded > 0)
		{
			memcpy(pBuffer+(count*sizeof(CreditStruct)), cur_credit->GetDataStruct(), sizeof(CreditStruct));
			count++; 
		}
	}

	uint8 version = CREDITFILE_VERSION;
	file.Write(&version, 1);
	file.Write(&count, 4);
	file.Write(pBuffer, count*sizeof(CreditStruct));
	file.Close();

	delete[] pBuffer;
	m_nLastSaved = ::GetTickCount();
}

CClientCredits* CClientCreditsList::GetCredit(uchar* key)
{
	CClientCredits* result;
	CCKey tkey(key);
	if (!m_mapClients.Lookup(tkey, result)){
		result = new CClientCredits(key);
		m_mapClients.SetAt(CCKey(result->GetKey()), result);
	}
	result->SetLastSeen();
	return result;
}

void CClientCreditsList::Process()
{
	if (::GetTickCount() - m_nLastSaved > 70000)
		SaveList();
}

#if 0
float CClientCredits::GetScoreRatio()
{
	if (credits->downloaded < 1000000) {
		return 1;
	}
	float result = 0;
	if (!credits->uploaded) {
		result = 10;
	} else {
		result = (float)(credits->downloaded*2)/credits->uploaded;
	}
	float result2 = 0;
	result2 = (float)(credits->downloaded/1048576);
	result2 += 2;
	result2 = (double)sqrt((double)result2);
	if (result > result2) {
		result = result2;
	}
	if (result < 1) {
		return 1;
	} else if (result > 10) {
		return 10;
	}
	return result;
}


CClientCreditsList::CClientCreditsList(CPreferences* in_prefs)
{
	app_prefs = in_prefs;
	lastsaved = ::GetTickCount();
	LoadList();
}

CClientCreditsList::~CClientCreditsList()
{
	SaveList();
	CClientCredits* cur_credit;
	POSITION pos = client_map.GetStartPosition();
	while (pos) {
		client_map.GetNextAssoc(pos,cur_credit);
		delete cur_credit;
	}
	client_map.RemoveAll();
}

void CClientCreditsList::LoadList()
{
	CSafeFile file;
	wxString name = app_prefs->GetAppDir() + wxString("clients.met");
	if (!wxFileExists(name)) {
		theApp.amuledlg->AddLogLine(false,CString(_("Failed to load creditfile")));
		return;
	}
	uint8 version;
	file.Read(&version,1);
	if (version != CREDITFILE_VERSION) {
		theApp.amuledlg->AddLogLine(false,CString(_("Creditfile is out of date and will be replaced")));
		file.Close();
		return;
	}
	uint32 count;
	file.Read(&count,4);
	client_map.InitHashTable(count+5000);
	for (uint32 i = 0; i != count;i++) {
		Credit_Struct* newcstruct = new Credit_Struct;
		memset(newcstruct,0,sizeof(Credit_Struct));
		file.Read(newcstruct,sizeof(Credit_Struct));
		CClientCredits* newcredits = new CClientCredits(newcstruct);
		client_map.SetAt(CCKey(newcredits->GetKey()),(CClientCredits*)newcredits);
	}
	file.Close();
	theApp.amuledlg->AddLogLine(false,CString(_("Creditfile loaded")),count);
}

void CClientCreditsList::SaveList()
{
	wxString name = app_prefs->GetAppDir() + wxString("clients.met");
	CFile diskfile;
	CMemFile file(0, 0, 16384);
	//if (!diskfile.Open(name,CFile::modeWrite|CFile::modeCreate)) {
	if(!diskfile.Create(name,TRUE)) {
		theApp.amuledlg->AddLogLine(true,CString(_("Failed to save creditfile")));
		return;
	}
	uint8 version = CREDITFILE_VERSION;
	file.Write(&version,1);
	uint32 count = client_map.GetCount();
	file.Write(&count,4);
	CClientCredits* cur_credit;
	POSITION pos = client_map.GetStartPosition();
	count = 0;
	while (pos) {
		client_map.GetNextAssoc(pos,cur_credit);
		if (cur_credit->GetDataStruct()->uploaded + cur_credit->GetDataStruct()->downloaded > 0) {
			file.Write(cur_credit->GetDataStruct(),sizeof(Credit_Struct));
			count++;
		}
	}
	file.Seek(1);//,FILE_BEGIN);
	file.Write(&count,4);
	int size = file.GetLength();
	BYTE* tmp = file.Detach();
	diskfile.Write(tmp,size);
	free(tmp);
	diskfile.Close();
	lastsaved = ::GetTickCount();
}

CClientCredits* CClientCreditsList::GetCredit(uchar* key)
{
	CClientCredits* result;
	CCKey tkey(key);
	if (!client_map.Lookup(tkey,result)) {
		result = new CClientCredits(key);
		client_map.SetAt(CCKey(result->GetKey()),result);
	}
	result->SetLastSeen();
	return result;
}

void CClientCreditsList::Process()
{
	if (::GetTickCount() - lastsaved > 70000) {
		SaveList();
	}
}

#endif
