/*
 * Copyright 2011, Haiku, Inc. All Rights Reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *		Oliver Tappe <zooey@hirschkaefer.de>
 *		Ingo Weinhold <ingo_weinhold@gmx.de>
 */
 
 
#include <package/PackageInfoSet.h>
 
#include <new>
 
#include <Referenceable.h>
 
#include <AutoDeleter.h>
 
#include <util/OpenHashTable.h>
 
#include <package/PackageInfo.h>
 
 
namespace BPackageKit {
 
 
// #pragma mark - PackageInfo
 
 
struct BPackageInfoSet::PackageInfo : public BPackageInfo {
	PackageInfo*	hashNext;
	PackageInfo*	listNext;
 
	PackageInfo(const BPackageInfo& other)
		:
		BPackageInfo(other),
		listNext(NULL)
	{
	}
 
	void DeleteList()
	{
		PackageInfo* info = this;
		while (info != NULL) {
			PackageInfo* next = info->listNext;
			delete info;
			info = next;
		}
	}
};
 
 
// #pragma mark - PackageInfoHashDefinition
 
 
struct BPackageInfoSet::PackageInfoHashDefinition {
	typedef const char*		KeyType;
	typedef	PackageInfo		ValueType;
 
	size_t HashKey(const char* key) const
	{
		return BString::HashValue(key);
	}
 
	size_t Hash(const PackageInfo* value) const
	{
		return value->Name().HashValue();
	}
 
	bool Compare(const char* key, const PackageInfo* value) const
	{
		return value->Name() == key;
	}
 
	PackageInfo*& GetLink(PackageInfo* value) const
	{
		return value->hashNext;
	}
};
 
 
// #pragma mark - PackageMap
 
 
struct BPackageInfoSet::PackageMap : public BReferenceable,
	public BOpenHashTable<PackageInfoHashDefinition> {
 
	PackageMap()
		:
		fCount(0)
	{
	}
 
	~PackageMap()
	{
		DeleteAllPackageInfos();
	}
 
	static PackageMap* Create()
	{
		PackageMap* map = new(std::nothrow) PackageMap;
		if (map == NULL || map->Init() != B_OK) {
			delete map;
			return NULL;
		}
 
		return map;
	}
 
	PackageMap* Clone() const
	{
		PackageMap* newMap = Create();
		if (newMap == NULL)
			return NULL;
		ObjectDeleter<PackageMap> newMapDeleter(newMap);
 
		for (BPackageInfoSet::Iterator it(this); it.HasNext();) {
			const BPackageInfo* info = it.Next();
			if (newMap->AddNewPackageInfo(*info) != B_OK)
				return NULL;
		}
 
		return newMapDeleter.Detach();
	}
 
	void AddPackageInfo(PackageInfo* info)
	{
		if (PackageInfo* oldInfo = Lookup(info->Name())) {
			info->listNext = oldInfo->listNext;
			oldInfo->listNext = info;
		} else
			Insert(info);
 
		fCount++;
	}
 
	status_t AddNewPackageInfo(const BPackageInfo& oldInfo)
	{
		PackageInfo* info = new(std::nothrow) PackageInfo(oldInfo);
		if (info == NULL)
			return B_NO_MEMORY;
		ObjectDeleter<PackageInfo> infoDeleter(info);
 
		status_t error = info->InitCheck();
		if (error != B_OK)
			return error;
 
		AddPackageInfo(infoDeleter.Detach());
 
		return B_OK;
	}
 
	void DeleteAllPackageInfos()
	{
		PackageInfo* info = Clear(true);
		while (info != NULL) {
			PackageInfo* next = info->hashNext;
			info->DeleteList();
			info = next;
		}
	}
 
	uint32 CountPackageInfos() const
	{
		return fCount;
	}
 
private:
	uint32	fCount;
};
 
 
// #pragma mark - Iterator
 
 
BPackageInfoSet::Iterator::Iterator(const PackageMap* map)
	:
	fMap(map),
	fNextInfo(map != NULL ? map->GetIterator().Next() : NULL)
{
}
 
 
bool
BPackageInfoSet::Iterator::HasNext() const
{
	return fNextInfo != NULL;
}
 
 
const BPackageInfo*
BPackageInfoSet::Iterator::Next()
{
	BPackageInfo* result = fNextInfo;
 
	if (fNextInfo != NULL) {
		if (fNextInfo->listNext != NULL) {
			// get next in list
			fNextInfo = fNextInfo->listNext;
		} else {
			// get next in hash table
			PackageMap::Iterator iterator
				= fMap->GetIterator(fNextInfo->Name());
			iterator.Next();
			fNextInfo = iterator.Next();
		}
	}
 
	return result;
}
 
 
// #pragma mark - BPackageInfoSet
 
 
BPackageInfoSet::BPackageInfoSet()
	:
	fPackageMap(NULL)
{
}
 
 
BPackageInfoSet::~BPackageInfoSet()
{
	if (fPackageMap != NULL)
		fPackageMap->ReleaseReference();
}
 
 
BPackageInfoSet::BPackageInfoSet(const BPackageInfoSet& other)
	:
	fPackageMap(other.fPackageMap)
{
	if (fPackageMap != NULL)
		fPackageMap->AcquireReference();
}
 
 
status_t
BPackageInfoSet::AddInfo(const BPackageInfo& info)
{
	if (!_CopyOnWrite())
		return B_NO_MEMORY;
 
	return fPackageMap->AddNewPackageInfo(info);
}
 
 
void
BPackageInfoSet::MakeEmpty()
{
	if (fPackageMap == NULL || fPackageMap->CountPackageInfos() == 0)
		return;
 
	// If our map is shared, just set it to NULL.
	if (fPackageMap->CountReferences() != 1) {
		fPackageMap->ReleaseReference();
		fPackageMap = NULL;
		return;
	}
 
	// Our map is not shared -- make it empty.
	fPackageMap->DeleteAllPackageInfos();
}
 
 
uint32
BPackageInfoSet::CountInfos() const
{
	if (fPackageMap == NULL)
		return 0;
 
	return fPackageMap->CountPackageInfos();
}
 
 
BPackageInfoSet::Iterator
BPackageInfoSet::GetIterator() const
{
	return Iterator(fPackageMap);
}
 
 
BPackageInfoSet&
BPackageInfoSet::operator=(const BPackageInfoSet& other)
{
	if (other.fPackageMap == fPackageMap)
		return *this;
 
	if (fPackageMap != NULL)
		fPackageMap->ReleaseReference();
 
	fPackageMap = other.fPackageMap;
 
	if (fPackageMap != NULL)
		fPackageMap->AcquireReference();
 
	return *this;
}
 
 
bool
BPackageInfoSet::_CopyOnWrite()
{
	if (fPackageMap == NULL) {
		fPackageMap = PackageMap::Create();
		return fPackageMap != NULL;
	}
 
	if (fPackageMap->CountReferences() == 1)
		return true;
 
	PackageMap* newMap = fPackageMap->Clone();
	if (newMap == NULL)
		return false;
 
	fPackageMap->ReleaseReference();
	fPackageMap = newMap;
	return true;
}
 
 
}	// namespace BPackageKit

V730 Not all members of a class are initialized inside the constructor. Consider inspecting: hashNext.