/*
* Copyright (c) 2007-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: 
* Note: This file may contain code to generate corrupt files for test purposes.
* Such code is excluded from production builds by use of compiler defines;
* it is recommended that such code should be removed if this code is ever published publicly.
*
*/


/**
 @file 
 @internalComponent
 @released
*/

#include "sisstring.h"
#include "utility.h"
#include "exception.h"
#include "utils.h"
#include "utf8.h"

// we cannot write or read a string directly from a file, because we don't know what format the
// MS implementation of STL will use for the std::string. So we need to go via an intemediate buffer.
// But strings can be variable size, including very big indeed, so that buffer will have to be
// allocated as need be. Buffer allocation and deletion is expensive. It is sensible to allocate
// a common buffer, and only change its size when necessary. To reduce the frequency of file size
// changes, the actual allocation oversteps the mark, so the next time something requests a slightly
// bigger string, no allocation is required (usually). These variables could be private static members of our
// string class, but that would mean some minor change to this code would require the entire
// product to be recompiled.

static TUint8* gBuffer = NULL;
static unsigned long gBufferLength = 0;
static const unsigned long gChunk = 512;



void VerifyBufferSize (const unsigned long aSize)
	{
	if (aSize == 0)
		{
		return;
		}
	if (aSize > gBufferLength)
		{
		delete [] gBuffer;
		gBufferLength = aSize + gChunk;
		gBuffer = new TUint8 [gBufferLength];
		}
	}


void CSISString::ExitInstance ()
	{
	delete [] gBuffer;
	gBuffer = NULL;
	gBufferLength = 0;
	}


void CSISString::Read (TSISStream& aFile, const TFieldSize& aContainerSize, const CSISFieldRoot::TFieldType aArrayType)
	{
	CSISHeader header (CSISFieldRoot::ESISString);
	if (IsDataThere (aFile, header, aArrayType))
		{
		if (header.DataSize () == 0)
			{
			iData.erase ();
			}
		else
			{
				unsigned int dataSize = header.DataSize();
				VerifyBufferSize (dataSize);
				dataSize = AlignedSize(dataSize);
				aFile.read (gBuffer, dataSize);

				if(sizeof(WCHAR) != 2)
					{
					int targetLength = dataSize*sizeof(wchar_t);
					wchar_t *ptrUCS4 = new wchar_t [dataSize];
					UTF16* sourceStart = reinterpret_cast<UTF16*>(gBuffer);
					UTF16* sourceEnd = sourceStart + dataSize;
					UCS4* targetStart = reinterpret_cast<UCS4*>(ptrUCS4);
					UCS4* targetEnd = targetStart + dataSize*2;
					ConvertUTF16toUCS4(&sourceStart, sourceEnd, &targetStart, targetEnd);
					iData.assign ((ptrUCS4), static_cast <std::wstring::size_type> (dataSize / 2));
					}
				else
					{
					iData.assign (	reinterpret_cast <wchar_t*> (gBuffer), 
  							static_cast <std::wstring::size_type> (header.DataSize () / sizeof (wchar_t)));
					}
			}

		}
	}


void CSISString::Write (TSISStream& aFile, const bool aIsArrayElement) const
	{
	if (! WasteOfSpace ())
		{
#ifdef GENERATE_ERRORS
		if (CSISFieldRoot::IsBugToBeCreated (CSISFieldRoot::EBugInsaneString))
			{
			CSISHeader (CSISFieldRoot::ESISString, rand ()).Write (aFile, aIsArrayElement);
			}
		else
#endif // GENERATE_ERRORS
			{
			CSISHeader (CSISFieldRoot::ESISString, ByteCount (aIsArrayElement)).Write (aFile, aIsArrayElement);
			}
		
		// SIS file format only supports UCS-2 format , so need conversion here if wchar is 4 bytes (Linux OS)
		if(sizeof(WCHAR) != 2)
			{
			int targetLength = iData.size()*2;
			UTF16 *ptrUtf16 = new UTF16[targetLength + 1];
			UCS4* sourceStart = reinterpret_cast<UCS4*>(const_cast<wchar_t*>(iData.c_str()));
			UCS4* sourceEnd = sourceStart + (iData.size()*sizeof(WCHAR));
			UTF16* targetStart = reinterpret_cast<UTF16*>(ptrUtf16);
			UTF16* targetEnd = targetStart + targetLength;
			ConvertUCS4toUTF16(&sourceStart, sourceEnd, &targetStart, targetEnd);
			int endOffset = (UTF16*)targetStart - ptrUtf16;
			targetStart = reinterpret_cast<UTF16*>(ptrUtf16) + endOffset;
			*targetStart = 0;
			aFile.write (reinterpret_cast <const TUint8*> (ptrUtf16),targetLength);
			}
		else
			{
			aFile.write (reinterpret_cast <const TUint8*> (iData.c_str ()), ByteCount (aIsArrayElement));
			}
		WriteFiller (aFile);
		}
	}


void CSISString::Dump (std::ostream& aStream, const int aLevel) const
	{
	aStream << "\"" << wstring2string (iData) << "\"";
	}


void CSISString::CalculateCrc (TCRC& aCRC, const bool aIsArrayElement) const
	{
	if(!WasteOfSpace())
		{
		CSISHeader (CSISFieldRoot::ESISString, ByteCount (false)).CalculateCrc (aCRC, aIsArrayElement);
		CSISFieldRoot::DoPaddedCrc(aCRC, reinterpret_cast <const TUint8*> (iData.c_str ()), ByteCount (aIsArrayElement));
		}
	}


CSISFieldRoot::TFieldSize CSISString::ByteCount (const bool aInsideArray) const
	{
	if(sizeof(wchar_t) != 2)
		return static_cast <CSISFieldRoot::TFieldSize> (iData.size () * sizeof (wchar_t)/2);
	else
		return static_cast <CSISFieldRoot::TFieldSize> (iData.size () * sizeof (wchar_t));
	}


bool CSISString::WasteOfSpace () const
	{
	return ! Required () && iData.empty ();
	}

