/**************************************************************
 * 
 * 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_comphelper.hxx"
#include <comphelper/composedprops.hxx>
#include <com/sun/star/container/XChild.hpp>
#include <com/sun/star/beans/XPropertySetInfo.hpp>
#include <cppuhelper/implbase1.hxx>

//.........................................................................
namespace comphelper
{
//.........................................................................

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

	//=====================================================================
	//= OComposedPropertySetInfo
	//=====================================================================
	class OComposedPropertySetInfo : public ::cppu::WeakImplHelper1< XPropertySetInfo >
	{
	private:
		Sequence< Property>		m_aProperties;

	public:
		OComposedPropertySetInfo(const Sequence< Property>& _rProperties);

		virtual Sequence< Property > SAL_CALL getProperties(  ) throw(RuntimeException);
		virtual Property SAL_CALL getPropertyByName( const ::rtl::OUString& _rName ) throw(UnknownPropertyException, RuntimeException);
		virtual sal_Bool SAL_CALL hasPropertyByName( const ::rtl::OUString& _rName ) throw(RuntimeException);
	};

	//=====================================================================
	//= OComposedPropertySet
	//=====================================================================
	//---------------------------------------------------------------------
	OComposedPropertySet::OComposedPropertySet(
			const Sequence< Reference< XPropertySet> > & _rElements,
			const IPropertySetComposerCallback* _pPropertyMetaData)
		:m_pInfo(NULL)
	{
		// copy the sequence
		sal_Int32 nSingleSets = _rElements.getLength();
		if (nSingleSets)
		{
			m_aSingleSets.resize(nSingleSets);
			const Reference< XPropertySet >* pSingleSets = _rElements.getConstArray();
			::std::copy(pSingleSets, pSingleSets + nSingleSets, m_aSingleSets.begin());
		}

		// impl ctor
		compose(_pPropertyMetaData);
	}

	//---------------------------------------------------------------------
	OComposedPropertySet::~OComposedPropertySet()
	{
		if (m_pInfo)
			m_pInfo->release();
	}

	//---------------------------------------------------------------------
	void OComposedPropertySet::compose(const IPropertySetComposerCallback* _pMetaData)
	{
		sal_Int32 nSingleSets = m_aSingleSets.size();

		if (nSingleSets>0)
		{
			// get the properties of the first set
			Reference< XPropertySet > xMasterSet = m_aSingleSets[0];
			Sequence< Property> aMasterProps;
			if (xMasterSet.is())
				aMasterProps = xMasterSet->getPropertySetInfo()->getProperties();
			sal_Int32 nMasterPropsCount = aMasterProps.getLength();
			const Property*	pMasterProps = aMasterProps.getConstArray();

			// check which of the master properties should be included
			Sequence<sal_Bool> aInclusionFlags(nMasterPropsCount);
			sal_Bool* pInclusionFlags = aInclusionFlags.getArray();

			// the states of all these properties
			Sequence< PropertyState > aPropertyStates(nMasterPropsCount);

			for (sal_Int32 i=0; i<nMasterPropsCount; ++i)
				pInclusionFlags[i] = sal_True;

			Reference< XPropertySet >  xSecondarySet;
			sal_Int32 nSecondaryPropertyCount;
			Sequence< Property > aSecondaryProperties;
			const Property* pPrimaryProperty = aMasterProps.getConstArray();
			for (sal_Int32 nPrimary=0; nPrimary<nMasterPropsCount; ++nPrimary, ++pPrimaryProperty)
			{
				if (_pMetaData && !_pMetaData->isComposeable(pPrimaryProperty->Name))
					// do not include this property
					pInclusionFlags[nPrimary] = sal_False;
				else
				{
					// search the property in all secondary sets
					for (sal_Int32 i=1; i<nSingleSets; ++i)
					{
						xSecondarySet = m_aSingleSets[i];
						aSecondaryProperties = xSecondarySet->getPropertySetInfo()->getProperties();
						nSecondaryPropertyCount = aSecondaryProperties.getLength();
						const Property*	pSecondaryProperties = aSecondaryProperties.getConstArray();

						// search the current primary property in the secondary property sequence
						sal_Int32 k=0;
						while (k<nSecondaryPropertyCount && (pSecondaryProperties[k].Name != pPrimaryProperty->Name))
							++k;

						if (k >= nSecondaryPropertyCount)
							// not found -> do not include
							pInclusionFlags[nPrimary] = sal_False;
					}
				}
			}

			// count what's left ....
			sal_Int32 nOverallProperties = 0;
			for (sal_Int32 nCounter=0; nCounter<nMasterPropsCount; ++nCounter)
			{
				if (pInclusionFlags[nCounter]) 
					++nOverallProperties;
			}

			// and finally construct our sequence
			m_aProperties = Sequence< Property >(nOverallProperties);
			Property* pProperties = m_aProperties.getArray();
			const Property* pMasterProperties = pMasterProps;
			sal_Int32 nOwnProperties = 0;
			for (sal_Int32 nCopy = 0; nCopy < nMasterPropsCount; ++nCopy, ++pMasterProperties)
			{
				if (pInclusionFlags[nCopy])
					pProperties[nOwnProperties++] = *pMasterProperties;
			}
		}
	}

	//------------------------------------------------------------------------------
	Reference< XPropertySetInfo > SAL_CALL OComposedPropertySet::getPropertySetInfo(  ) throw(RuntimeException)
	{
		::osl::MutexGuard aGuard(m_aMutex);
		if (!m_pInfo)
		{
			m_pInfo = new OComposedPropertySetInfo(m_aProperties);
			m_pInfo->acquire();
		}
		return m_pInfo;
	}

	//------------------------------------------------------------------------------
	PropertyState SAL_CALL OComposedPropertySet::getPropertyState( const ::rtl::OUString& _rPropertyName ) throw(UnknownPropertyException, RuntimeException)
	{
		// assume DIRECT for the moment
		PropertyState eState = PropertyState_DIRECT_VALUE;

		sal_Int32 nSingleSets = m_aSingleSets.size();
		if (nSingleSets>0)
		{
			// check the master state
			Reference< XPropertySet >  xMasterSet(m_aSingleSets[0]);
			Any aPrimaryValue;
			if (xMasterSet.is())
			{
				Reference< XPropertyState >  xMasterState(xMasterSet,UNO_QUERY);
				aPrimaryValue = xMasterSet->getPropertyValue(_rPropertyName);

				if (xMasterState.is())
					eState = xMasterState->getPropertyState(_rPropertyName);
			}

			// loop through the secondary sets
			PropertyState eSecondaryState;
			for (sal_Int32 i=1; i<nSingleSets; ++i)
			{
				Reference< XPropertySet >	xSecondary(m_aSingleSets[i]);
				Reference< XPropertyState >	xSecondaryState(xSecondary, UNO_QUERY);

				// the secondary state
				eSecondaryState = PropertyState_DIRECT_VALUE;
				if(xSecondaryState.is()) 
					eSecondaryState = xSecondaryState->getPropertyState(_rPropertyName);

				// the secondary value
				Any aSecondaryValue(xSecondary->getPropertyValue(_rPropertyName));

				if	(	(PropertyState_AMBIGUOUS_VALUE == eSecondaryState)		// secondary is ambiguous
					||	!::comphelper::compare(aPrimaryValue, aSecondaryValue)	// unequal values
					)
				{
					eState = PropertyState_AMBIGUOUS_VALUE;
					break;
				}
			}
		}
		else
		{
			throw UnknownPropertyException(  _rPropertyName, *this  );
		}

		return eState;
	}

	//---------------------------------------------------------------------
	Sequence< PropertyState > SAL_CALL OComposedPropertySet::getPropertyStates( const Sequence< ::rtl::OUString >& _rPropertyName ) throw(UnknownPropertyException, RuntimeException)
	{
		sal_Int32 nCount = _rPropertyName.getLength();
		Sequence< PropertyState > aReturn(nCount);
		const ::rtl::OUString* pNames = _rPropertyName.getConstArray();
		PropertyState* pStates = aReturn.getArray();
		for (sal_Int32 i=0; i<nCount; ++i, ++pNames, ++pStates)
			*pStates = getPropertyState(*pNames);
		return aReturn;
	}

	//---------------------------------------------------------------------
	void SAL_CALL OComposedPropertySet::setPropertyToDefault( const ::rtl::OUString& _rPropertyName ) throw(UnknownPropertyException, RuntimeException)
	{
		sal_Int32 nSingleSets = m_aSingleSets.size();
		for (sal_Int32 i=0; i<nSingleSets; ++i)
		{
			Reference< XPropertyState >	xState(m_aSingleSets[i], UNO_QUERY);
			if(xState.is())
				xState->setPropertyToDefault(_rPropertyName);
		}
	}

	//---------------------------------------------------------------------
	Any SAL_CALL OComposedPropertySet::getPropertyDefault( const ::rtl::OUString& ) throw(UnknownPropertyException, WrappedTargetException, RuntimeException)
	{
		return Any();
	}

	//------------------------------------------------------------------------------
	void SAL_CALL OComposedPropertySet::setPropertyValue( const ::rtl::OUString& _rPropertyName, const Any& _rValue ) throw(UnknownPropertyException, PropertyVetoException, IllegalArgumentException, WrappedTargetException, RuntimeException)
	{
		sal_Int32 nSingleSets = m_aSingleSets.size();
		for (sal_Int32 i=0; i<nSingleSets; ++i)
		{
			if (m_aSingleSets[i].is())
				m_aSingleSets[i]->setPropertyValue(_rPropertyName, _rValue);
		}
	}

	//------------------------------------------------------------------------------
	Any SAL_CALL OComposedPropertySet::getPropertyValue( const ::rtl::OUString& _rPropertyName ) throw(UnknownPropertyException, WrappedTargetException, RuntimeException)
	{
		sal_Int32 nSingleSets = m_aSingleSets.size();

		if ((nSingleSets>0) && (m_aSingleSets[0].is()))
			return m_aSingleSets[0]->getPropertyValue(_rPropertyName);
		return Any();
	}

	//------------------------------------------------------------------------------
	void SAL_CALL OComposedPropertySet::addPropertyChangeListener( const ::rtl::OUString&, const Reference< XPropertyChangeListener >& ) throw(UnknownPropertyException, WrappedTargetException, RuntimeException)
	{
		// TODO:
		// hold the single property sets weak
		// be a property change listener on all single property sets (for all composed properties)
		// upon property change
		//   determine the new state/value of the composed property
		//   broadcast the new composed property value
	}

	//------------------------------------------------------------------------------
	void SAL_CALL OComposedPropertySet::removePropertyChangeListener( const ::rtl::OUString&, const Reference< XPropertyChangeListener >& ) throw(UnknownPropertyException, WrappedTargetException, RuntimeException)
	{
		// TODO
	}

	//------------------------------------------------------------------------------
	void SAL_CALL OComposedPropertySet::addVetoableChangeListener( const ::rtl::OUString&, const Reference< XVetoableChangeListener >& ) throw(UnknownPropertyException, WrappedTargetException, RuntimeException)
	{
		OSL_ENSURE(sal_False, "OComposedPropertySet::addVetoableChangeListener: no implemented (yet)!");
	}

	//------------------------------------------------------------------------------
	void SAL_CALL OComposedPropertySet::removeVetoableChangeListener( const ::rtl::OUString&, const Reference< XVetoableChangeListener >& ) throw(UnknownPropertyException, WrappedTargetException, RuntimeException)
	{
		OSL_ENSURE(sal_False, "OComposedPropertySet::removeVetoableChangeListener: no implemented (yet)!");
	}

	//------------------------------------------------------------------------------
	OComposedPropertySetInfo::OComposedPropertySetInfo(const Sequence< Property>& rSeq)
		:m_aProperties(rSeq)
	{
	}

	//------------------------------------------------------------------------------
	Sequence< Property> SAL_CALL OComposedPropertySetInfo::getProperties() throw(RuntimeException)
	{
		return m_aProperties;
	}

	//------------------------------------------------------------------------------
	Property SAL_CALL OComposedPropertySetInfo::getPropertyByName( const ::rtl::OUString& _rName ) throw(UnknownPropertyException, RuntimeException)
	{
		sal_Int32 nLength = m_aProperties.getLength();
		const Property*	pProps = m_aProperties.getConstArray();
		// TODO TODO TODO: this O(n) search really sucks ...
		for (sal_Int32 i=0; i<nLength; ++i, ++pProps)
		{
			if (pProps->Name == _rName)
				return *pProps;
		}

		throw UnknownPropertyException( _rName, *this  );
	}

	//------------------------------------------------------------------------------
	sal_Bool SAL_CALL OComposedPropertySetInfo::hasPropertyByName( const ::rtl::OUString& _rName ) throw(RuntimeException)
	{
		sal_Int32 nLength = m_aProperties.getLength();
		const Property*	pProps = m_aProperties.getConstArray();
		// TODO TODO TODO: this O(n) search really sucks ...
		for( sal_Int32 i=0; i<nLength; ++i,++pProps )
		{
			if(pProps->Name == _rName) 
				return sal_True;
		}

		return sal_False;
	}

//.........................................................................
}	// namespace comphelper
//.........................................................................