/*
 * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved.
 * 
 * The contents of this file constitute Original Code as defined in and are
 * subject to the Apple Public Source License Version 1.2 (the 'License').
 * You may not use this file except in compliance with the License. Please obtain
 * a copy of the License at http://www.apple.com/publicsource and read it before
 * using this file.
 * 
 * This Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
 * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
 * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
 * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
 * specific language governing rights and limitations under the License.
 */


//
// MultiDLDb implementation.
//

#include <Security/multidldb.h>
#include <Security/securestorage.h>
#include <Security/cssmerrno.h>



namespace Security
{

using namespace CssmClient;

namespace CssmClient
{

//
// MultiDLDbDbCursorImpl declaration
//
class MultiDLDbDbCursorImpl : public DbCursorImpl
{
public:
	MultiDLDbDbCursorImpl(const MultiDLDb &parent, const CSSM_QUERY &query, CssmAllocator &allocator);
	MultiDLDbDbCursorImpl(const MultiDLDb &parent, uint32 capacity, CssmAllocator &allocator);
	virtual ~MultiDLDbDbCursorImpl();

	bool next(DbAttributes *attributes, ::CssmDataContainer *data, DbUniqueRecord &uniqueId);
private:
	MultiDLDb multiDLDb() { return parent<MultiDLDb>(); }
	void activate();
	void deactivate();

	MultiDLDbImpl::ListRef mListRef;
	MultiDLDbImpl::List::const_iterator mNext;
	MultiDLDbImpl::List::const_iterator mEnd;
	DbCursor mCursor;
};

} // end namespace CssmClient

} // end namespace Security

//
// MultiDLDbImpl
//
MultiDLDbImpl::MultiDLDbImpl(const vector<DLDbIdentifier> &list, bool useSecureStorage, const Cssm &cssm)
: ObjectImpl(cssm), mListRef(list), mUseSecureStorage(useSecureStorage)
{
}

MultiDLDbImpl::MultiDLDbImpl(const vector<DLDbIdentifier> &list, bool useSecureStorage)
: ObjectImpl(Cssm::standard()), mListRef(list), mUseSecureStorage(useSecureStorage)
{
}

MultiDLDbImpl::~MultiDLDbImpl()
{
	deactivate();
}

Db
MultiDLDbImpl::database(const DLDbIdentifier &dlDbIdentifier)
{
	StLock<Mutex> _(mLock);
	DbMap::const_iterator it = mDbMap.find(dlDbIdentifier);
	if (it != mDbMap.end())
		return it->second;

	Module module(dlDbIdentifier.ssuid().guid(), cssm());
	DL dl;
	if (dlDbIdentifier.ssuid().subserviceType() & CSSM_SERVICE_CSP)
	{
		if (mUseSecureStorage)
			dl = SSCSPDL(module);
		else
			dl = CSPDL(module);
	}
	else
		dl = DL(module);

	dl->subserviceId(dlDbIdentifier.ssuid().subserviceId());
	dl->version(dlDbIdentifier.ssuid().version());
	Db db(dl, dlDbIdentifier.dbName());
	if (find(mListRef->begin(), mListRef->end(), dlDbIdentifier) != mListRef->end())
		mDbMap.insert(DbMap::value_type(dlDbIdentifier, db));

	return db;
}

void
MultiDLDbImpl::list(const vector<DLDbIdentifier> &list)
{
	StLock<Mutex> _(mLock);
	set<DLDbIdentifier> oldList(mListRef->begin(), mListRef->end());
	mListRef = ListRef(list);
	set<DLDbIdentifier> newList(mListRef->begin(), mListRef->end());
	vector<DLDbIdentifier> obsolete;
	back_insert_iterator<vector<DLDbIdentifier> > ii(obsolete);
	// Remove all db's from the map that were in oldList but are not in mListRef.
	set_difference(oldList.begin(), oldList.end(), newList.begin(), newList.end(), ii);
	for (vector<DLDbIdentifier>::const_iterator it = obsolete.begin(); it != obsolete.end(); ++it)
		mDbMap.erase(*it);
}

DbCursorImpl *
MultiDLDbImpl::newDbCursor(const CSSM_QUERY &query, CssmAllocator &allocator)
{
	return new MultiDLDbDbCursorImpl(MultiDLDb(this), query, allocator);
}

DbCursorImpl *
MultiDLDbImpl::newDbCursor(uint32 capacity, CssmAllocator &allocator)
{
	return new MultiDLDbDbCursorImpl(MultiDLDb(this), capacity, allocator);
}

void
MultiDLDbImpl::activate()
{
}

void
MultiDLDbImpl::deactivate()
{
	StLock<Mutex> _(mLock);
	mDbMap.erase(mDbMap.begin(), mDbMap.end());
}


//
// MultiDLDbDbCursorImpl
//
MultiDLDbDbCursorImpl::MultiDLDbDbCursorImpl(const MultiDLDb &parent,
											 const CSSM_QUERY &query, CssmAllocator &allocator)
: DbCursorImpl(parent, query, allocator)
{
}

MultiDLDbDbCursorImpl::MultiDLDbDbCursorImpl(const MultiDLDb &parent,
											 uint32 capacity, CssmAllocator &allocator)
: DbCursorImpl(parent, capacity, allocator)
{
}

MultiDLDbDbCursorImpl::~MultiDLDbDbCursorImpl()
{
	try
	{
		deactivate();
	}
	catch(...) {}
}

bool
MultiDLDbDbCursorImpl::next(DbAttributes *attributes, ::CssmDataContainer *data, DbUniqueRecord &uniqueId)
{
	activate();
	for (;;)
	{
		if (!mCursor)
		{
			if (mNext == mEnd)
			{
				// This is how it ends.
				deactivate();
				return false;
			}

			mCursor = DbCursor(multiDLDb()->database(*mNext++), *this);
		}

		try
		{
			if (mCursor->next(attributes, data, uniqueId))
				return true;
		}

		catch(const CssmCommonError &err)
		{
			OSStatus status = err.osStatus();
			if(status != CSSMERR_DL_DATASTORE_DOESNOT_EXIST)
				throw;
		}



		mCursor = DbCursor();
	}
}

void
MultiDLDbDbCursorImpl::activate()
{
	if (!mActive)
	{
		mListRef = multiDLDb()->listRef();
		mNext = mListRef->begin();
		mEnd = mListRef->end();
		mActive = true;
	}
}

void
MultiDLDbDbCursorImpl::deactivate()
{
	if (mActive)
	{
		mActive = false;
		mListRef = MultiDLDbImpl::ListRef();
		mNext = mEnd;
		mCursor = DbCursor();
	}
}

