/*
* Copyright (c) 2002-2004 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: Utility methods for mrversit2.
*
*/


// Class include.
#include "emailtrace.h"
#include "esmricalutil.h"	// NESMRICalUtil

//debug

// User includes.
#include "esmricalerrors.h"			// Error codes
#include "esmricalkeywords.h"		// Literals
#include "cesmricalproperty.h"		// CESMRICalProperty
#include "cesmricalpropertyparam.h"	// CESMRICalPropertyParam

namespace NESMRICalUtil
	{
	/**
	Finds the first occurence of a character within a string which does not occur
	between a pair of double quotes.
	@param aString The string to search.
	@param aChar The character to search for.
	@return The position of the character within the string, or KErrNotFound.
	@internalTechnology
	*/
	TInt FindFirstUnquoted(const TDesC& aString, const TChar& aChar)
		{
		
		TBool quoted(EFalse);
		
		for (TInt pos = 0; pos < aString.Length(); ++pos)
			{
			TChar val(aString[pos]);
			
			if (val == KICalQuoteChar)
				{
				quoted = !quoted;
				}
			else if ((val == aChar) && (!quoted))
				{
				return pos;
				}
			}
			
		return KErrNotFound;
		}

	/**
	Finds the first occurence of a character within a string which is not escaped
	by a backslash character. Searching for a comma will find it in the following
	strings: "abc," and "abc\\," but will not find it in "abc\," and "abc\\\,".
	Note that backslash cannot exist unescaped, so cannot be searched for.
	@param aString The string to search.
	@param aChar The character to search for.
	@return The position of the character within the string, or KErrNotFound.
	@internalTechnology
	*/
	TInt FindFirstUnescaped(const TDesC& aString, const TChar& aChar)
		{
		
		TBool escaped(EFalse);
		
		for (TInt pos = 0; pos < aString.Length(); ++pos)
			{
			TChar val(aString[pos]);
			
			if (val == KICalBackslashChar)
				{
				escaped = !escaped;
				}
			else 
				{
				if ((val == aChar) && (!escaped))
					{
					return pos;
					}
					
				escaped = EFalse;
				}
			}
		
		return KErrNotFound;	
		}

	/**
	Finds the first occurence of a character within a string, whether or not it
	is escaped by a backslash character. If it was escaped, however, the
	aWasEscaped parameter will be set to true. Note that backslash cannot exist
	unescaped, so cannot be searched for.
	@param aString The string to search.
	@param aChar The character to search for.
	@param aWasEscaped True if the character was escaped, otherwise false.
	@return The position of the character within the string, or KErrNotFound.
	@internalTechnology
	*/
	TInt FindFirstAndUnescape(const TDesC& aString, const TChar& aChar, TBool& aWasEscaped)
		{
		
		aWasEscaped = EFalse;
		TBool escaped(EFalse);
		
		for (TInt pos = 0; pos < aString.Length(); ++pos)
			{
			TChar val(aString[pos]);
			
			if (val == KICalBackslashChar)
				{
				escaped = !escaped;
				}
			else 
				{
				if (val == aChar)
					{
					aWasEscaped = escaped;
					return pos;
					}
					
				escaped = EFalse;
				}
			}
		
		return KErrNotFound;	
		}

	/**
	Take a line and extract the name, parameter and value sections.
	@param aLine A line of text in which to find the data.
	@param aName A TPtrC referencing the section of aLine containing the name.
	@param aParameters A TPtrC referencing the section of aLine containing the
	parameters.
	@param aValues A TPtrC referencing the section of aLine containing the
	values.
	@return An error code: either KErrNone or KErrPropertyHasNoValue - avoid
	leaving so we can try and recover without the overhead of a trap.
	@internalTechnology
	*/
	TInt ExtractSectionsL(const TDesC& aLine, TPtrC& aName, TPtrC& aParameters, TPtrC& aValues)
		{
		
		// Find the value delimiter.
		const TInt colon = FindFirstUnquoted(aLine, KICalColonChar);

		// Vague check that the property has a value.
		if ((colon == KErrNotFound) || (colon == (aLine.Length() - 1)))
			{
			return KErrPropertyHasNoValue;	// No value given - the property is invalid.
			}

		// Find the first parameter delimiter.
		const TInt firstSemiColon = aLine.Locate(KICalSemiColonChar);
			
		// Find the characters dividing each section:
		TUint endOfName(colon);

		if ((firstSemiColon != KErrNotFound) && (firstSemiColon < colon))
			{
			endOfName = firstSemiColon;
			}
		
		// Set the name, parameters and values:
		aName.Set(aLine.Left(endOfName));
	
		if (endOfName < colon)
			{
			const TInt startOfParameters = endOfName + 1;
			aParameters.Set(aLine.Mid(startOfParameters, colon - startOfParameters));
			}
		else
			{
			aParameters.Set(aLine.Left(0));	// No parameters.
			}
		
		aValues.Set(aLine.Mid(colon + 1));
		
		return KErrNone;
		}

	/**
	Takes two strings, representing the parameters and values of a property, and
	the property itself, and populates the latter with the contents of the first
	two.
	@param aParameters A string of parameters.
	@param aValues A string of values.
	@param aProperty A property to add the parameters and values to.
	@internalTechnology
	*/
	void ExtractPropertyL(const TDesC& aParameters, const TDesC& aValues, CESMRICalProperty& aProperty)
		{
		
		ExtractParametersL(aParameters, aProperty);
		ExtractPropertyValuesL(aValues, aProperty);
		
		}

	/**
	Takes a string representing the parameters of a property, and the property
	itself, and populates the property with the contents of the string.
	@param aParameters A string of parameters.
	@param aProperty A property to add the parameters and values to.
	@internalTechnology
	*/
	void ExtractParametersL(const TDesC& aParameters, CESMRICalProperty& aProperty)
		{
		
		TPtrC remainder(aParameters);
		
		while (remainder.Length() > 0)
			{
			TInt endOfParameter(FindFirstUnquoted(remainder, KICalSemiColonChar));
			
			if (endOfParameter == KErrNotFound)
				{
				endOfParameter = remainder.Length();
				}
			
			CESMRICalPropertyParam& param = aProperty.AddPropertyParamL();
			ExtractParameterL(remainder.Left(endOfParameter), param);
			
			// Remove the parameter if it has no value.
			if (param.Values().Count() == 0)
				{
				aProperty.RemovePropertyParamL(param);
				}

			if (endOfParameter < remainder.Length())
				{
				remainder.Set(remainder.Mid(endOfParameter + 1));
				}
			else
				{
				break;
				}
			}
		}

	/**
	Takes a string representing a single parameter, and a class to store it in.
	@param aParameter A string of the form <parameter>[=<value>[,value]*].
	@param aPropertyParam A property parameter to store the data in.
	@internalTechnology
	*/
	void ExtractParameterL(const TDesC& aParameter, CESMRICalPropertyParam& aPropertyParam)
		{
		
		TInt firstEquals(aParameter.Locate(KICalEqualsChar));
		TInt endOfType(aParameter.Length());
		
		if (firstEquals != KErrNotFound)
			{
			endOfType = firstEquals;
			}
		
		aPropertyParam.SetTypeL(aParameter.Left(endOfType));
		
		if (endOfType < aParameter.Length())
			{
			ExtractParameterValuesL(aParameter.Mid(endOfType + 1), aPropertyParam);
			}
		}

	/**
	Takes a string of comma separated values and adds them to a parameter.
	@param aValues A string of the form <value>[,<value>]*.
	@param aPropertyParam A property parameter to store the values in.
	@internalTechnology
	*/
	void ExtractParameterValuesL(const TDesC& aValues, CESMRICalPropertyParam& aPropertyParam)
		{
		
		TPtrC remainder(aValues);
		
		while (remainder.Length() > 0)
			{
			TInt endOfValue(FindFirstUnquoted(remainder, KICalCommaChar));
			
			if (endOfValue == KErrNotFound)
				{
				endOfValue = remainder.Length();
				}
			
			// Add value.
			if (endOfValue > 0)
				{
				aPropertyParam.AddValueL(remainder.Left(endOfValue));
				}
			
			if (endOfValue < remainder.Length())
				{
				remainder.Set(remainder.Mid(endOfValue + 1));
				}
			else
				{
				break;
				}
			}
		}

	/**
	Takes a string of comma separated values and adds them to a property.
	@param aValues A string of the form <value>[,<value>]*.
	@param aProperty A property to store the values in.
	@param aEscaping Used for dealing with incorrect output from other applications.
	@leave Leaves with KErrUnknown if the value of aEscaping is not known.
	@internalTechnology
	*/
	void ExtractPropertyValuesL(const TDesC& aValues, CESMRICalProperty& aProperty, TEscaping aEscaping)
		{
		
		TPtrC remainder(aValues);
		TBool wasEscaped(EFalse);
		
		while (remainder.Length() > 0)
			{
			TInt endOfValue( KErrNotFound );
			
			switch (aEscaping)
				{
				case EEscapeNormal:
					endOfValue = FindFirstUnescaped(remainder, KICalCommaChar);
					break;
				case EEscapeValueSeparators:
					endOfValue = FindFirstAndUnescape(remainder, KICalCommaChar, wasEscaped);
					break;
				default:
					User::Leave(KErrUnknown);
					break;
				}
			
			if (endOfValue == KErrNotFound)
				{
				endOfValue = remainder.Length();
				}
			
			// Add value.
			if (endOfValue > 0)
				{
				aProperty.AddValueL(remainder.Left(endOfValue - (wasEscaped ? 1 : 0)));
				}
			
			if (endOfValue < remainder.Length())
				{
				remainder.Set(remainder.Mid(endOfValue + 1));
				}
			else
				{
				break;
				}
			}
		}
		
	/**
	Determines if the given property has a type which holds a text value.
	@param aProperty The property to test.
	@return ETrue if the property value is a "text" type.
	@internalTechnology
	*/
	TBool PropertyHasTextValueL(const CESMRICalProperty& aProperty)
		{
		
		const TDesC& type = aProperty.Type();
		
		// First check if it's an X-type.
		if ((type.Length() >= 2) && (type.Left(2).CompareF(KICalXProperty) == 0))
			{
			return ETrue;
			}

		// Else return ETrue if the property is PRODID, UID, CATEGORIES, COMMENT, CONTACT,
		// DESCRIPTION, LOCATION, RELATED-TO, RESOURCES, SUMMARY, TZID or TZNAME.
		return ((type.CompareF(KICalProdId) == 0) || (type.CompareF(KICalUid) == 0) ||
			(type.CompareF(KICalCategories) == 0) || (type.CompareF(KICalComment) == 0) ||
			(type.CompareF(KICalContact) == 0) || (type.CompareF(KICalDescription) == 0) || 
			(type.CompareF(KICalLocation) == 0) || (type.CompareF(KICalRelatedto) == 0) || 
			(type.CompareF(KICalResources) == 0) || (type.CompareF(KICalSummary) == 0) || 
			(type.CompareF(KICalTzid) == 0) || (type.CompareF(KICalTzname) == 0));
		}
	}

// End of File

