//
// This file is part of the aMule Project.
//
// Copyright (c) 2008-2011 Dévai Tamás ( gonosztopi@amule.org )
// Copyright (c) 2004-2011 aMule Team ( admin@amule.org / http://www.amule.org )
//
// Any parts of this program derived from the xMule, lMule or eMule project,
// or contributed by third-party developers are copyrighted by their
// respective authors.
//
// 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
//

#include "KadFiles.h"
#include "Print.h"
#include "../../SafeFile.h"
#include "../../kademlia/kademlia/SearchManager.h"	// for CSearchManager::GetInvalidKeywordChars()
#include <wx/tokenzr.h>

void DecodePreferencesKadDat(const CFileDataIO& file)
{
	cout << "IP      : " << CKadIP(file.ReadUInt32()) << '\n';
	cout << "(unused): " << file.ReadUInt16() << '\n';
	cout << "ClientID: " << file.ReadUInt128() << '\n';
}

void DecodeLoadIndexDat(const CFileDataIO& file)
{
	cout << "Version   : " << file.ReadUInt32();
	cout << "\n(savetime): " << CTimeT(file.ReadUInt32());
	uint32_t numLoad = file.ReadUInt32();
	cout << "\nnumLoad   : " << numLoad << '\n';
	for (uint32_t i = 0; i < numLoad; i++) {
		cout << "\t{ " << file.ReadUInt128();
		cout << ", " << CTimeT(file.ReadUInt32()) << " }\n";
	}
}

// from Kademlia.cpp
#include "../../CryptoPP_Inc.h"
void KadGetKeywordHash(const wxString& rstrKeyword, Kademlia::CUInt128* pKadID)
{
	uint8_t Output[16];

	CryptoPP::Weak::MD4 md4_hasher;

	// This should be safe - we assume rstrKeyword is ANSI anyway.
	Unicode2CharBuf ansi_buffer(unicode2UTF8(rstrKeyword));

	md4_hasher.CalculateDigest(Output, (const uint8_t *) (const char *) ansi_buffer, strlen(ansi_buffer));

	pKadID->SetValueBE(Output);
}

// code from CSearchManager::GetWords(const wxString& str, WordList *words)
bool IdentifyKeyword(const Kademlia::CUInt128& keyID, const wxString& str, wxString& keyword)
{
	wxStringTokenizer tkz(str, Kademlia::CSearchManager::GetInvalidKeywordChars());
	while (tkz.HasMoreTokens()) {
		wxString current_word = tkz.GetNextToken();

		if (current_word.Length() > 2) {
			current_word.MakeLower();
			Kademlia::CUInt128 currentID;
			KadGetKeywordHash(current_word, &currentID);
			if (currentID == keyID) {
				keyword = current_word;
				return true;
			}
		}
	}
	return false;
}

void DecodeKeyIndexDat(const CFileDataIO& file)
{
	uint32_t version;
	uint32_t numKeys;
	uint32_t numSource;
	uint32_t numName;
	uint8_t tagCount;

	cout << "Version : " << (version = file.ReadUInt32());
	cout << "\nSaveTime: " << CTimeT(file.ReadUInt32());
	cout << "\nID      : " << file.ReadUInt128();
	cout << "\nnumKeys : " << (numKeys = file.ReadUInt32()) << '\n';
	for (uint32_t ik = 0; ik < numKeys; ik++) {
		Kademlia::CUInt128 keyID = file.ReadUInt128();
		bool identified = false;
		cout << "\tKeyID    : " << keyID;
		cout << "\n\tnumSource: " << (numSource = file.ReadUInt32()) << '\n';
		for (uint32_t is = 0; is < numSource; is++) {
			cout << "\t\tSourceID: " << file.ReadUInt128();
			cout << "\n\t\tnumName : " << (numName = file.ReadUInt32()) << '\n';
			for (uint32_t iN = 0; iN < numName; iN++) {
				cout << "\t\t\tLifeTime : " <<  CTimeT(file.ReadUInt32()) << '\n';
				if (version >= 3) {
					uint32_t count;
					cout << "\t\t\tnameCount: " << (count = file.ReadUInt32()) << '\n';
					for (uint32_t i = 0; i < count; i++) {
						wxString name = file.ReadString(true, 2);
						cout << "\t\t\t\t{ " << MakePrintableString(name);
						cout << ", " << file.ReadUInt32() << " }\n";
						wxString keyword;
						if (!identified && IdentifyKeyword(keyID, name, keyword)) {
							cout << "\tKeyword: " << MakePrintableString(keyword) << '\n';
							identified = true;
						}
					}
					cout << "\t\t\tipCount  : " << (count = file.ReadUInt32()) << '\n';
					for (uint32_t i = 0; i < count; i++) {
						cout << "\t\t\t\t{ " << CKadIP(file.ReadUInt32());
						cout << ", " << CTimeT(file.ReadUInt32()) << " }\n";
					}
				}
				cout << "\t\t\ttagCount : " << (uint32)(tagCount = file.ReadUInt8()) << '\n';
				for (uint32_t it = 0; it < tagCount; it++) {
					CTag *tag = file.ReadTag();
					cout << "\t\t\t\t" << *tag << '\n';
					delete tag;
				}
			}
		}
	}
}

void DecodeSourceIndexDat(const CFileDataIO& file)
{
	uint32_t numKeys;
	uint32_t numSource;
	uint32_t numName;
	uint8_t tagCount;

	cout << "Version : " << file.ReadUInt32();
	cout << "\nSaveTime: " << CTimeT(file.ReadUInt32());
	cout << "\nnumKeys : " << (numKeys = file.ReadUInt32()) << '\n';
	for (uint32_t ik = 0; ik < numKeys; ik++) {
		cout << "\tKeyID    : " << file.ReadUInt128();
		cout << "\n\tnumSource: " << (numSource = file.ReadUInt32()) << '\n';
		for (uint32_t is = 0; is < numSource; is++) {
			cout << "\t\tSourceID: " << file.ReadUInt128();
			cout << "\n\t\tnumName : " << (numName = file.ReadUInt32()) << '\n';
			for (uint32_t iN = 0; iN < numName; iN++) {
				cout << "\t\t\tLifeTime: " << CTimeT(file.ReadUInt32());
				cout << "\n\t\t\ttagCount: " << static_cast<unsigned int>(tagCount = file.ReadUInt8()) << '\n';
				for (uint32_t it = 0; it < tagCount; it++) {
					CTag *tag = file.ReadTag();
					cout << "\t\t\t\t" << *tag << '\n';
					delete tag;
				}
			}
		}
	}
}

void DecodeNodesDat(const CFileDataIO& file)
{
	uint32_t numContacts = file.ReadUInt32();
	uint32_t fileVersion = 0;

	cout << "NumContacts #1  : " << numContacts << '\n';
	if (numContacts == 0) {
		cout << "FileVersion     : " << (fileVersion = file.ReadUInt32()) << '\n';
		if (fileVersion == 3) {
			cout << "BootstrapEdition: " << file.ReadUInt32() << '\n';
		}
		if (fileVersion >= 1 && fileVersion <= 3) {
			cout << "NumContacts #2  : " << (numContacts = file.ReadUInt32()) << '\n';
		}
	}
	for (uint32_t i = 0; i < numContacts; i++) {
		cout << wxString::Format(wxT("#%u\tID       : "), i) << file.ReadUInt128();
		cout << "\n\tIP       : " << CKadIP(file.ReadUInt32());
		cout << "\n\tUDP Port : " << file.ReadUInt16();
		cout << "\n\tTCP Port : " << file.ReadUInt16();
		if (fileVersion >= 1) {
			cout << "\n\tVersion  : ";
		} else {
			cout << "\n\tType     : ";
		}
		cout << (unsigned)file.ReadUInt8();
		if (fileVersion >= 2) {
			cout << "\n\tUDP Key  : { " << hex(file.ReadUInt32());
			cout << ", " << CKadIP(file.ReadUInt32());
			cout << " }\n\tVerified : " << (file.ReadUInt8() != 0 ? "true" : "false");
		}
		cout << '\n';
	}
}
