/**************************************************************
 * 
 * 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 <xmloff/XMLEventExport.hxx>

#ifndef _COM_SUN_STAR_BEANS_PROPERTYVALUE_HPP
#include <com/sun/star/beans/PropertyValue.hpp>
#endif

#ifndef _COM_SUN_STAR_DOCUMENT_XEVENTSSUPPLIER_HPP
#include <com/sun/star/document/XEventsSupplier.hpp>
#endif

#ifndef _COM_SUN_STAR_CONTAINER_XNAMEREPLACE_HPP
#include <com/sun/star/container/XNameReplace.hpp>
#endif
#include <tools/debug.hxx>
#include <xmloff/xmlexp.hxx>
#include <xmloff/xmltoken.hxx>
#include "xmloff/xmlnmspe.hxx"
#include <xmloff/nmspmap.hxx>


using namespace ::com::sun::star::uno;

using std::map;
using ::rtl::OUString;
using ::com::sun::star::beans::PropertyValue;
using ::com::sun::star::document::XEventsSupplier;
using ::com::sun::star::container::XNameReplace;
using ::com::sun::star::container::XNameAccess;
using ::xmloff::token::GetXMLToken;
using ::xmloff::token::XML_EVENT_LISTENERS;


XMLEventExport::XMLEventExport(SvXMLExport& rExp,
						 const XMLEventNameTranslation* pTranslationTable) :
	sEventType(RTL_CONSTASCII_USTRINGPARAM("EventType")),
    rExport(rExp),
    bExtNamespace(false)
{
	AddTranslationTable(pTranslationTable);
}

XMLEventExport::~XMLEventExport()
{
	// delete all handlers
	HandlerMap::iterator aEnd = aHandlerMap.end();
	for( HandlerMap::iterator aIter =
			 aHandlerMap.begin();
		 aIter != aEnd;
		 aIter++ )
	{
		delete aIter->second;
	}
	aHandlerMap.clear();
}

void XMLEventExport::AddHandler( const OUString& rName,
								 XMLEventExportHandler* pHandler )
{
	DBG_ASSERT(pHandler != NULL, "Need EventExportHandler");
	if (pHandler != NULL)
	{
		aHandlerMap[rName] = pHandler;
	}
}

void XMLEventExport::AddTranslationTable(
	const XMLEventNameTranslation* pTransTable )
{
	if (NULL != pTransTable)
	{
		// put translation table into map
		for(const XMLEventNameTranslation* pTrans = pTransTable;
			pTrans->sAPIName != NULL;
			pTrans++)
		{
			aNameTranslationMap[OUString::createFromAscii(pTrans->sAPIName)] =
				XMLEventName(pTrans->nPrefix, pTrans->sXMLName);
		}
	}
	// else? ignore!
}

void XMLEventExport::Export( Reference<XEventsSupplier> & rSupplier,
							 sal_Bool bWhitespace)
{
	if (rSupplier.is())
	{
		Reference<XNameAccess> xAccess(rSupplier->getEvents(), UNO_QUERY);
		Export(xAccess, bWhitespace);
	}
	// else: no supplier, no export -> ignore!
}

void XMLEventExport::Export( Reference<XNameReplace> & rReplace,
							 sal_Bool bWhitespace)
{
	Reference<XNameAccess> xAccess(rReplace, UNO_QUERY);
	Export(xAccess, bWhitespace);
}

void XMLEventExport::Export( Reference<XNameAccess> & rAccess,
							 sal_Bool bWhitespace)
{
	// early out if we don't actually get any events
	if (!rAccess.is())
	{
		return;
	}

	// have we already processed an element?
	sal_Bool bStarted = sal_False;

	// iterate over all event types
	Sequence<OUString> aNames = rAccess->getElementNames();
	sal_Int32 nCount = aNames.getLength();
	for(sal_Int32 i = 0; i < nCount; i++)
	{
		// translate name
        NameMap::iterator aIter = aNameTranslationMap.find(aNames[i]);
		if (aIter != aNameTranslationMap.end())
		{
			const XMLEventName& rXmlName = aIter->second;

			// get PropertyValues for this event
			Any aAny = rAccess->getByName( aNames[i] );
			Sequence<PropertyValue> aValues;
			aAny >>= aValues;

            // now export the current event
            ExportEvent( aValues, rXmlName, bWhitespace, bStarted );
		}
#ifdef DBG_UTIL
		else
		{
			// don't proceed further
            ::rtl::OString aStr("Unknown event name:" );
            aStr += ::rtl::OUStringToOString( aNames[i], RTL_TEXTENCODING_UTF8 );
            DBG_ERROR( aStr.getStr() );
		}
#endif
	}

	// close <script:events> element (if it was opened before)
	if (bStarted)
	{
		EndElement(bWhitespace);
	}
}

void XMLEventExport::ExportExt( Reference<XNameAccess> & rAccess,
                                sal_Bool bWhitespace )
{
    // set bExtNamespace flag to use XML_NAMESPACE_OFFICE_EXT namespace
    // for events element (not for child elements)
    bExtNamespace = true;
    Export(rAccess, bWhitespace);
    bExtNamespace = false;          // reset for future Export calls
}

/// export a singular event and write <office:events> container
void XMLEventExport::ExportSingleEvent(
    Sequence<PropertyValue>& rEventValues,
    const OUString& rApiEventName,
    sal_Bool bUseWhitespace )
{
    // translate the name
    NameMap::iterator aIter = aNameTranslationMap.find(rApiEventName);
    if (aIter != aNameTranslationMap.end())
	{
        const XMLEventName& rXmlName = aIter->second;

        // export the event ...
        sal_Bool bStarted = sal_False;
        ExportEvent( rEventValues, rXmlName, bUseWhitespace, bStarted );

        // ... and close the container element (if necessary)
        if (bStarted)
        {
            EndElement(bUseWhitespace);
        }
    }
#ifdef DBG_UTIL
    else
    {
        // don't proceed further
        ::rtl::OString aStr("Unknown event name:" );
        aStr += ::rtl::OUStringToOString( rApiEventName, RTL_TEXTENCODING_UTF8 );
        DBG_ERROR( aStr.getStr() );
    }
#endif
}


/// export a single event
void XMLEventExport::ExportEvent(
    Sequence<PropertyValue>& rEventValues,
    const XMLEventName& rXmlEventName,
    sal_Bool bUseWhitespace,
    sal_Bool& rExported )
{
    // search for EventType value and then delegate to EventHandler
    sal_Int32 nValues = rEventValues.getLength();
    const PropertyValue* pValues = rEventValues.getConstArray();

    for(sal_Int32 nVal = 0; nVal < nValues; nVal++)
    {
        if (sEventType.equals(pValues[nVal].Name))
        {
            // found! Now find handler and delegate
            OUString sType;
            pValues[nVal].Value >>= sType;

            if (aHandlerMap.count(sType))
            {
                if (! rExported)
                {
                    // OK, we haven't yet exported the enclosing
                    // element. So we do that now.
                    rExported = sal_True;
                    StartElement(bUseWhitespace);
                }

				OUString aEventQName(
					rExport.GetNamespaceMap().GetQNameByKey(
							rXmlEventName.m_nPrefix, rXmlEventName.m_aName ) );

                // delegate to proper ExportEventHandler
                aHandlerMap[sType]->Export(rExport, aEventQName,
                                           rEventValues, bUseWhitespace);
            }
            else
            {
                if (! sType.equalsAsciiL("None", sizeof("None")-1))
                {
                    DBG_ERROR("unknown event type returned by API");
                    // unknown type -> error (ignore)
                }
                // else: we ignore None fields
            }

            // early out: we don't need to look for another type
            break;
        }
        // else: we only care for EventType -> ignore
    }
}


void XMLEventExport::StartElement(sal_Bool bWhitespace)
{
	if (bWhitespace)
    {
		rExport.IgnorableWhitespace();
    }
    sal_uInt16 nNamespace = bExtNamespace ? XML_NAMESPACE_OFFICE_EXT
                                          : XML_NAMESPACE_OFFICE;
    rExport.StartElement( nNamespace, XML_EVENT_LISTENERS,
						  bWhitespace);
}

void XMLEventExport::EndElement(sal_Bool bWhitespace)
{
    sal_uInt16 nNamespace = bExtNamespace ? XML_NAMESPACE_OFFICE_EXT
                                          : XML_NAMESPACE_OFFICE;
    rExport.EndElement(nNamespace, XML_EVENT_LISTENERS, bWhitespace);
	if (bWhitespace)
	{
		rExport.IgnorableWhitespace();
	}
}


// implement aStandardEventTable (defined in xmlevent.hxx)
const XMLEventNameTranslation aStandardEventTable[] =
{
	{ "OnSelect", 			XML_NAMESPACE_DOM, "select" }, // "on-select"
	{ "OnInsertStart",		XML_NAMESPACE_OFFICE, "insert-start" }, // "on-insert-start"
	{ "OnInsertDone",		XML_NAMESPACE_OFFICE, "insert-done" }, // "on-insert-done"
	{ "OnMailMerge",		XML_NAMESPACE_OFFICE, "mail-merge" }, // "on-mail-merge"
	{ "OnAlphaCharInput",	XML_NAMESPACE_OFFICE, "alpha-char-input" }, // "on-alpha-char-input"
	{ "OnNonAlphaCharInput",	XML_NAMESPACE_OFFICE, "non-alpha-char-input" }, // "on-non-alpha-char-input"
	{ "OnResize",			XML_NAMESPACE_DOM, "resize" }, // "on-resize"
	{ "OnMove",				XML_NAMESPACE_OFFICE, "move" }, // "on-move"
	{ "OnPageCountChange",	XML_NAMESPACE_OFFICE, "page-count-change" }, // "on-page-count-change"
	{ "OnMouseOver",		XML_NAMESPACE_DOM, "mouseover" }, // "on-mouse-over"
	{ "OnClick",			XML_NAMESPACE_DOM, "click" }, // "on-click"
	{ "OnMouseOut",			XML_NAMESPACE_DOM, "mouseout" }, // "on-mouse-out"
	{ "OnLoadError",		XML_NAMESPACE_OFFICE, "load-error" }, // "on-load-error"
	{ "OnLoadCancel",		XML_NAMESPACE_OFFICE, "load-cancel" }, // "on-load-cancel"
	{ "OnLoadDone",			XML_NAMESPACE_OFFICE, "load-done" }, // "on-load-done"
	{ "OnLoad",				XML_NAMESPACE_DOM, "load" }, // "on-load"
	{ "OnUnload",			XML_NAMESPACE_DOM, "unload" }, // "on-unload"
	{ "OnStartApp",			XML_NAMESPACE_OFFICE, "start-app" }, // "on-start-app"
	{ "OnCloseApp",			XML_NAMESPACE_OFFICE, "close-app" }, // "on-close-app"
	{ "OnNew",				XML_NAMESPACE_OFFICE, "new" }, // "on-new"
	{ "OnSave",				XML_NAMESPACE_OFFICE, "save" }, // "on-save"
	{ "OnSaveAs",			XML_NAMESPACE_OFFICE, "save-as" }, // "on-save-as"
	{ "OnFocus",			XML_NAMESPACE_DOM, "DOMFocusIn" }, // "on-focus"
	{ "OnUnfocus",			XML_NAMESPACE_DOM, "DOMFocusOut" }, // "on-unfocus"
	{ "OnPrint",			XML_NAMESPACE_OFFICE, "print" }, // "on-print"
	{ "OnError",			XML_NAMESPACE_DOM, "error" }, // "on-error"
	{ "OnLoadFinished",		XML_NAMESPACE_OFFICE, "load-finished" }, // "on-load-finished"
	{ "OnSaveFinished",		XML_NAMESPACE_OFFICE, "save-finished" }, // "on-save-finished"
	{ "OnModifyChanged",	XML_NAMESPACE_OFFICE, "modify-changed" }, // "on-modify-changed"
	{ "OnPrepareUnload",	XML_NAMESPACE_OFFICE, "prepare-unload" }, // "on-prepare-unload"
	{ "OnNewMail",			XML_NAMESPACE_OFFICE, "new-mail" }, // "on-new-mail"
	{ "OnToggleFullscreen",	XML_NAMESPACE_OFFICE, "toggle-fullscreen" }, // "on-toggle-fullscreen"
	{ "OnSaveDone", 		XML_NAMESPACE_OFFICE, "save-done" }, // "on-save-done"
	{ "OnSaveAsDone",		XML_NAMESPACE_OFFICE, "save-as-done" }, // "on-save-as-done"
	{ "OnCopyTo",			XML_NAMESPACE_OFFICE, "copy-to" },
	{ "OnCopyToDone",		XML_NAMESPACE_OFFICE, "copy-to-done" },
	{ "OnViewCreated",		XML_NAMESPACE_OFFICE, "view-created" },
	{ "OnPrepareViewClosing", XML_NAMESPACE_OFFICE, "prepare-view-closing" },
	{ "OnViewClosed",       XML_NAMESPACE_OFFICE, "view-close" },
    { "OnVisAreaChanged",	XML_NAMESPACE_OFFICE, "visarea-changed" }, // "on-visarea-changed"
    { "OnCreate",           XML_NAMESPACE_OFFICE, "create" },
    { "OnSaveAsFailed",     XML_NAMESPACE_OFFICE, "save-as-failed" },
    { "OnSaveFailed",       XML_NAMESPACE_OFFICE, "save-failed" },
    { "OnCopyToFailed",     XML_NAMESPACE_OFFICE, "copy-to-failed" },
    { "OnTitleChanged",     XML_NAMESPACE_OFFICE, "title-changed" },
    { "OnModeChanged",      XML_NAMESPACE_OFFICE, "mode-changed" },
    { "OnSaveTo",           XML_NAMESPACE_OFFICE, "save-to" },
    { "OnSaveToDone",       XML_NAMESPACE_OFFICE, "save-to-done" },
    { "OnSaveToFailed",     XML_NAMESPACE_OFFICE, "save-to-failed" },
    { "OnSubComponentOpened",   XML_NAMESPACE_OFFICE, "subcomponent-opened" },
    { "OnSubComponentClosed",   XML_NAMESPACE_OFFICE, "subcomponent-closed" },
    { "OnStorageChanged",       XML_NAMESPACE_OFFICE, "storage-changed" },
    { "OnMailMergeFinished",    XML_NAMESPACE_OFFICE, "mail-merge-finished" },
    { "OnFieldMerge",           XML_NAMESPACE_OFFICE, "field-merge" },
    { "OnFieldMergeFinished",   XML_NAMESPACE_OFFICE, "field-merge-finished" },
    { "OnLayoutFinished",       XML_NAMESPACE_OFFICE, "layout-finished" },
    { "OnDoubleClick",      XML_NAMESPACE_OFFICE, "dblclick" },
    { "OnRightClick",       XML_NAMESPACE_OFFICE, "contextmenu" },
    { "OnChange",           XML_NAMESPACE_OFFICE, "content-changed" },
    { "OnCalculate",        XML_NAMESPACE_OFFICE, "calculated" },

	{ NULL, 0, 0 }
};