/**************************************************************
 * 
 * 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.
 * 
 *************************************************************/



// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_idlc.hxx"
#include <idlc/astscope.hxx>
#include <idlc/astbasetype.hxx>
#ifndef _IDLC_ASTINERFACE_HXX_
#include <idlc/astinterface.hxx>
#endif
#include <idlc/errorhandler.hxx>


using namespace ::rtl;

sal_Bool isGlobal(const OString& scopedName)
{
	if ((scopedName.getLength() == 0) || (scopedName.indexOf(':') == 0))
	{
		return sal_True;	
	}
	return sal_False;
}	

AstScope::AstScope(NodeType nodeType)
	: m_nodeType(nodeType)
{
	
}	

AstScope::~AstScope()
{
	
}	

AstDeclaration* AstScope::addDeclaration(AstDeclaration* pDecl)
{
	AstDeclaration* pDeclaration = NULL;

	if ((pDeclaration = lookupForAdd(pDecl)) != NULL) 
	{
		if (pDecl->getNodeType() == NT_union_branch )
		{
			m_declarations.push_back(pDecl);
			return pDecl;			
		}
		if ( pDecl->hasAncestor(pDeclaration) )
		{
			idlc()->error()->error2(EIDL_REDEF_SCOPE, pDecl, pDeclaration);
			return NULL;			
		}
		if ( (pDecl->getNodeType() == pDeclaration->getNodeType()) &&
			 (pDecl->getNodeType() == NT_sequence
              || pDecl->getNodeType() == NT_array
              || pDecl->getNodeType() == NT_instantiated_struct) )
		{
			return pDeclaration;
		}
		if ( (pDeclaration->getNodeType() == NT_interface)
			 && (pDecl->getNodeType() == NT_interface)
			 && !((AstInterface*)pDeclaration)->isDefined() )
		{
			m_declarations.push_back(pDecl);
			return pDecl;
		}
		if ( (NT_service == m_nodeType) &&
			 ( ((pDecl->getNodeType() == NT_interface_member) 
			    && (pDeclaration->getNodeType() == NT_interface)) ||
               ((pDecl->getNodeType() == NT_service_member)  
			    && (pDeclaration->getNodeType() == NT_service)) )
            )
		{
			m_declarations.push_back(pDecl);
			return pDecl;
		}
		
		idlc()->error()->error2(EIDL_REDEF_SCOPE, scopeAsDecl(this), pDecl);
		return NULL;						
	}
	
	m_declarations.push_back(pDecl);
	return pDecl;
}	

sal_uInt16 AstScope::getNodeCount(NodeType nodeType)
{
	DeclList::const_iterator iter = getIteratorBegin();
	DeclList::const_iterator end = getIteratorEnd();
	AstDeclaration* pDecl = NULL;
	sal_uInt16 count = 0;
	
	while ( iter != end )
	{
		pDecl = *iter;
		if ( pDecl->getNodeType() == nodeType )
			count++;
		++iter;
	}
	return count;			
}	

AstDeclaration* AstScope::lookupByName(const OString& scopedName)
{
	AstDeclaration* pDecl = NULL;
	AstScope*		pScope = NULL;
	if (scopedName.getLength() == 0)
		return NULL;
		
	// If name starts with "::" start look up in global scope
	if ( isGlobal(scopedName) )
	{
		pDecl = scopeAsDecl(this);
		if ( !pDecl )
			return NULL;
			
		pScope = pDecl->getScope();
		// If this is the global scope ...
		if ( !pScope )
		{
			// look up the scopedName part after "::"
			OString subName = scopedName.copy(2);
			pDecl = lookupByName(subName);
			return pDecl;
			//return pScope->lookupByName();
		}
		// OK, not global scope yet, so simply iterate with parent scope 
		pDecl = pScope->lookupByName(scopedName);
		return pDecl;
	}

	// The name does not start with "::"
	// Look up in the local scope and start with the first scope
    sal_Int32 nIndex = scopedName.indexOf(':');
	OString firstScope =  nIndex > 0 ? scopedName.copy(0, nIndex) : scopedName;
	sal_Bool	bFindFirstScope = sal_True;
	pDecl = lookupByNameLocal(firstScope);
	if ( !pDecl )
	{
		bFindFirstScope = sal_False;

		// OK, not found. Go down parent scope chain
		pDecl = scopeAsDecl(this);
		if ( pDecl )
		{
			pScope = pDecl->getScope();
			if ( pScope )
	   			pDecl = pScope->lookupByName(scopedName);
			else
				pDecl = NULL;
			
		 	// Special case for scope which is an interface. We
		 	// have to look in the inherited interfaces as well.
			if ( !pDecl ) 
			{
				if (m_nodeType == NT_interface)
					pDecl = lookupInInherited(scopedName);
			}
	   }
	}

	if ( bFindFirstScope && (firstScope != scopedName) )
	{
        sal_Int32 i = 0;
        sal_Int32 nOffset = 2;
        do
        {
            pScope = declAsScope(pDecl);
            if( pScope )
            {
                pDecl = pScope->lookupByNameLocal(scopedName.getToken(nOffset, ':', i ));
                nOffset = 1;
            }
            if( !pDecl )
                break;
        } while( i != -1 );

        if ( !pDecl )
        {
            // last try if is not the global scope and the scopeName isn't specify global too
            pDecl = scopeAsDecl(this);
            if ( pDecl && (pDecl->getLocalName() != "") )
            {
                pScope = pDecl->getScope();
                if ( pScope )
                    pDecl = pScope->lookupByName(scopedName);
            } else
            {
                pDecl = NULL;
            }            
        }

	}

	return pDecl;
}	

AstDeclaration* AstScope::lookupByNameLocal(const OString& name) const
{
	DeclList::const_iterator iter(m_declarations.begin());
	DeclList::const_iterator end(m_declarations.end());
	AstDeclaration* pDecl = NULL;
	
	while ( iter != end )
	{
		pDecl = *iter;
		if ( pDecl->getLocalName() == name )
			return pDecl;
		++iter;
	}
	return NULL;			
}	

AstDeclaration* AstScope::lookupInInherited(const OString& scopedName) const
{
	AstInterface* pInterface = (AstInterface*)this;	
	
	if ( !pInterface )
		return NULL;

	// Can't look in an interface which was not yet defined
	if ( !pInterface->getScope() )
	{
		idlc()->error()->forwardLookupError(pInterface, scopedName);
	}

	// OK, loop through inherited interfaces. Stop when you find it
    AstInterface::InheritedInterfaces::const_iterator iter(
        pInterface->getAllInheritedInterfaces().begin());
    AstInterface::InheritedInterfaces::const_iterator end(
        pInterface->getAllInheritedInterfaces().end());
    while ( iter != end )
    {
        AstInterface const * resolved = iter->getResolved();
        AstDeclaration* pDecl = resolved->lookupByNameLocal(scopedName);
        if ( pDecl )
            return pDecl;
        pDecl = resolved->lookupInInherited(scopedName);
        if ( pDecl )
            return pDecl;
        ++iter;
    }
	// Not found
	return NULL;
}

AstDeclaration* AstScope::lookupPrimitiveType(ExprType type)
{
	AstDeclaration* pDecl = NULL;
	AstScope*		pScope = NULL;
	AstBaseType*	pBaseType = NULL;
	OString			typeName;
	pDecl = scopeAsDecl(this);
	if ( !pDecl )
		return NULL;
	pScope = pDecl->getScope();
	if ( pScope)
		return pScope->lookupPrimitiveType(type);

	switch (type) 
	{
        case ET_none:
            OSL_ASSERT(false);
            break;
		case ET_short:
			typeName = OString("short");
			break;
		case ET_ushort:
			typeName = OString("unsigned short");
			break;
		case ET_long:
			typeName = OString("long");
			break;
		case ET_ulong:
			typeName = OString("unsigned long");
			break;
		case ET_hyper:
			typeName = OString("hyper");
			break;
		case ET_uhyper:
			typeName = OString("unsigned hyper");
			break;
		case ET_float:
			typeName = OString("float");
			break;
		case ET_double:
			typeName = OString("double");
			break;
		case ET_char:
			typeName = OString("char");
			break;
		case ET_byte:
			typeName = OString("byte");
			break;
		case ET_boolean:
			typeName = OString("boolean");
			break;
		case ET_any:
			typeName = OString("any");
			break;
		case ET_void:
			typeName = OString("void");
			break;
		case ET_type:
			typeName = OString("type");
			break;
		case ET_string:
			typeName = OString("string");
			break;
	}

	pDecl = lookupByNameLocal(typeName);	

	if ( pDecl && (pDecl->getNodeType() == NT_predefined) )
	{
		pBaseType = (AstBaseType*)pDecl;
		
		if ( pBaseType->getExprType() == type )
			return pDecl;
	}

	return NULL;
}
	
AstDeclaration* AstScope::lookupForAdd(AstDeclaration* pDecl)
{
	if ( !pDecl )
		return NULL;

	AstDeclaration* pRetDecl = lookupByNameLocal(pDecl->getLocalName());

   return pRetDecl;	
}	
