// Copyright (c) 2002-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of the License "Eclipse Public License v1.0"
// which accompanies this distribution, and is available
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
//
// Initial Contributors:
// Nokia Corporation - initial contribution.
//
// Contributors:
//
// Description:
//

#include "t_prop_ldd.h"
#include <kernel/kernel.h>
#include "nk_priv.h"

class DPropLDevice : public DLogicalDevice
	{
public:
	DPropLDevice();
	virtual TInt Install();
	virtual void GetCaps(TDes8& aDes) const;
	virtual TInt Create(DLogicalChannelBase*& aChannel);
	};

class DPropLChannel : public DLogicalChannelBase
	{
public:
	DPropLChannel();
	~DPropLChannel();

	virtual TInt DoCreate(TInt aUnit, const TDesC8* anInfo, const TVersion& aVer);
	virtual TInt Request(TInt aFunction, TAny* a1, TAny* a2);

private:
	TInt Basic(RPropChannel::TBasicInfo* aInfo);

	static void CompleteFn(TAny* aPtr, TInt aReason);
	TInt iReason;
	NFastSemaphore iSem;

	};

DECLARE_STANDARD_LDD()
//
// Create a new device
//
	{
	return new DPropLDevice;
	}

DPropLDevice::DPropLDevice()
//
// Constructor
//
	{
	//iUnitsMask=0;
	iVersion = TVersion(1,0,1);
	// iParseMask = 0;
	}

TInt DPropLDevice::Install()
//
// Install the device driver.
//
	{
	TInt r = SetName(&KPropLdName);
	return r;
	}

void DPropLDevice::GetCaps(TDes8&) const
//
// Return the Comm capabilities.
//
	{
	}

TInt DPropLDevice::Create(DLogicalChannelBase*& aChannel)
//
// Create a channel on the device.
//
	{
	aChannel = new DPropLChannel;
	return aChannel ? KErrNone : KErrNoMemory;
	}

DPropLChannel::DPropLChannel() 
	{
	NKern::FSSetOwner(&iSem, NKern::CurrentThread());
	}

TInt DPropLChannel::DoCreate(TInt /*aUnit*/, const TDesC8* /* aInfo*/ , const TVersion& aVer)
//
// Create the channel from the passed info.
//
	{
	if (!Kern::QueryVersionSupported(TVersion(1,0,1),aVer))
		return KErrNotSupported;
	return KErrNone;
	}

DPropLChannel::~DPropLChannel()
	{
	}


#define BASIC_ERROR(aRes, aCond) \
	{\
	if (!(aCond)) \
		{ \
		Kern::Printf("Test '" #aCond "' fails; r = %d;\n\tfile '" __FILE__ "'; line %d;\n", aRes, __LINE__); \
		prop.Close(); \
		return EFalse; \
		} \
	}

void DPropLChannel::CompleteFn(TAny* aPtr, TInt aReason)
	{ // static
	DPropLChannel* self = (DPropLChannel*) aPtr;
	self->iReason = aReason;
	NKern::FSSignal(&self->iSem);
	}

TBool DPropLChannel::Basic(RPropChannel::TBasicInfo* aInfo)
	{

	TUid category = aInfo->iCategory;
	TUint key = aInfo->iKey;
	TUint count = aInfo->iCount;
	RProperty::TType type = aInfo->iType;

	for (TUint i = 0; i < count; ++i)
		{
		RPropertyRef prop;
		TInt r = prop.Open(category, key);
		BASIC_ERROR(r, r == KErrNotFound);
		r = prop.Attach(category, key);
		BASIC_ERROR(r, r == KErrNone);

		//	Defines the attributes and access control for a property. This can only be done 
		//	once for each property. Subsequent attempts to define the same property will return
		//	KErrAlreadyExists.

		TSecurityPolicy policy;

		r = prop.Define(type, policy, policy);
		BASIC_ERROR(r, r == KErrNone);
		r = prop.Define(type, policy, policy);
		BASIC_ERROR(r, r == KErrAlreadyExists);
		r = prop.Delete();
		BASIC_ERROR(r, r == KErrNone);

		// Define fails with KErrArgument if wrong type or attribute was specified.
		r = prop.Define(RProperty::ETypeLimit, policy, policy);
		BASIC_ERROR(r, r == KErrArgument);

		static _LIT_SECURITY_POLICY_PASS(KPassPolicy);
		TSecurityPolicy badPolicy;
		*(TInt*)&badPolicy = -1;

		r = prop.Define(type, badPolicy, policy);
		BASIC_ERROR(r, r == KErrArgument);
		r = prop.Define(type, KPassPolicy, badPolicy);
		BASIC_ERROR(r, r == KErrArgument);
	
		if (type == RProperty::EInt)
			{
			// Define fails with KErrArgument if aType is TInt and aPreallocate is not 0
			r = prop.Define(type, KPassPolicy, KPassPolicy, 16);
			BASIC_ERROR(r, r == KErrArgument);

			// Following defintion the property has a default value, 0 for integer properties
			r = prop.Define(RProperty::EInt, KPassPolicy, KPassPolicy);
			BASIC_ERROR(r, r == KErrNone);
			TInt value;
			r = prop.Get(value);
			BASIC_ERROR(r, r == KErrNone);
			BASIC_ERROR(value, value == 0);
			r = prop.Delete();
			BASIC_ERROR(r, r == KErrNone);
			}
		else 
			{
			// Defne fails with KErrTooBig if aPeallocate is grater than KMaxPropertySize.
			r = prop.Define(RProperty::EByteArray, KPassPolicy, KPassPolicy, RProperty::KMaxPropertySize);
			BASIC_ERROR(r, r == KErrNone);
			r = prop.Delete();
			BASIC_ERROR(r, r == KErrNone);
			r = prop.Define(RProperty::EByteArray, KPassPolicy, KPassPolicy, RProperty::KMaxPropertySize+1);
			BASIC_ERROR(r, r == KErrTooBig);

			// Following defintion the property has a default value, zero-length data for byte-array and text 
			// properties. 
			r = prop.Define(RProperty::EByteArray, KPassPolicy, KPassPolicy);
			BASIC_ERROR(r, r == KErrNone);
			TBuf8<16> buf;
			r = prop.Get(buf);
			BASIC_ERROR(r, r == KErrNone);
			BASIC_ERROR(buf.Size(), buf.Size() == 0);
			r = prop.Delete();
			BASIC_ERROR(r, r == KErrNone);
			}

		// Pending subscriptions for this property will not be completed until a new value is published.
		TPropertySubsRequest subs(CompleteFn, this);
		iReason = KRequestPending;
		r = prop.Subscribe(subs);
		r = prop.Define(type, KPassPolicy, KPassPolicy);
		BASIC_ERROR(r, r == KErrNone);
		BASIC_ERROR(iReason, iReason == KRequestPending);
		r = prop.Delete();
		BASIC_ERROR(r, r == KErrNone);
		NKern::FSWait(&iSem);
		BASIC_ERROR(iReason, iReason == KErrNotFound);

		// If the property has not been defined Delete() fails with KErrNotFound.
		r = prop.Delete();
		BASIC_ERROR(r, r == KErrNotFound);

		// When deleted any pending subscriptions for the property will be completed with KErrNotFound.
		r = prop.Define(type, KPassPolicy, KPassPolicy);
		BASIC_ERROR(r, r == KErrNone);
		iReason = KRequestPending;
		r = prop.Subscribe(subs);
		BASIC_ERROR(r, r == KErrNone);
		BASIC_ERROR(iReason, iReason == KRequestPending);
		r = prop.Delete();
		BASIC_ERROR(r, r == KErrNone);
		NKern::FSWait(&iSem);
		BASIC_ERROR(iReason, iReason == KErrNotFound);

		// Any new request will not complete until the property is defined and published again.
		iReason = KRequestPending;
		r = prop.Subscribe(subs);
		BASIC_ERROR(r, r == KErrNone);
		BASIC_ERROR(iReason, iReason == KRequestPending);
		r = prop.Define(type, KPassPolicy, KPassPolicy);
		BASIC_ERROR(r, r == KErrNone);
		BASIC_ERROR(iReason, iReason == KRequestPending);
		if (type == RProperty::EInt)
			{
			r = prop.Set(1);
			BASIC_ERROR(r, r == KErrNone);
			}
		else
			{
			TBuf8<16> buf((TUint8*) "Foo");
			r = prop.Set(buf);
			BASIC_ERROR(r, r == KErrNone);
			}
		NKern::FSWait(&iSem);
		BASIC_ERROR(iReason, iReason == KErrNone);
		r = prop.Delete();
		BASIC_ERROR(r, r == KErrNone);

		// If the property has not been defined Set()/Get() fail with KErrNotFound.
			{
			TInt value;
			TBuf8<16> buf;
			if (type == RProperty::EInt)
				{
				r = prop.Get(value);
				BASIC_ERROR(r, r == KErrNotFound);
				r = prop.Set(value);
				BASIC_ERROR(r, r == KErrNotFound);
				}
			else
				{
				r = prop.Get(buf);
				BASIC_ERROR(r, r == KErrNotFound);
				r = prop.Set(buf);
				BASIC_ERROR(r, r == KErrNotFound);
				}
			}

		r = prop.Define(type, KPassPolicy, KPassPolicy);
		BASIC_ERROR(r, r == KErrNone);

		// If the property is larger than KMaxPropertySize Set() fails with KErrTooBig
			{
			if (type ==  RProperty::EByteArray)
				{
				TBuf8<RProperty::KMaxPropertySize + 1> buf(RProperty::KMaxPropertySize + 1);
				r = prop.Set(buf);
				BASIC_ERROR(r, r == KErrTooBig);
				}
			}

		// When type of operation mismatch with the property type Set()/Get() fails with KErrArgument.
			{
			if (type !=  RProperty::EInt)
				{
				TInt value;
				r = prop.Get(value);
				BASIC_ERROR(r, r == KErrArgument);
				r = prop.Set(value);
				BASIC_ERROR(r, r == KErrArgument);
				}
			else
				{
				TBuf8<16> buf;
				r = prop.Get(buf);
				BASIC_ERROR(r, r == KErrArgument);
				r = prop.Set(buf);
				BASIC_ERROR(r, r == KErrArgument);
				}
			}

		// Get/Set
		if (type == RProperty::EInt)
			{
			r = prop.Set(1);
			BASIC_ERROR(r, r == KErrNone);
			TInt value = 0;
			r = prop.Get(value);
			BASIC_ERROR(r, r == KErrNone);
			BASIC_ERROR(value, value == 1);
			}
		else 
			{
			TBuf8<16> ibuf((TUint8*)"Foo");
			TBuf8<16> obuf;
			r = prop.Set(ibuf);
			BASIC_ERROR(r, r == KErrNone);
			r = prop.Get(obuf);
			BASIC_ERROR(r, r == KErrNone);
			r = obuf.Compare(ibuf);
			BASIC_ERROR(r, r == 0);
			}

		// If the supplied buffer is too small Get() fails with KErrOverflow and the truncated value is reported.
		if (type == RProperty::EByteArray)
			{
			TBuf8<16> ibuf((TUint8*) "0123456789012345");
			TBuf8<16> obuf((TUint8*) "abcdefghigklmnop");
			TPtr8 optr((TUint8*) obuf.Ptr(), 0, 15);
			r = prop.Set(ibuf);
			BASIC_ERROR(r, r == KErrNone);
			r = prop.Get(optr);
			BASIC_ERROR(r, r == KErrOverflow);
			BASIC_ERROR(optr.Length(), optr.Length() == 15); 
			BASIC_ERROR(obuf[14], obuf[14] == '4'); 
			BASIC_ERROR(obuf[15], obuf[15] == 'p');
			}

		// The calling thread will have the specified request status signalled when the property is next updated.
		iReason = KRequestPending;
		r = prop.Subscribe(subs);
		BASIC_ERROR(r, r == KErrNone);
		BASIC_ERROR(iReason, iReason == KRequestPending);
		if (type == RProperty::EInt)
			{
			r = prop.Set(1);
			BASIC_ERROR(r, r == KErrNone);
			}
		else
			{
			TBuf8<16> buf((TUint8*) "Foo");
			r = prop.Set(buf);
			BASIC_ERROR(r, r == KErrNone);
			}
		NKern::FSWait(&iSem);
		BASIC_ERROR(iReason, iReason == KErrNone);

		r = prop.Delete();
		BASIC_ERROR(r, r == KErrNone);

		// Cancel an outstanding subscription request.
		// If it has not already completed, the request is completed with KErrCancelled.
		iReason = KRequestPending;
		r = prop.Subscribe(subs);
		BASIC_ERROR(r, r == KErrNone);
		BASIC_ERROR(iReason, iReason == KRequestPending);
		prop.Cancel(subs);		
		NKern::FSWait(&iSem);
		BASIC_ERROR(iReason, iReason == KErrCancel);

		r = prop.Define(type, KPassPolicy, KPassPolicy);
		BASIC_ERROR(r, r == KErrNone);

		iReason = KRequestPending;
		r = prop.Subscribe(subs);
		BASIC_ERROR(r, r == KErrNone);
		BASIC_ERROR(iReason, iReason == KRequestPending);
		if (type == RProperty::EInt)
			{
			r = prop.Set(1);
			BASIC_ERROR(r, r == KErrNone);
			}
		else
			{
			TBuf8<16> buf((TUint8*) "Foo");
			r = prop.Set(buf);
			BASIC_ERROR(r, r == KErrNone);
			}
		NKern::FSWait(&iSem);
		BASIC_ERROR(iReason, iReason == KErrNone);
		prop.Cancel(subs);		
		BASIC_ERROR(iReason, iReason == KErrNone);

		iReason = KRequestPending;
		r = prop.Subscribe(subs);
		BASIC_ERROR(r, r == KErrNone);
		BASIC_ERROR(iReason, iReason == KRequestPending);
		prop.Cancel(subs);		
		NKern::FSWait(&iSem);
		BASIC_ERROR(iReason, iReason == KErrCancel);

		r = prop.Delete();
		BASIC_ERROR(r, r == KErrNone);

		prop.Close();
		}
	return ETrue;
	}

//
// Client requests.
//
TBool DPropLChannel::Request(TInt aFunction, TAny* a1, TAny*)
	{
	TBool r;
	switch (aFunction)
		{
		case RPropChannel::EBasicTests:
			RPropChannel::TBasicInfo info;
			kumemget32(&info, a1, sizeof(info));
			NKern::ThreadEnterCS();
			r = Basic(&info);
			NKern::ThreadLeaveCS();
			break;	
		default:
			r = EFalse;
			break;
		}
	return r;
	}
