/**************************************************************
 * 
 * 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 <osl/interlck.h>
#include	<rtl/alloc.h>
#include	<codemaker/dependency.hxx>

using namespace rtl;

TypeDependency::TypeDependency()
{
	m_pImpl = new TypeDependencyImpl();
	acquire();
}	

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

void TypeDependency::acquire()
{ 
	osl_incrementInterlockedCount(&m_pImpl->m_refCount);
}

void TypeDependency::release()
{ 
	if (0 == osl_decrementInterlockedCount(&m_pImpl->m_refCount))
	{
		delete m_pImpl;
	}
}

sal_Bool TypeDependency::insert(const OString& type, const OString& depend, sal_uInt16 use)
{
	sal_Bool ret =  sal_False;
	
	if (type.getLength() > 0 && depend.getLength() > 0)
	{
		if (m_pImpl->m_dependencies.count(type) > 0)
		{
			TypeUsing typeUsing(depend, use);
			TypeUsingSet::iterator iter;
			if ((iter = m_pImpl->m_dependencies[type].find(typeUsing)) != m_pImpl->m_dependencies[type].end())
			{
  				(((TypeUsing *) &(*iter))->m_use) = (*iter).m_use | use;
			} else
			{
				m_pImpl->m_dependencies[type].insert(typeUsing);
			}
		} else
		{
			TypeUsing typeUsing(depend, use);
			TypeUsingSet tmpSet;
			tmpSet.insert(typeUsing);
			m_pImpl->m_dependencies[type]=tmpSet;
		}
	}
	
	return ret;	
}	

TypeUsingSet TypeDependency::getDependencies(const OString& type)
{
	if (type.getLength() > 0)
	{
		if (m_pImpl->m_dependencies.count(type) > 0)
		{
			return m_pImpl->m_dependencies[type];
		}
	}
		
	return TypeUsingSet();
}	

sal_Bool TypeDependency::hasDependencies(const OString& type)
{
	if (type.getLength() > 0)
	{
		if (m_pImpl->m_dependencies.count(type) > 0)
		{
			return sal_True;
		}
	}
		
	return sal_False;	
}	

void TypeDependency::setGenerated(const OString& type, sal_uInt16 genFlag)
{
//	m_pImpl->m_generatedTypes.insert(type);
	if (m_pImpl->m_generatedTypes.count(type) > 0)
		m_pImpl->m_generatedTypes[type]= m_pImpl->m_generatedTypes[type] | genFlag;
	else
		m_pImpl->m_generatedTypes[type]=genFlag;
}	

sal_Bool TypeDependency::isGenerated(const OString& type, sal_uInt16 genFlag)
{
/*
	if (m_pImpl->m_generatedTypes.count(type) > 0)
		return sal_True;

	return sal_False;
*/
	if (m_pImpl->m_generatedTypes.count(type) > 0 &&
		m_pImpl->m_generatedTypes[type] & genFlag)
	{
		return sal_True;
	}

	return sal_False;
}	

static sal_Bool checkFieldDependencies(TypeManager& typeMgr, TypeDependency& dependencies,
									   TypeReader& reader, const OString& type)
{
	sal_uInt32 count = reader.getFieldCount();

	if (count == 0 || reader.getTypeClass() == RT_TYPE_ENUM)
		return sal_True;
		
	OString fieldType;
	for (sal_uInt16 i=0; i < count; i++)
	{
		fieldType = reader.getFieldType(i);
		
		if (fieldType.getLength() > 0)
		{
			dependencies.insert(type, fieldType, TYPEUSE_MEMBER);
			checkTypeDependencies(typeMgr, dependencies, fieldType);
		}
	}
	
	return sal_True;		
}	

static sal_Bool checkMethodDependencies(TypeManager& typeMgr, TypeDependency& dependencies,
									    TypeReader& reader, const OString& type)
{
	sal_uInt32 count = reader.getMethodCount();

	if (count == 0)
		return sal_True;
		
	OString returnType, paramType, excType;
	sal_uInt32 paramCount = 0;
	sal_uInt32 excCount = 0;	
	RTParamMode paramMode = RT_PARAM_INVALID;
	for (sal_uInt16 i=0; i < count; i++)
	{
		returnType = reader.getMethodReturnType(i);
		
		dependencies.insert(type, returnType, TYPEUSE_RETURN);
		checkTypeDependencies(typeMgr, dependencies, returnType);

		paramCount = reader.getMethodParamCount(i);		
		excCount = reader.getMethodExcCount(i);

		sal_uInt16 j;
		for (j=0; j < paramCount; j++)
		{
			paramType = reader.getMethodParamType(i, j);
			paramMode = reader.getMethodParamMode(i, j);
			
			switch (paramMode)
			{
				case RT_PARAM_IN:
					dependencies.insert(type, paramType, TYPEUSE_INPARAM);
					break;
				case RT_PARAM_OUT:
					dependencies.insert(type, paramType, TYPEUSE_OUTPARAM);
					break;
				case RT_PARAM_INOUT:
					dependencies.insert(type, paramType, TYPEUSE_INOUTPARAM);
					break;
                default:
                    break;
			}

			checkTypeDependencies(typeMgr, dependencies, paramType);
		}
		
		for (j=0; j < excCount; j++)
		{
			excType = reader.getMethodExcType(i, j);
			dependencies.insert(type, excType, TYPEUSE_EXCEPTION);
			checkTypeDependencies(typeMgr, dependencies, excType);
		}
		
	}
	
	return sal_True;		
}	

static sal_Bool checkReferenceDependencies(TypeManager& typeMgr, TypeDependency& dependencies,
										   TypeReader& reader, const OString& type)
{
	sal_uInt32 count = reader.getReferenceCount();

	if (count == 0)
		return sal_True;
		
	OString referenceName;
	for (sal_uInt16 i=0; i < count; i++)
	{
		referenceName = reader.getReferenceName(i);
		
		dependencies.insert(type, referenceName, TYPEUSE_NORMAL);
		checkTypeDependencies(typeMgr, dependencies, referenceName);
	}
	
	return sal_True;	
}	

sal_Bool checkTypeDependencies(TypeManager& typeMgr, TypeDependency& dependencies, const OString& type, sal_Bool bDepend)
{
	if (!typeMgr.isValidType(type))
		return sal_False;

	if (dependencies.hasDependencies(type))
		return sal_True;		

	TypeReader reader = typeMgr.getTypeReader(type);
	
	if ( !reader.isValid() )
	{
		if (type.equals("/"))
			return sal_True;
		else
			return sal_False;
	}

    if ( bDepend && reader.getTypeClass() == RT_TYPE_MODULE)
    {
    	checkFieldDependencies(typeMgr, dependencies, reader, type);
        return sal_True;
    }
    
    for (sal_uInt16 i = 0; i < reader.getSuperTypeCount(); ++i) {
        OString superType(reader.getSuperTypeName(i));
		dependencies.insert(type, superType, TYPEUSE_SUPER);
		checkTypeDependencies(typeMgr, dependencies, superType);
	}

	if (reader.getTypeClass() == RT_TYPE_INTERFACE)
	{
		dependencies.insert(type, "com/sun/star/uno/RuntimeException", TYPEUSE_EXCEPTION);
		dependencies.insert(type, "com/sun/star/uno/TypeClass", TYPEUSE_NORMAL);
		checkTypeDependencies(typeMgr, dependencies, "com/sun/star/uno/RuntimeException", bDepend);
	}	
	
	checkFieldDependencies(typeMgr, dependencies, reader, type);
	checkMethodDependencies(typeMgr, dependencies, reader, type);
	checkReferenceDependencies(typeMgr, dependencies, reader, type);

    // make the scope modules as dependencies
    sal_Int32 nPos = type.lastIndexOf( '/' );

	if ( nPos >= 0 )
	{
		OString aScope( type.copy( 0, nPos ) );
		OStringBuffer tmpBuf(aScope.getLength());

		nPos = 0;
		do
		{
			tmpBuf.append(aScope.getToken(0, '/', nPos));
			dependencies.insert(type, tmpBuf.getStr(), TYPEUSE_SCOPE);
			tmpBuf.append('/');
		} while( nPos != -1 );
    }
    
	return sal_True;
}