/**************************************************************
 * 
 * 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_xmloff.hxx"
#include "EventOASISTContext.hxx"
#include "EventMap.hxx"
#include "MutableAttrList.hxx"
#include "xmloff/xmlnmspe.hxx"
#include "ActionMapTypesOASIS.hxx"
#include "AttrTransformerAction.hxx"
#include "TransformerActions.hxx"
#ifndef _XMLOFF_TRANSFORMERBASE_HXX
#include "TransformerBase.hxx"
#endif

#ifndef OASIS_FILTER_OOO_1X
// Used to parse Scripting Framework URLs
#include <com/sun/star/uri/XUriReferenceFactory.hpp>
#include <com/sun/star/uri/XVndSunStarScriptUrl.hpp>
#include <comphelper/processfactory.hxx>
#endif

#include <hash_map>

using ::rtl::OUString;

using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::xml::sax;
using namespace ::xmloff::token;

class XMLTransformerOASISEventMap_Impl:
	public ::std::hash_map< NameKey_Impl, ::rtl::OUString, 
						    NameHash_Impl, NameHash_Impl >
{
public:
	XMLTransformerOASISEventMap_Impl( XMLTransformerEventMapEntry *pInit );
	~XMLTransformerOASISEventMap_Impl();
};

XMLTransformerOASISEventMap_Impl::XMLTransformerOASISEventMap_Impl( XMLTransformerEventMapEntry *pInit )
{
	if( pInit )
	{
		XMLTransformerOASISEventMap_Impl::key_type aKey;
		XMLTransformerOASISEventMap_Impl::mapped_type aData;
		while( pInit->m_pOASISName )
		{
			aKey.m_nPrefix = pInit->m_nOASISPrefix;
			aKey.m_aLocalName = OUString::createFromAscii(pInit->m_pOASISName);

			OSL_ENSURE( find( aKey ) == end(), "duplicate event map entry" );

			aData = OUString::createFromAscii(pInit->m_pOOoName);

			XMLTransformerOASISEventMap_Impl::value_type aVal( aKey, aData );

			insert( aVal );
			++pInit;
		}
	}
}

XMLTransformerOASISEventMap_Impl::~XMLTransformerOASISEventMap_Impl()
{
}

// -----------------------------------------------------------------------------

TYPEINIT1( XMLEventOASISTransformerContext, XMLRenameElemTransformerContext);

XMLEventOASISTransformerContext::XMLEventOASISTransformerContext( 
		XMLTransformerBase& rImp, 
		const OUString& rQName ) :
	XMLRenameElemTransformerContext( rImp, rQName, 
		 rImp.GetNamespaceMap().GetKeyByAttrName( rQName ), XML_EVENT )
{
}

XMLEventOASISTransformerContext::~XMLEventOASISTransformerContext()
{
}

XMLTransformerOASISEventMap_Impl 
	*XMLEventOASISTransformerContext::CreateEventMap()
{
	return new XMLTransformerOASISEventMap_Impl( aTransformerEventMap );
}

XMLTransformerOASISEventMap_Impl 
	*XMLEventOASISTransformerContext::CreateFormEventMap()
{
	return new XMLTransformerOASISEventMap_Impl( aFormTransformerEventMap );
}

void XMLEventOASISTransformerContext::FlushEventMap(
		XMLTransformerOASISEventMap_Impl *p )
{
	delete p;
}

OUString XMLEventOASISTransformerContext::GetEventName( 
		sal_uInt16 nPrefix,
		const OUString& rName,
	   	XMLTransformerOASISEventMap_Impl& rMap,
	   	XMLTransformerOASISEventMap_Impl *pMap2)
{
	XMLTransformerOASISEventMap_Impl::key_type aKey( nPrefix, rName );
	if( pMap2 )
	{
		XMLTransformerOASISEventMap_Impl::const_iterator aIter = 
			pMap2->find( aKey );
		if( !(aIter == pMap2->end()) )
			return (*aIter).second;
	}

	XMLTransformerOASISEventMap_Impl::const_iterator aIter = rMap.find( aKey );
	if( aIter == rMap.end() )
		return rName;
	else
		return (*aIter).second;
}

bool ParseURLAsString(
	const OUString& rAttrValue,
	OUString* pName, OUString* pLocation )
{
	OUString SCHEME( RTL_CONSTASCII_USTRINGPARAM( "vnd.sun.star.script:" ) );

	sal_Int32 params = rAttrValue.indexOf( '?' );
	if ( rAttrValue.indexOf( SCHEME ) != 0 || params < 0 )
	{
		return sal_False;
	}

	sal_Int32 start = SCHEME.getLength();
	*pName = rAttrValue.copy( start, params - start ); 

	OUString aToken;
	OUString aLanguage; 
	params++;
	do
	{
		aToken = rAttrValue.getToken( 0, '&', params );
		sal_Int32 dummy = 0;

		if ( aToken.match( GetXMLToken( XML_LANGUAGE ) ) )
		{
			aLanguage = aToken.getToken( 1, '=', dummy );
		}
		else if ( aToken.match( GetXMLToken( XML_LOCATION ) ) )
		{
			OUString tmp = aToken.getToken( 1, '=', dummy );
			if ( tmp.equalsIgnoreAsciiCase( GetXMLToken( XML_DOCUMENT ) ) )
			{
				*pLocation = GetXMLToken( XML_DOCUMENT );
			}
			else
			{
				*pLocation = GetXMLToken( XML_APPLICATION );
			}
		}
	} while ( params >= 0 );

	if ( aLanguage.equalsIgnoreAsciiCaseAscii( "basic" ) )
	{
		return sal_True;
	}
	return sal_False;
}

bool ParseURL(
	const OUString& rAttrValue,
	OUString* pName, OUString* pLocation )
{
#ifdef OASIS_FILTER_OOO_1X
	return ParseURLAsString( rAttrValue, pName, pLocation ); 
#else
	Reference< com::sun::star::lang::XMultiServiceFactory >
		xSMgr = ::comphelper::getProcessServiceFactory();

	Reference< com::sun::star::uri::XUriReferenceFactory >
		xFactory( xSMgr->createInstance( OUString::createFromAscii(
			"com.sun.star.uri.UriReferenceFactory" ) ), UNO_QUERY );

	if ( xFactory.is() )
	{
		Reference< com::sun::star::uri::XVndSunStarScriptUrl > xUrl (
			xFactory->parse( rAttrValue ), UNO_QUERY );

		if ( xUrl.is() )
		{
			OUString aLanguageKey = GetXMLToken( XML_LANGUAGE );
			if ( xUrl.is() && xUrl->hasParameter( aLanguageKey ) )
			{
				OUString aLanguage = xUrl->getParameter( aLanguageKey );

				if ( aLanguage.equalsIgnoreAsciiCaseAscii( "basic" ) )
				{
					*pName = xUrl->getName();

					OUString tmp =
						xUrl->getParameter( GetXMLToken( XML_LOCATION ) );

					OUString doc = GetXMLToken( XML_DOCUMENT );

					if ( tmp.equalsIgnoreAsciiCase( doc ) )
					{
						*pLocation = doc;
					}
					else
					{
						*pLocation = GetXMLToken( XML_APPLICATION );
					}
					return sal_True;
				}
			}
		}
		return sal_False;
	}
	else
	{
		return ParseURLAsString( rAttrValue, pName, pLocation ); 
	}
#endif
}

void XMLEventOASISTransformerContext::StartElement( 
	const Reference< XAttributeList >& rAttrList )
{
	OSL_TRACE("XMLEventOASISTransformerContext::StartElement");

	XMLTransformerActions *pActions =
		GetTransformer().GetUserDefinedActions( OASIS_EVENT_ACTIONS );
	OSL_ENSURE( pActions, "go no actions" );
	
	Reference< XAttributeList > xAttrList( rAttrList );
	XMLMutableAttributeList *pMutableAttrList = 0;
	sal_Int16 nAttrCount = xAttrList.is() ? xAttrList->getLength() : 0;
	for( sal_Int16 i=0; i < nAttrCount; i++ )
	{
		const OUString& rAttrName = xAttrList->getNameByIndex( i );
		OUString aLocalName;
		sal_uInt16 nPrefix =
			GetTransformer().GetNamespaceMap().GetKeyByAttrName( rAttrName, 
																 &aLocalName );
		XMLTransformerActions::key_type aKey( nPrefix, aLocalName );
		XMLTransformerActions::const_iterator aIter =
			pActions->find( aKey );
		if( !(aIter == pActions->end() ) )
		{
			if( !pMutableAttrList )
			{
				pMutableAttrList = 
						new XMLMutableAttributeList( xAttrList );
				xAttrList = pMutableAttrList;
			}
			const OUString& rAttrValue = xAttrList->getValueByIndex( i );
			switch( (*aIter).second.m_nActionType )
			{
			case XML_ATACTION_HREF:
				{
					OUString aAttrValue( rAttrValue );
					OUString aName, aLocation;

					bool bNeedsTransform =
						ParseURL( rAttrValue, &aName, &aLocation );

					if ( bNeedsTransform )
					{
						pMutableAttrList->RemoveAttributeByIndex( i );

						OUString aAttrQName(
							GetTransformer().GetNamespaceMap().GetQNameByKey(
								XML_NAMESPACE_SCRIPT, 
							::xmloff::token::GetXMLToken( XML_MACRO_NAME ) ) );

						pMutableAttrList->AddAttribute( aAttrQName, aName );

						sal_Int16 idx = pMutableAttrList->GetIndexByName(
							GetTransformer().GetNamespaceMap().GetQNameByKey(
								XML_NAMESPACE_SCRIPT,
							GetXMLToken( XML_LANGUAGE ) ) );

						pMutableAttrList->SetValueByIndex( idx,
							OUString::createFromAscii("StarBasic") );

						OUString aLocQName(
							GetTransformer().GetNamespaceMap().GetQNameByKey(
								XML_NAMESPACE_SCRIPT,
								GetXMLToken( XML_LOCATION ) ) );

						pMutableAttrList->AddAttribute( aLocQName, aLocation );
					}
				}
				break;
			case XML_ATACTION_EVENT_NAME:
				{
					// Check if the event belongs to a form or control by
					// cehcking the 2nd ancestor element, f.i.:
					// <form:button><form:event-listeners><form:event-listener>
					const XMLTransformerContext *pObjContext =
						GetTransformer().GetAncestorContext( 1 );
					sal_Bool bForm = pObjContext && 

						pObjContext->HasNamespace(XML_NAMESPACE_FORM );
					pMutableAttrList->SetValueByIndex( i, 
								   GetTransformer().GetEventName( rAttrValue,
									  							  bForm ) );
				}
				break;
			case XML_ATACTION_REMOVE_NAMESPACE_PREFIX:
				{
					OUString aAttrValue( rAttrValue );
					sal_uInt16 nValPrefix =
						static_cast<sal_uInt16>((*aIter).second.m_nParam1);
					if( GetTransformer().RemoveNamespacePrefix( 
								aAttrValue, nValPrefix ) )
						pMutableAttrList->SetValueByIndex( i, aAttrValue );
				}
				break;
			case XML_ATACTION_MACRO_NAME:
            {
                OUString aName, aLocation;
                bool bNeedsTransform =
                ParseURL( rAttrValue, &aName, &aLocation );
                
                if ( bNeedsTransform )
                {
                    pMutableAttrList->SetValueByIndex( i, aName );
                    
                    sal_Int16 idx = pMutableAttrList->GetIndexByName(
                    GetTransformer().GetNamespaceMap().GetQNameByKey(
                    XML_NAMESPACE_SCRIPT,
                    GetXMLToken( XML_LANGUAGE ) ) );
                    
                    pMutableAttrList->SetValueByIndex( idx,
                    OUString::createFromAscii("StarBasic") );
                    
                    OUString aLocQName(
                    GetTransformer().GetNamespaceMap().GetQNameByKey(
                    XML_NAMESPACE_SCRIPT,
                    GetXMLToken( XML_LOCATION ) ) );
                    
                    pMutableAttrList->AddAttribute( aLocQName, aLocation );
                }
                else
                {
                    const OUString& rApp = GetXMLToken( XML_APPLICATION ); 
                    const OUString& rDoc = GetXMLToken( XML_DOCUMENT ); 
                    OUString aAttrValue;
                    if( rAttrValue.getLength() > rApp.getLength()+1 &&
                        rAttrValue.copy(0,rApp.getLength()).
                            equalsIgnoreAsciiCase( rApp ) &&
                        ':' == rAttrValue[rApp.getLength()] )
                    {
                        aLocation = rApp;
                        aAttrValue = rAttrValue.copy( rApp.getLength()+1 );
                    }
                    else if( rAttrValue.getLength() > rDoc.getLength()+1 &&
                             rAttrValue.copy(0,rDoc.getLength()).
                                equalsIgnoreAsciiCase( rDoc ) &&
                             ':' == rAttrValue[rDoc.getLength()] )
                    {
                        aLocation= rDoc;
                        aAttrValue = rAttrValue.copy( rDoc.getLength()+1 );
                    }
                    if( aAttrValue.getLength() )
                        pMutableAttrList->SetValueByIndex( i, 
                    aAttrValue );
                    if( aLocation.getLength() )
                    {	
                        OUString aAttrQName( GetTransformer().GetNamespaceMap().
                        GetQNameByKey( XML_NAMESPACE_SCRIPT, 
                        ::xmloff::token::GetXMLToken( XML_LOCATION ) ) );
                        pMutableAttrList->AddAttribute( aAttrQName, aLocation );
                        // draw bug
                        aAttrQName = GetTransformer().GetNamespaceMap().
                        GetQNameByKey( XML_NAMESPACE_SCRIPT, 
                        ::xmloff::token::GetXMLToken( XML_LIBRARY ) );
                        pMutableAttrList->AddAttribute( aAttrQName, aLocation );
                    }
                }
            }
            break;
			case XML_ATACTION_COPY:
				break;
			default:
				OSL_ENSURE( sal_False, "unknown action" );
				break;
			}
		}
	}

	XMLRenameElemTransformerContext::StartElement( xAttrList );
}