/**************************************************************
 * 
 * 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_svtools.hxx"

#include <com/sun/star/beans/PropertyValue.hpp>
#include <rtl/ustrbuf.hxx>
#include <tools/rtti.hxx>
#include <tools/solar.h>
#include <svtools/unoevent.hxx>
#include <svl/macitem.hxx>

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

using ::com::sun::star::container::NoSuchElementException;
using ::com::sun::star::container::XNameReplace;
using ::com::sun::star::lang::IllegalArgumentException;
using ::com::sun::star::lang::WrappedTargetException;
using ::com::sun::star::lang::XServiceInfo;
using ::com::sun::star::beans::PropertyValue;
using ::cppu::WeakImplHelper2;
using ::rtl::OUString;
using ::rtl::OUStringBuffer;


const sal_Char sAPI_ServiceName[] = "com.sun.star.container.XNameReplace";
const sal_Char sAPI_SvDetachedEventDescriptor[] = "SvDetachedEventDescriptor";

//
// SvBaseEventDescriptor
//

SvBaseEventDescriptor::SvBaseEventDescriptor( const SvEventDescription* pSupportedMacroItems ) :
		sEventType(RTL_CONSTASCII_USTRINGPARAM("EventType")),
		sMacroName(RTL_CONSTASCII_USTRINGPARAM("MacroName")),
		sLibrary(RTL_CONSTASCII_USTRINGPARAM("Library")),
		sStarBasic(RTL_CONSTASCII_USTRINGPARAM("StarBasic")),
		sJavaScript(RTL_CONSTASCII_USTRINGPARAM("JavaScript")),
		sScript(RTL_CONSTASCII_USTRINGPARAM("Script")),
		sNone(RTL_CONSTASCII_USTRINGPARAM("None")),
		sServiceName(RTL_CONSTASCII_USTRINGPARAM(sAPI_ServiceName)),
		sEmpty(),
		mpSupportedMacroItems(pSupportedMacroItems),
		mnMacroItems(0)
{
	DBG_ASSERT(pSupportedMacroItems != NULL, "Need a list of supported events!");

	for( ; mpSupportedMacroItems[mnMacroItems].mnEvent != 0; mnMacroItems++) ;
}


SvBaseEventDescriptor::~SvBaseEventDescriptor()
{
}

void SvBaseEventDescriptor::replaceByName(
	const OUString& rName,
	const Any& rElement )
	throw(
		IllegalArgumentException,
		NoSuchElementException,
		WrappedTargetException,
		RuntimeException)
{
	sal_uInt16 nMacroID = getMacroID(rName);

	// error checking
	if (0 == nMacroID)
		throw NoSuchElementException();
	if (rElement.getValueType() != getElementType())
		throw IllegalArgumentException();

	// get sequence
	Sequence<PropertyValue> aSequence;
	rElement >>= aSequence;

	// perform replace (in subclass)
	SvxMacro aMacro(sEmpty,sEmpty);
	getMacroFromAny(aMacro, rElement);
	replaceByName(nMacroID, aMacro);
}

Any SvBaseEventDescriptor::getByName(
	const OUString& rName )
	throw(
		NoSuchElementException,
		WrappedTargetException,
		RuntimeException)
{
	sal_uInt16 nMacroID = getMacroID(rName);

	// error checking
	if (0 == nMacroID)
		throw NoSuchElementException();

	// perform get (in subclass)
	Any aAny;
	SvxMacro aMacro( sEmpty, sEmpty );
	getByName(aMacro, nMacroID);
	getAnyFromMacro(aAny, aMacro);
	return aAny;
}

Sequence<OUString> SvBaseEventDescriptor::getElementNames()
	throw(RuntimeException)
{
	// create and fill sequence
	Sequence<OUString> aSequence(mnMacroItems);
	for( sal_Int16 i = 0; i < mnMacroItems; i++)
	{
		aSequence[i] = OUString::createFromAscii( mpSupportedMacroItems[i].mpEventName );
	}

	return aSequence;
}

sal_Bool SvBaseEventDescriptor::hasByName(
	const OUString& rName )
	throw(RuntimeException)
{
	sal_uInt16 nMacroID = getMacroID(rName);
	return (nMacroID != 0);
}

Type SvBaseEventDescriptor::getElementType()
	throw(RuntimeException)
{
	return ::getCppuType((Sequence<PropertyValue> *)0);
}

sal_Bool SvBaseEventDescriptor::hasElements()
	throw(RuntimeException)
{
	return mnMacroItems != 0;
}

sal_Bool SvBaseEventDescriptor::supportsService(const OUString& rServiceName)
	throw(RuntimeException)
{
	return sServiceName.equals(rServiceName);
}

Sequence<OUString> SvBaseEventDescriptor::getSupportedServiceNames(void)
	throw(RuntimeException)
{
	Sequence<OUString> aSequence(1);
	aSequence[0] = sServiceName;

	return aSequence;
}

sal_uInt16 SvBaseEventDescriptor::mapNameToEventID(const OUString& rName) const
{
	// iterate over known event names
	for(sal_Int16 i = 0; i < mnMacroItems; i++)
	{
		if (0 == rName.compareToAscii(mpSupportedMacroItems[i].mpEventName))
		{
			return mpSupportedMacroItems[i].mnEvent;
		}
	}

	// not found -> return zero
	return 0;
}

OUString SvBaseEventDescriptor::mapEventIDToName(sal_uInt16 nPoolID) const
{
	// iterate over known event IDs
	for(sal_Int16 i = 0; i < mnMacroItems; i++)
	{
		if (nPoolID == mpSupportedMacroItems[i].mnEvent)
		{
			return OUString::createFromAscii(mpSupportedMacroItems[i].mpEventName);
		}
	}

	// not found -> return empty string
	return OUString();
}

sal_uInt16 SvBaseEventDescriptor::getMacroID(const OUString& rName) const
{
	return mapNameToEventID(rName);
}

void SvBaseEventDescriptor::getAnyFromMacro(Any& rAny,
									   const SvxMacro& rMacro)
{
	sal_Bool bRetValueOK = sal_False;	// do we have a ret value?

	if (rMacro.HasMacro())
	{
		switch (rMacro.GetScriptType())
		{
			case STARBASIC:
			{
				// create sequence
				Sequence<PropertyValue> aSequence(3);
				Any aTmp;

				// create type
				PropertyValue aTypeValue;
				aTypeValue.Name = sEventType;
				aTmp <<= sStarBasic;
				aTypeValue.Value = aTmp;
				aSequence[0] = aTypeValue;

				// macro name
				PropertyValue aNameValue;
				aNameValue.Name = sMacroName;
				OUString sNameTmp(rMacro.GetMacName());
				aTmp <<= sNameTmp;
				aNameValue.Value = aTmp;
				aSequence[1] = aNameValue;

				// library name
				PropertyValue aLibValue;
				aLibValue.Name = sLibrary;
				OUString sLibTmp(rMacro.GetLibName());
				aTmp <<= sLibTmp;
				aLibValue.Value = aTmp;
				aSequence[2] = aLibValue;

				rAny <<= aSequence;
				bRetValueOK = sal_True;
				break;
			}
			case EXTENDED_STYPE:
			{   
				// create sequence
				Sequence<PropertyValue> aSequence(2);
				Any aTmp;

				// create type
				PropertyValue aTypeValue;
				aTypeValue.Name = sEventType;
				aTmp <<= sScript;
				aTypeValue.Value = aTmp;
				aSequence[0] = aTypeValue;

				// macro name
				PropertyValue aNameValue;
				aNameValue.Name = sScript;
				OUString sNameTmp(rMacro.GetMacName());
				aTmp <<= sNameTmp;
				aNameValue.Value = aTmp;
				aSequence[1] = aNameValue;

				rAny <<= aSequence;
				bRetValueOK = sal_True;
				break;
                        }
			case JAVASCRIPT:
			default:
				DBG_ERROR("not implemented");
		}
	}
	// else: bRetValueOK not set

	// if we don't have a return value, make an empty one
	if (! bRetValueOK)
	{
		// create "None" macro
		Sequence<PropertyValue> aSequence(1);

		PropertyValue aKindValue;
		aKindValue.Name = sEventType;
		Any aTmp;
		aTmp <<= sNone;
		aKindValue.Value = aTmp;
		aSequence[0] = aKindValue;

		rAny <<= aSequence;
		bRetValueOK = sal_True;
	}
}


void SvBaseEventDescriptor::getMacroFromAny(
	SvxMacro& rMacro,
	const Any& rAny)
		throw ( IllegalArgumentException )
{
	// get sequence
	Sequence<PropertyValue> aSequence;
	rAny >>= aSequence;

	// process ...
	sal_Bool bTypeOK = sal_False;
	sal_Bool bNone = sal_False;		// true if EventType=="None"
	enum ScriptType eType = EXTENDED_STYPE;
	OUString sScriptVal;
	OUString sMacroVal;
	OUString sLibVal;
	sal_Int32 nCount = aSequence.getLength();
	for (sal_Int32 i = 0; i < nCount; i++)
	{
		PropertyValue& aValue = aSequence[i];
		if (aValue.Name.equals(sEventType))
		{
			OUString sTmp;
			aValue.Value >>= sTmp;
			if (sTmp.equals(sStarBasic))
			{
				eType = STARBASIC;
				bTypeOK = sal_True;
			}
			else if (sTmp.equals(sJavaScript))
			{
				eType = JAVASCRIPT;
				bTypeOK = sal_True;
			}
			else if (sTmp.equals(sScript))
			{
				eType = EXTENDED_STYPE;
				bTypeOK = sal_True;
			}
			else if (sTmp.equals(sNone))
			{
				bNone = sal_True;
				bTypeOK = sal_True;
			}
			// else: unknown script type
		}
		else if (aValue.Name.equals(sMacroName))
		{
			aValue.Value >>= sMacroVal;
		}
		else if (aValue.Name.equals(sLibrary))
		{
			aValue.Value >>= sLibVal;
		}
		else if (aValue.Name.equals(sScript))
		{
			aValue.Value >>= sScriptVal;
		}
		// else: unknown PropertyValue -> ignore
	}

	if (bTypeOK)
	{
		if (bNone)
		{
			// return empty macro
			rMacro = SvxMacro( sEmpty, sEmpty );
		}
		else
		{
			if (eType == STARBASIC)
			{
				// create macro and return
				SvxMacro aMacro(sMacroVal, sLibVal, eType);
				rMacro = aMacro;
			}
			else if (eType == EXTENDED_STYPE) 
			{
				SvxMacro aMacro(sScriptVal, sScript);
				rMacro = aMacro;
			}
			else
			{
				// we can't process type: abort
				// TODO: JavaScript macros
				throw IllegalArgumentException();
			}
		}
	}
	else
	{
		// no valid type: abort
		throw IllegalArgumentException();
	}
}




//
// SvEventDescriptor
//


SvEventDescriptor::SvEventDescriptor(
	XInterface& rParent,
	const SvEventDescription* pSupportedMacroItems) :
		SvBaseEventDescriptor(pSupportedMacroItems),
		xParentRef(&rParent)
{
}


SvEventDescriptor::~SvEventDescriptor()
{
	// automatically release xParentRef !
}

void SvEventDescriptor::replaceByName( 
	const sal_uInt16 nEvent,
	const SvxMacro& rMacro)
		throw(
			IllegalArgumentException, 
			NoSuchElementException, 
			WrappedTargetException, 
			RuntimeException)
{
	SvxMacroItem aItem(getMacroItemWhich());
	aItem.SetMacroTable(getMacroItem().GetMacroTable());
	aItem.SetMacro(nEvent, rMacro);
	setMacroItem(aItem);
}

void SvEventDescriptor::getByName( 
	SvxMacro& rMacro,
	const sal_uInt16 nEvent )
		throw(
			NoSuchElementException, 
			WrappedTargetException, 
			RuntimeException)
{
	const SvxMacroItem& rItem = getMacroItem();
	if( rItem.HasMacro( nEvent ) )
		rMacro = rItem.GetMacro(nEvent);
	else
	{
		SvxMacro aEmptyMacro(sEmpty, sEmpty);
		rMacro = aEmptyMacro;
	}
}




//
// SvDetachedEventDescriptor
//

SvDetachedEventDescriptor::SvDetachedEventDescriptor(
	const SvEventDescription* pSupportedMacroItems) : 
	SvBaseEventDescriptor(pSupportedMacroItems),
	sImplName(RTL_CONSTASCII_USTRINGPARAM(sAPI_SvDetachedEventDescriptor))
{
	// allocate aMacros
	aMacros = new SvxMacro*[mnMacroItems];

	// ... and initialize
	for(sal_Int16 i = 0; i < mnMacroItems; i++)
	{
		aMacros[i] = NULL;
	}
}

SvDetachedEventDescriptor::~SvDetachedEventDescriptor()
{
	// delete contents of aMacros
	for(sal_Int16 i = 0; i < mnMacroItems; i++)
	{
		if (NULL != aMacros[i])
			delete aMacros[i];
	}

    delete [] aMacros;
}

sal_Int16 SvDetachedEventDescriptor::getIndex(const sal_uInt16 nID) const
{
	// iterate over supported events
	sal_Int16 nIndex = 0;
	while ( (mpSupportedMacroItems[nIndex].mnEvent != nID) && 
			(mpSupportedMacroItems[nIndex].mnEvent != 0)      )
	{
		nIndex++;
	}
	return (mpSupportedMacroItems[nIndex].mnEvent == nID) ? nIndex : -1;
}

OUString SvDetachedEventDescriptor::getImplementationName() 
	throw( ::com::sun::star::uno::RuntimeException )
{
	return sImplName;
}


void SvDetachedEventDescriptor::replaceByName( 
	const sal_uInt16 nEvent,
	const SvxMacro& rMacro)
	throw(
		IllegalArgumentException, 
		NoSuchElementException, 
		WrappedTargetException, 
		RuntimeException)
{
	sal_Int16 nIndex = getIndex(nEvent);
	if (-1 == nIndex)
		throw IllegalArgumentException();

	aMacros[nIndex] = new SvxMacro(rMacro.GetMacName(), rMacro.GetLibName(),
								   rMacro.GetScriptType() );
}


void SvDetachedEventDescriptor::getByName( 
	SvxMacro& rMacro,
	const sal_uInt16 nEvent )
	throw(
		NoSuchElementException, 
		WrappedTargetException, 
		RuntimeException)
{
	sal_Int16 nIndex = getIndex(nEvent);
	if (-1 == nIndex )
		throw NoSuchElementException();

	if( aMacros[nIndex] )
		rMacro = (*aMacros[nIndex]);
}

sal_Bool SvDetachedEventDescriptor::hasByName( 
	const sal_uInt16 nEvent ) const		/// item ID of event
		throw(IllegalArgumentException)
{
	sal_Int16 nIndex = getIndex(nEvent);
	if (-1 == nIndex)
		throw IllegalArgumentException();

	return (NULL == aMacros[nIndex]) ? sal_False : aMacros[nIndex]->HasMacro();
}


//
// SvMacroTableEventDescriptor
//

SvMacroTableEventDescriptor::SvMacroTableEventDescriptor(const SvEventDescription* pSupportedMacroItems) :
	SvDetachedEventDescriptor(pSupportedMacroItems)
{
}

SvMacroTableEventDescriptor::SvMacroTableEventDescriptor(
	const SvxMacroTableDtor& rMacroTable,
	const SvEventDescription* pSupportedMacroItems) :
		SvDetachedEventDescriptor(pSupportedMacroItems)
{
	copyMacrosFromTable(rMacroTable);
}

SvMacroTableEventDescriptor::~SvMacroTableEventDescriptor()
{
}

void SvMacroTableEventDescriptor::copyMacrosFromTable(
	const SvxMacroTableDtor& rMacroTable)
{
	for(sal_Int16 i = 0; mpSupportedMacroItems[i].mnEvent != 0; i++)
	{
		const sal_uInt16 nEvent = mpSupportedMacroItems[i].mnEvent;
		const SvxMacro* pMacro = rMacroTable.Get(nEvent);
		if (NULL != pMacro)
			replaceByName(nEvent, *pMacro);
	}

}

void SvMacroTableEventDescriptor::copyMacrosIntoTable(
	SvxMacroTableDtor& rMacroTable)
{
	for(sal_Int16 i = 0; mpSupportedMacroItems[i].mnEvent != 0; i++)
	{
		const sal_uInt16 nEvent = mpSupportedMacroItems[i].mnEvent;
		if (hasByName(nEvent))
		{
			SvxMacro* pMacro = new SvxMacro(sEmpty, sEmpty);
			getByName(*pMacro, nEvent);
			rMacroTable.Insert(nEvent, pMacro);
		}
	}
}



