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

#include <ReportControllerObserver.hxx>
#include <ReportController.hxx>
#include <svl/smplhint.hxx>
#include <vos/mutex.hxx>
#include <vcl/svapp.hxx>
#include <com/sun/star/report/XFormattedField.hpp>
#include <com/sun/star/awt/FontSlant.hpp>
#include <FormattedFieldBeautifier.hxx>

#include <svx/unopage.hxx>

// DBG_*
#include <tools/debug.hxx>
// DBG_UNHANDLED_EXCEPTION
#include <tools/diagnose_ex.h>

namespace rptui
{

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

    // const OReportController *& m_pReportController;

DECLARE_STL_USTRINGACCESS_MAP(bool, AllProperties);
DECLARE_STL_STDKEY_MAP(uno::Reference< beans::XPropertySet >, AllProperties, PropertySetInfoCache);

class OXReportControllerObserverImpl
{
    OXReportControllerObserverImpl(OXReportControllerObserverImpl&);
    void operator =(OXReportControllerObserverImpl&);
public:
    const OReportController&                            m_rReportController;
	::std::vector< uno::Reference< container::XChild> > m_aSections;
    ::osl::Mutex                                        m_aMutex;
    oslInterlockedCount                                 m_nLocks;
	sal_Bool                                            m_bReadOnly;

    OXReportControllerObserverImpl(const OReportController& _rController);
    ~OXReportControllerObserverImpl();
};

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

    OXReportControllerObserverImpl::OXReportControllerObserverImpl(const OReportController& _rController)
            :m_rReportController(_rController)
            ,m_nLocks(0)
            ,m_bReadOnly(sal_False)
    {
    }
    
    OXReportControllerObserverImpl::~OXReportControllerObserverImpl()
    {
    }

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

    DBG_NAME(rpt_OXReportControllerObserver)

    OXReportControllerObserver::OXReportControllerObserver(const OReportController& _rController)
            :m_pImpl(new OXReportControllerObserverImpl(_rController) )
            ,m_aFormattedFieldBeautifier(_rController)
            ,m_aFixedTextColor(_rController)
    {
        DBG_CTOR( rpt_OXReportControllerObserver,NULL);

        Application::AddEventListener(LINK( this, OXReportControllerObserver, SettingsChanged ) );
    }

    OXReportControllerObserver::~OXReportControllerObserver()
    {
        DBG_CTOR( rpt_OXReportControllerObserver,NULL);
        Application::RemoveEventListener(LINK( this, OXReportControllerObserver, SettingsChanged ) );
    }

    // -----------------------------------------------------------------------------
	IMPL_LINK(OXReportControllerObserver, SettingsChanged, VclWindowEvent*, _pEvt)
	{
		if ( _pEvt )
        {
            sal_Int32 nEvent = _pEvt->GetId();
            /*
              // just for debug
            if (nEvent == VCLEVENT_WINDOW_CHILDCREATED ||
                nEvent == VCLEVENT_WINDOW_PAINT ||
                nEvent == VCLEVENT_WINDOW_MOVE ||
                nEvent == VCLEVENT_WINDOW_RESIZE ||
                nEvent == VCLEVENT_WINDOW_SHOW ||
                nEvent == VCLEVENT_WINDOW_MOUSEMOVE ||
                nEvent == VCLEVENT_WINDOW_FRAMETITLECHANGED || 
                nEvent == VCLEVENT_WINDOW_HIDE ||
                nEvent == VCLEVENT_EDIT_MODIFY ||
                nEvent == VCLEVENT_SCROLLBAR_ENDSCROLL ||
                nEvent == VCLEVENT_EDIT_SELECTIONCHANGED ||
                nEvent == VCLEVENT_TABPAGE_INSERTED ||
                nEvent == VCLEVENT_TABPAGE_REMOVED ||
                nEvent == VCLEVENT_TOOLBOX_FORMATCHANGED ||
                nEvent == VCLEVENT_TOOLBOX_ITEMADDED ||
                nEvent == VCLEVENT_TOOLBOX_ALLITEMCHANGED ||
                nEvent == VCLEVENT_MENUBARADDED ||
                nEvent == 1
                )
            {
                return 0L;
            }
            */
            
            if (nEvent == VCLEVENT_APPLICATION_DATACHANGED )
            {
                DataChangedEvent* pData = reinterpret_cast<DataChangedEvent*>(_pEvt->GetData());
                if ( pData && ((( pData->GetType() == DATACHANGED_SETTINGS	)	||
                                ( pData->GetType() == DATACHANGED_DISPLAY	))	&&
                               ( pData->GetFlags() & SETTINGS_STYLE		)))
                {
                    OEnvLock aLock(*this);
                    
                    // sal_uInt32 nCount = m_pImpl->m_aSections.size();
                    
                    // send all Section Objects a 'tingle'
                    // maybe they need a change in format, color, etc
                    ::std::vector< uno::Reference< container::XChild > >::const_iterator aIter = m_pImpl->m_aSections.begin();
                    ::std::vector< uno::Reference< container::XChild > >::const_iterator aEnd = m_pImpl->m_aSections.end();
                    for (;aIter != aEnd; aIter++)
                    {
                        const uno::Reference<container::XChild> xChild (*aIter);
                        if (xChild.is())
                        {
                            uno::Reference<report::XSection> xSection(xChild, uno::UNO_QUERY);
                            if (xSection.is())
                            {    
                                const sal_Int32 nCount = xSection->getCount();
                                for (sal_Int32 i = 0; i < nCount; ++i)
                                {
                                    const uno::Any aObj = xSection->getByIndex(i);
                                    uno::Reference < report::XReportComponent > xReportComponent(aObj, uno::UNO_QUERY);
                                    if (xReportComponent.is())
                                    {
                                        m_aFormattedFieldBeautifier.handle(xReportComponent);
                                        m_aFixedTextColor.handle(xReportComponent);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        
        return 0L;
    }

    // XEventListener
    void SAL_CALL OXReportControllerObserver::disposing(const lang::EventObject& e) throw( uno::RuntimeException )
    {
        (void) e;
	    // check if it's an object we have cached informations about
        uno::Reference< beans::XPropertySet > xSourceSet(e.Source, uno::UNO_QUERY);
        if ( xSourceSet.is() )
        {
            uno::Reference< report::XSection> xSection(xSourceSet,uno::UNO_QUERY);
            if ( xSection.is() )
                RemoveSection(xSection);
            else
                RemoveElement(xSourceSet);
	    }
    }
    
    void OXReportControllerObserver::Clear()
    {
        OEnvLock aLock(*this);
        // sal_uInt32 nDebugValue = m_pImpl->m_aSections.size();
        m_pImpl->m_aSections.clear();
    }
    
    // XPropertyChangeListener
    void SAL_CALL OXReportControllerObserver::propertyChange(const beans::PropertyChangeEvent& _rEvent) throw(uno::RuntimeException)
    {
        (void) _rEvent;
        ::osl::ClearableMutexGuard aGuard( m_pImpl->m_aMutex );

    	if ( IsLocked() )
            return;

        m_aFormattedFieldBeautifier.notifyPropertyChange(_rEvent);
        m_aFixedTextColor.notifyPropertyChange(_rEvent);        
    }
    
// -----------------------------------------------------------------------------
void OXReportControllerObserver::Lock() 
{ 
    OSL_ENSURE(m_refCount,"Illegal call to dead object!");
    osl_incrementInterlockedCount( &m_pImpl->m_nLocks ); 
}
void OXReportControllerObserver::UnLock() 
{ 
    OSL_ENSURE(m_refCount,"Illegal call to dead object!");

    osl_decrementInterlockedCount( &m_pImpl->m_nLocks ); 
}
sal_Bool OXReportControllerObserver::IsLocked() const { return m_pImpl->m_nLocks != 0; }

//------------------------------------------------------------------------------
void OXReportControllerObserver::AddSection(const uno::Reference< report::XSection > & _xSection)
{
    OEnvLock aLock(*this);
    try
    {
	    uno::Reference<container::XChild> xChild = _xSection.get();
	    m_pImpl->m_aSections.push_back(xChild);
	    uno::Reference< uno::XInterface >  xInt(_xSection);
	    AddElement(xInt);
    }
    catch(const uno::Exception&)
    {
        DBG_UNHANDLED_EXCEPTION();
    }
}

//------------------------------------------------------------------------------
void OXReportControllerObserver::RemoveSection(const uno::Reference< report::XSection > & _xSection)
{
	OEnvLock aLock(*this);
    try
    {
	    uno::Reference<container::XChild> xChild(_xSection.get());
	    m_pImpl->m_aSections.erase(::std::remove(m_pImpl->m_aSections.begin(),m_pImpl->m_aSections.end(),
		    xChild), m_pImpl->m_aSections.end());
	    uno::Reference< uno::XInterface >  xInt(_xSection);
	    RemoveElement(xInt);
    }
    catch(uno::Exception&)
    {
        DBG_UNHANDLED_EXCEPTION();
    }
}

//------------------------------------------------------------------------------
void OXReportControllerObserver::TogglePropertyListening(const uno::Reference< uno::XInterface > & Element)
{
	// listen at Container
	uno::Reference< container::XIndexAccess >  xContainer(Element, uno::UNO_QUERY);
	if (xContainer.is())
	{
		uno::Reference< uno::XInterface > xInterface;
		sal_Int32 nCount = xContainer->getCount();
		for(sal_Int32 i = 0;i != nCount;++i)
		{
			xInterface.set(xContainer->getByIndex( i ),uno::UNO_QUERY);
			TogglePropertyListening(xInterface);
		}
	}

	uno::Reference< beans::XPropertySet >  xSet(Element, uno::UNO_QUERY);
	if (xSet.is())
	{
		if (!m_pImpl->m_bReadOnly)
			xSet->addPropertyChangeListener( ::rtl::OUString(), this );
		else
			xSet->removePropertyChangeListener( ::rtl::OUString(), this );
	}
}


//------------------------------------------------------------------------------
void OXReportControllerObserver::switchListening( const uno::Reference< container::XIndexAccess >& _rxContainer, bool _bStartListening ) SAL_THROW(())
{
    OSL_PRECOND( _rxContainer.is(), "OXReportControllerObserver::switchListening: invalid container!" );
    if ( !_rxContainer.is() )
        return;

    try
    {
        // also handle all children of this element
		uno::Reference< uno::XInterface > xInterface;
        sal_Int32 nCount = _rxContainer->getCount();
		for(sal_Int32 i = 0;i != nCount;++i)
		{
			xInterface.set(_rxContainer->getByIndex( i ),uno::UNO_QUERY);
            if ( _bStartListening )
		        AddElement( xInterface );
            else
		        RemoveElement( xInterface );
	    }

        // be notified of any changes in the container elements
	    uno::Reference< container::XContainer > xSimpleContainer( _rxContainer, uno::UNO_QUERY );
        // OSL_ENSURE( xSimpleContainer.is(), "OXReportControllerObserver::switchListening: how are we expected to be notified of changes in the container?" );
	    if ( xSimpleContainer.is() )
        {
            if ( _bStartListening )
		        xSimpleContainer->addContainerListener( this );
            else
		        xSimpleContainer->removeContainerListener( this );
        }
    }
    catch( const uno::Exception& )
    {
    	DBG_UNHANDLED_EXCEPTION();
    }
}

//------------------------------------------------------------------------------
void OXReportControllerObserver::switchListening( const uno::Reference< uno::XInterface >& _rxObject, bool _bStartListening ) SAL_THROW(())
{
    OSL_PRECOND( _rxObject.is(), "OXReportControllerObserver::switchListening: how should I listen at a NULL object?" );

    try
    {
        if ( !m_pImpl->m_bReadOnly )
        {
            uno::Reference< beans::XPropertySet > xProps( _rxObject, uno::UNO_QUERY );
	        if ( xProps.is() )
            {
                if ( _bStartListening )
    		        xProps->addPropertyChangeListener( ::rtl::OUString(), this );
                else
    	    	    xProps->removePropertyChangeListener( ::rtl::OUString(), this );
            }
        }

        uno::Reference< util::XModifyBroadcaster > xBroadcaster( _rxObject, uno::UNO_QUERY );
        if ( xBroadcaster.is() )
        {
            if ( _bStartListening )
                xBroadcaster->addModifyListener( this );
            else
                xBroadcaster->removeModifyListener( this );
        }
    }
    catch( const uno::Exception& )
    {
        DBG_UNHANDLED_EXCEPTION();
    }
}

//------------------------------------------------------------------------------
void SAL_CALL OXReportControllerObserver::modified( const lang::EventObject& /*aEvent*/ ) throw (uno::RuntimeException)
{
    // implSetModified();
}

//------------------------------------------------------------------------------
void OXReportControllerObserver::AddElement(const uno::Reference< uno::XInterface >& _rxElement )
{
    // if ( !IsLocked() )
    // {
    m_aFormattedFieldBeautifier.notifyElementInserted(_rxElement);
    m_aFixedTextColor.notifyElementInserted(_rxElement);
    // }
    
    // if it's a container, start listening at all elements
    uno::Reference< container::XIndexAccess > xContainer( _rxElement, uno::UNO_QUERY );
	if ( xContainer.is() )
        switchListening( xContainer, true );

    switchListening( _rxElement, true );
}

//------------------------------------------------------------------------------
void OXReportControllerObserver::RemoveElement(const uno::Reference< uno::XInterface >& _rxElement)
{
    switchListening( _rxElement, false );

	uno::Reference< container::XIndexAccess > xContainer( _rxElement, uno::UNO_QUERY );
    if ( xContainer.is() )
        switchListening( xContainer, false );
}

// -----------------------------------------------------------------------------
::std::vector< uno::Reference< container::XChild> >::const_iterator OXReportControllerObserver::getSection(const uno::Reference<container::XChild>& _xContainer) const
{
    ::std::vector< uno::Reference< container::XChild> >::const_iterator aFind = m_pImpl->m_aSections.end();
    if ( _xContainer.is() )
    {
        aFind = ::std::find(m_pImpl->m_aSections.begin(),m_pImpl->m_aSections.end(),_xContainer);
    		
	    if ( aFind == m_pImpl->m_aSections.end() )
	    {
            uno::Reference<container::XChild> xParent(_xContainer->getParent(),uno::UNO_QUERY);
            aFind = getSection(xParent);
        }
    }
    return aFind;
}
// XContainerListener
//------------------------------------------------------------------------------
void SAL_CALL OXReportControllerObserver::elementInserted(const container::ContainerEvent& evt) throw(uno::RuntimeException)
{
	::vos::OClearableGuard aSolarGuard( Application::GetSolarMutex() );
    ::osl::MutexGuard aGuard( m_pImpl->m_aMutex );

	// neues Object zum lauschen
	uno::Reference< uno::XInterface >  xIface( evt.Element, uno::UNO_QUERY );
	if ( xIface.is() )
    {
        AddElement(xIface);
    }
}

//------------------------------------------------------------------------------
void SAL_CALL OXReportControllerObserver::elementReplaced(const container::ContainerEvent& evt) throw(uno::RuntimeException)
{
	::vos::OClearableGuard aSolarGuard( Application::GetSolarMutex() );
    ::osl::MutexGuard aGuard( m_pImpl->m_aMutex );

    uno::Reference< uno::XInterface >  xIface(evt.ReplacedElement,uno::UNO_QUERY);
	OSL_ENSURE(xIface.is(), "OXReportControllerObserver::elementReplaced: invalid container notification!");
	RemoveElement(xIface);

    xIface.set(evt.Element,uno::UNO_QUERY);
	AddElement(xIface);
}

//------------------------------------------------------------------------------
void SAL_CALL OXReportControllerObserver::elementRemoved(const container::ContainerEvent& evt) throw(uno::RuntimeException)
{
	::vos::OClearableGuard aSolarGuard( Application::GetSolarMutex() );
    ::osl::MutexGuard aGuard( m_pImpl->m_aMutex );

    uno::Reference< uno::XInterface >  xIface( evt.Element, uno::UNO_QUERY );
	if ( xIface.is() )
	{
        RemoveElement(xIface);
    }
}


} // namespace rptui



