/*
* Copyright (c) 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: 
* @internalComponent * @released
*
*/

#include "utf16string.h"
#ifdef __LINUX__
#include <iconv.h>
#elif defined(WIN32)
#ifdef _STLP_INTERNAL_WINDOWS_H
#define __INTERLOCKED_DECLARED
#endif
#include <windows.h>
#endif
UTF16String::UTF16String() :iData(0), iLength(0){
}
UTF16String::UTF16String(const UTF16String& aRight){
	iLength = aRight.iLength ;
	iData = new TUint16[iLength + 1];
	memcpy(iData,aRight.iData, iLength << 1);
	iData[iLength] = 0;
}
UTF16String::UTF16String(const string& aUtf8Str){
	iData = 0 ;
	iLength = 0 ;
	Assign(aUtf8Str.c_str(),aUtf8Str.length());
}
UTF16String::UTF16String(const TUint16* aUtf16Str,TInt aLength /* = -1*/){
	
	if(aLength < 0){
		aLength = 0 ;
		const TUint16* p = aUtf16Str ;
		while(*p){
			p++ ;
			aLength ++ ;
		}
	}
	if(aLength > 0){		
		iLength = aLength ;
		aLength <<= 1 ;
		iData = new TUint16[iLength + 1] ;
		memcpy(iData,aUtf16Str,aLength);
		iData[iLength] = 0;
	}else{
		iData = 0 ;
		iLength = 0 ;
	}
	
}
UTF16String::UTF16String(const char* aUtf8Str,TInt aLength /*= -1 */){
	iData = 0 ;
	iLength = 0 ;	
	Assign(aUtf8Str,aLength);
}	
UTF16String::~UTF16String(){
	if(iData)
		delete []iData ;
}	

UTF16String& UTF16String::operator = (const UTF16String& aRight){
	if(&aRight != this){
		if(iData) 
			delete []iData ; 
		iLength = aRight.iLength ;
		iData = new TUint16[iLength + 1];
		memcpy(iData,aRight.iData, iLength << 1);
		iData[iLength] = 0;
	}
	return *this;
}
bool UTF16String::FromFile(const char* aFileName){
	if(!aFileName || !(*aFileName))
		return false ;
	ifstream ifs(aFileName,ios_base::in + ios_base::binary); 	
	if(!ifs.is_open())
		return false ;
	
	ifs.seekg(0,ios_base::end);
	size_t length = ifs.tellg();
	if((length % 2) == 1 ){
		ifs.close() ;
		return false ;
	}
	ifs.seekg(0,ios_base::beg);
	TUint16 hdr ;
	size_t readLen = length - sizeof(hdr) ;
	length >>= 1 ;	
	TUint16 *newData = new TUint16[length + 1];
	ifs.read(reinterpret_cast<char*>(&hdr),sizeof(hdr));		
	if(hdr == 0xFEFF){ 
		ifs.read(reinterpret_cast<char*>(newData),readLen);
		length -- ;
	}
	else{		 
		*newData = hdr ;
		ifs.read(reinterpret_cast<char*>(&newData[1]),readLen);
	} 
	ifs.close();
	iLength = length ;
	if(iData)
		delete []iData ;
	iData = newData ;
	iData[iLength] = 0;	
	return true ;
}
/**
* aResult will not changed on error
*/
bool UTF16String::ToUTF8(string& aResult) const {
	if(IsEmpty()){
		aResult = "";
		return true;
	}
	size_t bufLen = (iLength + 1) << 2 ;
	char* buffer = new char[bufLen] ;
#ifdef WIN32
	int r = WideCharToMultiByte(CP_UTF8,0,reinterpret_cast<WCHAR*>(iData),iLength,buffer,bufLen,NULL,NULL);
	if(r < 0){
		delete []buffer ;
		return false ;
	}
	buffer[r] = 0;
	aResult.assign(buffer,r);
#else
	iconv_t it = iconv_open("UTF-8","UTF-16");
	if((iconv_t)(-1) == it){ 
		return  false;
	}
	char* bufferEnd = buffer ;
	char* in_ptr = reinterpret_cast<char*>(iData);
	size_t in_len = iLength << 1 ;
	iconv(it,&in_ptr,&in_len ,&bufferEnd,&bufLen);
	iconv_close(it);
	*bufferEnd = 0 ;
	size_t length = bufferEnd - buffer ;
	aResult.assign(buffer,length);
#endif
	delete []buffer ;
	return true ;
}
bool UTF16String::Assign(const char* aUtf8Str,TInt aLength /* = -1*/) {
	if(0 == aUtf8Str) return false ;	
	if(aLength < 0) aLength = strlen(aUtf8Str); 
		
#ifdef WIN32
	size_t newLength = aLength + 1;
	TUint16* newData = new TUint16[newLength];
	int r = MultiByteToWideChar(CP_UTF8,0,aUtf8Str,aLength,reinterpret_cast<WCHAR*>(newData),newLength);
	if(r < 0){
		delete []newData ;
		return false ;
	}
	iLength = r ;	
#else
	char* forFree = 0 ;
	if(aUtf8Str[aLength - 1] != 0){
		forFree = new char[aLength + 1];
		memcpy(forFree,aUtf8Str,aLength );
		forFree[aLength] = 0;
		aUtf8Str = forFree ;
	}
	iconv_t it = iconv_open("UTF-16","UTF-8");
	if((iconv_t)(-1) == it){ 
		 
		return  false;
	}
	size_t newLength = aLength + 2;
	TUint16* newData = new TUint16[newLength];
	newLength <<= 1;
	char* out_ptr = reinterpret_cast<char*>(newData);
	size_t in_len = aLength ;
	char* in_ptr = const_cast<char*>(aUtf8Str);
	iconv(it,&in_ptr,&in_len ,&out_ptr ,&newLength);
	newLength = out_ptr - reinterpret_cast<char*>(newData);
	iconv_close(it);
	if(newLength % 2 == 1){ //should not be possible
		delete []newData;
		return false ;
	}
	iLength = (newLength >> 1) ;
	if(forFree)
		delete []forFree;
#endif
	newData[iLength] = 0 ;
	if(iData){
		delete []iData ;
	}
	iData = newData ;	
	if(*iData == 0xFEFF)
		iLength -- ;
	
	return true ;
}
static const TUint16 NullUInt16Str[1] = {0};
const TUint16* UTF16String::c_str() const {
	if(0 == iData)
		return NullUInt16Str;
	else if(0xFEFF != *iData)
		return iData ;
	else
		return iData + 1;
}
///
/// aUtf16Str must end with '\0'
/// 
int UTF16String::Compare(const TUint16* aUtf16Str) const {
	if(!aUtf16Str || !(*aUtf16Str))
		return (iLength > 0) ? 1 : 0; 
	size_t i ;
	for(i = 0 ; aUtf16Str[i] != 0 ; i++) { 
		if( iData[i] > aUtf16Str[i])
			return 1;
		else if(iData[i] < aUtf16Str[i])
			return -1 ;
	}
	return (i < iLength) ? 1 : 0 ; 
}
///
/// aUtf16Str must end with '\0'
/// 
int UTF16String::CompareNoCase(const TUint16* aUtf16Str) const {
	if(!aUtf16Str || !(*aUtf16Str))
		return (iLength > 0) ? 1 : 0; 
	size_t i ;
	TUint16 a, b;
	for(i = 0 ; aUtf16Str[i] != 0 ; i++) { 
		a = iData[i];
		b = aUtf16Str[i] ;
		if( a >= 'A' && a <= 'Z') a |= 0x20 ;
		if( b >= 'A' && b <= 'Z') b |= 0x20 ;
			
		if( a > b )
			return 1;
		else if( a < b )
			return -1 ;
	}
	return (i < iLength) ? 1 : 0 ; 
}
TUint16* UTF16String::Alloc(size_t aNewLen) {
	TUint16* newData = new TUint16[aNewLen + 1] ;
	if(!newData) return 0;
	if(iData) delete []iData ;
	
	iLength = aNewLen ;
	iData = newData ;
	*iData = 0 ;
	iData[aNewLen] = 0;
	return iData;
}
