/*
 * 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.
 */


//
// cssmclient - common client interface to CSSM and MDS
//
#ifndef _H_CDSA_CLIENT_CSSMCLIENT
#define _H_CDSA_CLIENT_CSSMCLIENT  1

#include <Security/utilities.h>
#include <Security/threading.h>
#include <Security/globalizer.h>
#include <Security/cssmalloc.h>
#include <Security/refcount.h>
#include <map>
#include <stdio.h>	// debug

namespace Security
{

namespace CssmClient
{

//
// Forward declarations
//
class Cssm;
class Module;
class Attachment;


//
// An mixin for objects that have (store) GUIDs.
// The GUID value is meant to be set-once constant, and can be locked handled accordingly.
//
class HasGuid {
public:
	HasGuid(const Guid &guid) { mGuid = guid; }
	HasGuid() { }
	
	const Guid &guid() const { return mGuid; }

protected:
	void setGuid(const Guid &guid) { mGuid = guid; }
	
private:
	Guid mGuid;
};


//
// Exceptions are based on the CssmError utility class. We add our own class of client-side exceptions.
//
class Error : public CssmError {
public:
	Error(int err) : CssmError(err) { }
	CSSM_RETURN cssmError() const;
	virtual const char *what () const throw();
	
	enum {
		objectBusy = -1,
	};
};


//
// The CssmObject abstract class models features common to different Cssm objects.
// It handles a tree hierarchy of objects (parent/children) safely.
//
class Object;

class ObjectImpl : virtual public RefCount
{
public:
	explicit ObjectImpl(); // Constructor for Impl objects without a parent.
	explicit ObjectImpl(const Object &parent);
	virtual ~ObjectImpl();

	bool isActive() const { return mActive; }

	virtual CssmAllocator &allocator() const;
	virtual void allocator(CssmAllocator &alloc);

	// Pointer comparison by default.  Subclasses may override.
	virtual bool operator <(const ObjectImpl &other) const;
	virtual bool operator ==(const ObjectImpl &other) const;

	static void check(CSSM_RETURN status);

protected:
	bool mActive;					// loaded, attached, etc.
	mutable CssmAllocator *mAllocator; // allocator hierarchy (NULL => TBD)
	
	template <class Obj> Obj parent() const
	{ assert(mParent); return Obj(static_cast<typename Obj::Impl *>(&(*mParent))); }

	void addChild();
	void removeChild();
	bool isIdle() const { return mChildCount == 0; }

	// {de,}activate() assume you have locked *this
	virtual void activate() = 0;
	virtual void deactivate() = 0;

private:
	RefPointer<ObjectImpl> mParent;		// parent object
	AtomicCounter<uint32> mChildCount;
};


class Object
{
	friend class ObjectImpl;
public:
	typedef ObjectImpl Impl;
	explicit Object(Impl *impl) : mImpl(impl) {}

protected:
	// @@@ CSPDL subclass breaks if the is a static_cast
	template <class _Impl> _Impl &impl() const
	{ return dynamic_cast<_Impl &>(*mImpl); }

public:
	Impl *operator ->() const { return &(*mImpl); }
	Impl &operator *() const { return *mImpl; }

	// @@@ Why is this needed.  DbCursor which inheirits from Object wants to call this.
	template <class _Impl> _Impl &checkedImpl() const
	{ return dynamic_cast<_Impl &>(*mImpl); }

	bool operator !() const { return !mImpl; }
	operator bool() const { return mImpl; }
    
    bool isActive() const				{ return mImpl && mImpl->isActive(); }
    CssmAllocator &allocator() const	{ return mImpl->allocator(); }

	bool operator <(const Object &other) const
	{ return mImpl && other.mImpl ? *mImpl < *other.mImpl : mImpl < other.mImpl; }
	bool operator ==(const Object &other) const
	{ return mImpl && other.mImpl ? *mImpl == *other.mImpl : mImpl == other.mImpl; }

private:
	RefPointer<Impl> mImpl;
};


//
// A CSSM loadable module.
// You rarely directly interact with these objects, but if you need to,
// here they are.
//
class ModuleImpl : public ObjectImpl, public HasGuid
{
public:
	ModuleImpl(const Guid &guid);
	ModuleImpl(const Guid &guid, const Cssm &session);
	virtual ~ModuleImpl();
	
	void load() { activate(); }
	void unload() { deactivate(); }
	bool isLoaded() const { return isActive(); }
	
	Cssm session() const;

protected:
	void activate();
	void deactivate();
};

class Module : public Object
{
public:
	typedef ModuleImpl Impl;
	explicit Module(Impl *impl) : Object(impl) {}
	Module() : Object(NULL) {} // XXX This might break operator <
	Module(const Guid &guid) : Object(new Impl(guid)) {}
	Module(const Guid &guid, const Cssm &session) : Object(new Impl(guid, session)) {}

	Impl *operator ->() const { return &impl<Impl>(); }
	Impl &operator *() const { return impl<Impl>(); }
};


//
// An Attachment object. This is the base class of all typed attachment classes.
//
class AttachmentImpl : public ObjectImpl
{
public:
	AttachmentImpl(const Guid &guid, CSSM_SERVICE_TYPE subserviceType);
	AttachmentImpl(const Module &module, CSSM_SERVICE_TYPE subserviceType);
	//AttachmentImpl(... mds reference ...);
	virtual ~AttachmentImpl();

	// Virtual so that subclasses can return there true mask.
	virtual CSSM_SERVICE_MASK subserviceMask() const;

	CSSM_SERVICE_TYPE subserviceType() const { return mSubserviceType; }
	CSSM_VERSION version() const { return mVersion; }
	void version(const CSSM_VERSION &v) { mVersion = v; }
	uint32 subserviceId() const { return mSubserviceId; }
	virtual void subserviceId(uint32 id);
	CSSM_ATTACH_FLAGS flags() const { return mAttachFlags; }
	void flags(CSSM_ATTACH_FLAGS f) { mAttachFlags = f; }

	void attach() { activate(); }
	void detach() { deactivate(); }
	bool attached() const { return isActive(); }

	Module module() const;
	const Guid &guid() const { return module()->guid(); }
	CSSM_MODULE_HANDLE handle() { attach(); return mHandle; }

	CssmSubserviceUid subserviceUid() const;

protected:
	void activate();
	void deactivate();

private:
	void make(CSSM_SERVICE_TYPE subserviceType);	// common constructor

	CSSM_MODULE_HANDLE mHandle;

	CSSM_SERVICE_TYPE mSubserviceType;				// set by constructor
	CSSM_VERSION mVersion;
	uint32 mSubserviceId;
	CSSM_ATTACH_FLAGS mAttachFlags;

	CssmAllocatorMemoryFunctions mMemoryFunctions;	// set on attach()
};

class Attachment : public Object
{
public:
	typedef AttachmentImpl Impl;
	explicit Attachment(Impl *impl) : Object(impl) {}
	Attachment(const Guid &guid, CSSM_SERVICE_TYPE subserviceType)
	: Object(new Impl(guid, subserviceType)) {}
	Attachment(const Module &module, CSSM_SERVICE_TYPE subserviceType)
	: Object(new Impl(module, subserviceType)) {}
	//Attachment(... mds reference ...);

	Impl *operator ->() const { return &impl<Impl>(); }
	Impl &operator *() const { return impl<Impl>(); }
};


//
// A CSSM session object.
// You usually only have one per program, or library, or what-not.
//
class Cssm;

class CssmImpl : public ObjectImpl {
    class StandardCssm; friend class StandardCssm;
public:
	CssmImpl();
	virtual ~CssmImpl();

	void init() { activate(); }
	void terminate() { deactivate(); }

	CSSM_PRIVILEGE_SCOPE scope() const { return mScope; }
	void scope(CSSM_PRIVILEGE_SCOPE sc) { mScope = sc; }
	const Guid &callerGuid() const { return mCallerGuid; }
	void callerGuid(const CSSM_GUID &guid) { mCallerGuid = Guid::overlay(guid); }

	Module autoModule(const Guid &guid);
    
protected:
	explicit CssmImpl(bool);				// internal constructor

	void setup();							// constructor setup

	void activate();
	void deactivate();

private:
	// CSSM global configuration -- picked up on each Init
	CSSM_VERSION mVersion;
	CSSM_PRIVILEGE_SCOPE mScope;
	Guid mCallerGuid;

	// module repository: modules by guid (protected by self)
	typedef map<Guid, Module> ModuleMap;
	ModuleMap moduleMap;
	Mutex mapLock;

public:
	static Cssm standard();
	static void catchExit();

private:
	static void atExitHandler();
    
    class StandardCssm : public Mutex {
    public:
        StandardCssm() : mCssm(NULL) { }
        ~StandardCssm();
        void setCssm(CssmImpl *cssm);
        void unsetCssm(CssmImpl *cssm);
        CssmImpl *get();
        
    private:
        CssmImpl *mCssm;
    };
    static ModuleNexus<StandardCssm> mStandard;
};

class Cssm : public Object
{
public:
	typedef CssmImpl Impl;
	explicit Cssm(Impl *impl) : Object(impl) {}
	explicit Cssm() : Object(new Impl()) {}

	Impl *operator ->() const { return &impl<Impl>(); }
	Impl &operator *() const { return impl<Impl>(); }

	static Cssm standard() { return CssmImpl::standard(); }
};

} // end namespace CssmClient 

} // end namespace Security

#endif // _H_CDSA_CLIENT_CSSMCLIENT
