/**************************************************************
 * 
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/



#include	<rtl/alloc.h>
#include	<osl/file.hxx>
#include	<codemaker/typemanager.hxx>

using namespace rtl;

TypeManager::TypeManager()
{
	m_pImpl = new TypeManagerImpl();
	acquire();	
}	

TypeManager::~TypeManager()
{
	release();
}	

sal_Int32 TypeManager::acquire()
{ 
	return osl_incrementInterlockedCount(&m_pImpl->m_refCount);
}

sal_Int32 TypeManager::release()
{ 
	sal_Int32 refCount = 0;
	if (0 == (refCount = osl_decrementInterlockedCount(&m_pImpl->m_refCount)) )
	{
		delete m_pImpl;
	}
	return refCount;;
}

RegistryTypeManager::RegistryTypeManager()
{
	m_pImpl = new RegistryTypeManagerImpl();
	acquire();	
}	

RegistryTypeManager::~RegistryTypeManager()
{
	release();
}	

void RegistryTypeManager::acquire()
{ 
	TypeManager::acquire();
}

void RegistryTypeManager::release()
{ 
	if (0 == TypeManager::release())
	{
		if (m_pImpl->m_pMergedRegistry)
		{	
			if (m_pImpl->m_pMergedRegistry->isValid())
			{
				m_pImpl->m_pMergedRegistry->destroy(OUString());
			}
			
			delete m_pImpl->m_pMergedRegistry;
		}
		
		if (m_pImpl->m_registries.size() > 0)
		{
			freeRegistries();
		}

		delete m_pImpl;
	}
}

sal_Bool RegistryTypeManager::init(sal_Bool bMerged, const StringVector& regFiles)
{
	m_pImpl->m_isMerged = bMerged && (regFiles.size() > 1);
	
	if (regFiles.empty())
		return sal_False;

	StringVector::const_iterator iter = regFiles.begin();

	Registry tmpReg;
	while (iter != regFiles.end())
	{
		if (!tmpReg.open( convertToFileUrl(*iter), REG_READONLY))
			m_pImpl->m_registries.push_back(new Registry(tmpReg));
		else
		{
			freeRegistries();
			return sal_False;
		}
		iter++;
	}

	if (m_pImpl->m_isMerged)
	{
		Registry *pTmpReg = new Registry;
        OUString tmpName;
		osl::FileBase::createTempFile(0, 0, &tmpName);
		if (!pTmpReg->create(tmpName))
		{
			RegistryKey rootKey;	
			RegError ret = REG_NO_ERROR;
			OUString aRoot( RTL_CONSTASCII_USTRINGPARAM("/") );
			iter = regFiles.begin();
			pTmpReg->openRootKey(rootKey);
			
			while (iter != regFiles.end())
			{
				if ( (ret = pTmpReg->mergeKey(rootKey, aRoot, convertToFileUrl( *iter ))) )
				{
					if (ret != REG_MERGE_CONFLICT)
					{
						freeRegistries();
						rootKey.closeKey();
						pTmpReg->destroy( OUString() );
						delete pTmpReg;
						return sal_False;
					}
				}
				iter++;
			}
			
			m_pImpl->m_pMergedRegistry = pTmpReg;
			freeRegistries();
		} else
		{
			delete pTmpReg;
			freeRegistries();
			return sal_False;
		}
	}
	
	return sal_True;
}	

TypeReader RegistryTypeManager::getTypeReader(const OString& name)
{
	TypeReader reader;
	RegistryKey key(searchTypeKey(name));
	
	if (key.isValid())
	{
		RegValueType 	valueType;
		sal_uInt32		valueSize;

		if (!key.getValueInfo(OUString(), &valueType, &valueSize))
		{
			sal_uInt8*	pBuffer = (sal_uInt8*)rtl_allocateMemory(valueSize);	
			if (!key.getValue(OUString(), pBuffer))
			{
				reader = TypeReader(pBuffer, valueSize, sal_True);
			}		
			rtl_freeMemory(pBuffer);
		}
	}
	return reader;
}	

RTTypeClass RegistryTypeManager::getTypeClass(const OString& name)
{
	if (m_pImpl->m_t2TypeClass.count(name) > 0)
	{
		return m_pImpl->m_t2TypeClass[name];		
	} else
	{
		RegistryKey key(searchTypeKey(name));
		
		if (key.isValid())
		{
			RegValueType 	valueType;
			sal_uInt32		valueSize;

			if (!key.getValueInfo(OUString(), &valueType, &valueSize))
			{
				sal_uInt8*	pBuffer = (sal_uInt8*)rtl_allocateMemory(valueSize);	
				if (!key.getValue(OUString(), pBuffer))
				{
					TypeReader reader(pBuffer, valueSize, sal_False);

					RTTypeClass ret = reader.getTypeClass();
					
					rtl_freeMemory(pBuffer);
					
					m_pImpl->m_t2TypeClass[name] = ret;				
					return ret;
				}		
				rtl_freeMemory(pBuffer);
			}
		}
	}	

	return RT_TYPE_INVALID;	
}	

void RegistryTypeManager::setBase(const OString& base)
{ 
	m_pImpl->m_base = base;

	if (base.lastIndexOf('/') != (base.getLength() - 1))
	{
		m_pImpl->m_base += "/";			
	}
}

void RegistryTypeManager::freeRegistries()
{
	RegistryList::const_iterator iter = m_pImpl->m_registries.begin();

	while (iter != m_pImpl->m_registries.end())
	{
		delete *iter;

		iter++;
	}	

}	

RegistryKey	RegistryTypeManager::searchTypeKey(const OString& name)
{
	RegistryKey key, rootKey;

	if (m_pImpl->m_isMerged)
	{
		if (!m_pImpl->m_pMergedRegistry->openRootKey(rootKey))
		{
			rootKey.openKey(OStringToOUString(m_pImpl->m_base + name, RTL_TEXTENCODING_UTF8), key);
		}
	} else
	{
		RegistryList::const_iterator iter = m_pImpl->m_registries.begin();

		while (iter != m_pImpl->m_registries.end())
		{
			if (!(*iter)->openRootKey(rootKey))
			{
				if (!rootKey.openKey(OStringToOUString(m_pImpl->m_base + name, RTL_TEXTENCODING_UTF8), key))
					break;
			}

			iter++;
		}	
	}
	
	return key;
}	
	
