// Copyright (c) 1997-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of "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 <s32mem.h>
#include <tzid.h>
#include "tzidinternal.h"
#include <tz.h>


/**
The maximum number of digits for a user-defined time zone (when it is 0x20000 -
1).

@internalComponent
*/
const TInt KMaxTzIdDigits = 6;


CTzId::CTzId()
	{
	}


CTzId::CTzId(TUint aNumericId) :
	iReferenceId(aNumericId)
	{
	}


/**
Time zone ID data externaliser.

@param aStream A stream that will contain the serialised time zone ID.

@internalComponent
*/
EXPORT_C void CTzId::ExternalizeL(RWriteStream& aStream) const
	{
	aStream << iReferenceId;
	TInt32 size	= 0;
	if (iZoneId)
		{
		size = iZoneId->Length();
		aStream << size;
		if (size)
			{
			aStream.WriteL(iZoneId->Des(), size);
			}
		}
	else
		{
		aStream << size;
		}
	}


/**
Serialised CTzId data internaliser.

@param aStream a stream containing the serialised time zone Id

@internalComponent
*/
EXPORT_C void CTzId::InternalizeL(RReadStream& aStream)
	{

	aStream >> iReferenceId;

	delete iZoneId;
	iZoneId = NULL;

	TInt32 size;
	aStream >> size;

	const TInt KMaxSize = KMaxTInt / 2;
	if (size < 0 || size >= KMaxSize)
	    {
	    User::Leave( KErrArgument );
	    }

    // Don't create the tz id if the size is 0 - it hasn't been set.
    if (size)
        {
    	iZoneId = HBufC8::NewL(size);
    	TPtr8 bufferPtr(iZoneId->Des() );
    	aStream.ReadL(bufferPtr, size);
        }
	}


/**
Destructor.
*/
EXPORT_C CTzId::~CTzId()
	{
	delete iZoneId;
	}


/**
Time zone ID factory method.

@param aStream a stream containing the serialised time zone Id

@leave KErrNoMemory or some other system error code

@return Pointer to CTzId; clients own the object.

@internalComponent
*/
EXPORT_C CTzId* CTzId::NewL(RReadStream& aStream)
	{
	CTzId* self = new(ELeave) CTzId();
	CleanupStack::PushL(self);

	self->InternalizeL(aStream);

	CleanupStack::Pop(self);
	return self;
	}


/**
Time zone ID factory method.

@param aNameIdentity A name recognised by the time zone database.

@return A pointer to the time zone ID. Clients take ownership.
*/
EXPORT_C CTzId* CTzId::NewL(const TDesC8& aNameIdentity)
	{
	CTzId* self = new(ELeave) CTzId();
	CleanupStack::PushL(self);

	self->iZoneId = aNameIdentity.AllocL();

	CleanupStack::Pop(self);
	return self;
	}


/**
Time zone ID factory method.

@param aNumericId A numeric reference ID, generated by the time zone compiler.

@return A pointer to the time zone ID. Clients take ownership.

@panic TzServer 8 aNumericId must not be zero.
*/
EXPORT_C CTzId* CTzId::NewL(TUint aNumericId)
	{
	const TUint KUnacceptableTzId = 0;

	// aNumericId must not be zero 
	__ASSERT_ALWAYS((aNumericId != KUnacceptableTzId), RTz::Panic(RTz::EPanicUnsupportedTimeZoneNoId));
	CTzId* self = new(ELeave) CTzId(aNumericId);
	CleanupStack::PushL(self);
	self->ConstructL();	
	CleanupStack::Pop(self);
	return self;
	}

	
void CTzId::ConstructL()
	{
	if (IsUserTzId())
		{
		TBuf8<KMaxTzIdDigits> name;
   		name.Num(iReferenceId); 
		SetIdL(name);
		}
	}


/**
Replicates a CTzId.

@return A pointer to CTzId; clients own the object.

@leave KErrNoMemory or some other system error code.

@internalComponent
*/
EXPORT_C CTzId* CTzId::CloneL() const
	{
	CTzId* clone = NULL;
	if (iZoneId)
		{
		clone = NewL(*iZoneId);
		clone->iReferenceId = iReferenceId;
		}
	else
		{
		clone = new(ELeave) CTzId(iReferenceId);
		}
	return clone;
	}


/**
Retrieves the time zone's name.

This can be used after calling RTz::GetTimeZoneIdL().

@return The time zone's name, if one has been set. A NULL descriptor if not.
*/
EXPORT_C const TDesC8& CTzId::TimeZoneNameID() const
	{
	if (NULL == iZoneId)
		{
		return KNullDesC8();
		}

	return *iZoneId;
	}


/**
Retrieves the time zone's numeric ID.

This can be used after calling RTz::GetTimeZoneIdL().

@return The time zone's numeric ID, generated by the time zone compiler.
*/
EXPORT_C TUint CTzId::TimeZoneNumericID() const
	{
	return iReferenceId;
	}


/**
Sets the numeric time zone ID.

@param aNumericId The new numeric ID, generated by the time zone compiler.

@internalComponent
*/
EXPORT_C  void CTzId::SetId(TUint aNumericId)
	{
	iReferenceId = aNumericId;
	}


/**
Sets the the time zone's name.

@param aNameIdentity string TZID as used in the Olson's TZ Database

@internalComponent
*/
EXPORT_C void CTzId::SetIdL(const TDesC8& aNameIdentity)
	{
	delete iZoneId;
	iZoneId = NULL;

	iZoneId = aNameIdentity.AllocL();
	}


/** 
Equality operator.

@param aTZId The ID of another time zone.

@return ETrue if the specified time zone ID is the same as this one, otherwise
EFalse.
*/
EXPORT_C TBool CTzId::operator==(const CTzId& aTZId) const
	{
	if ((aTZId.iReferenceId != 0) && (iReferenceId != 0))
		{
		return (aTZId.iReferenceId == iReferenceId);
		}
	else
		{
		if (iZoneId == NULL)
			{
			return EFalse;
			}

		if (aTZId.iZoneId == NULL)
			{
			return EFalse;
			}

		return (iZoneId->Compare(*(aTZId.iZoneId)) == 0);
		}
	}


/**
Determines if this time zone identifier has a numeric identifier value that
belongs to the range of values used to identify user-defined time zones.

@return Returns ETrue if this time zone identifier has a numeric identifier
value that belongs to the range of values used to identify user-defined time
zones and EFalse otherwise.
*/
EXPORT_C TBool CTzId::IsUserTzId() const
	{
	// Check if just the string identifier has been set.  If so, we will need
	// to attempt to extract a number from the string identifier and use this
	// as the numeric identifier.
	if (iReferenceId == 0 && iZoneId && *iZoneId != KNullDesC8)
		{
		TUint intId;
		TLex8 lex(*iZoneId );
		if(lex.Val(intId) == KErrNone)
			{
			CTzId* self = const_cast<CTzId*>(this);
			self->SetId(intId);
			}
		}

	return IsUserTzId(iReferenceId);
	}


/**
Determines if the given numeric time zone identifier value belongs to the range
of values used to identify user-defined time zones.

@param aTzId Numeric time zone identifier value.

@return Returns ETrue if the given numeric time zone identifier value belongs to
the range of values used to identify user-defined time zones and EFalse
otherwise.
*/
EXPORT_C TBool CTzId::IsUserTzId(TUint aTzId)
	{
	return (aTzId >= KUserTzIdMin && aTzId <= KUserTzIdMax);
	}
