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

//_________________________________________________________________________________________________________________
//	my own includes
//_________________________________________________________________________________________________________________

#include <dispatch/popupmenudispatcher.hxx>
#include <general.h>
#include <framework/menuconfiguration.hxx>
#include <framework/addonmenu.hxx>
#include <services.h>
#include <properties.h>

//_________________________________________________________________________________________________________________
//	interface includes
//_________________________________________________________________________________________________________________
#include <com/sun/star/frame/FrameSearchFlag.hpp>
#include <com/sun/star/awt/XToolkit.hpp>
#include <com/sun/star/awt/WindowAttribute.hpp>
#include <com/sun/star/awt/WindowDescriptor.hpp>
#include <com/sun/star/awt/PosSize.hpp>
#include <com/sun/star/awt/XWindowPeer.hpp>
#include <com/sun/star/beans/UnknownPropertyException.hpp>
#include <com/sun/star/lang/WrappedTargetException.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/container/XEnumeration.hpp>

//_________________________________________________________________________________________________________________
//	includes of other projects
//_________________________________________________________________________________________________________________

#include <ucbhelper/content.hxx>
#include <vos/mutex.hxx>
#include <rtl/ustrbuf.hxx>
#include <vcl/svapp.hxx>

//_________________________________________________________________________________________________________________
//	namespace
//_________________________________________________________________________________________________________________

namespace framework{

using namespace ::com::sun::star				;
using namespace ::com::sun::star::awt			;
using namespace ::com::sun::star::beans			;
using namespace ::com::sun::star::container		;
using namespace ::com::sun::star::frame			;
using namespace ::com::sun::star::lang			;
using namespace ::com::sun::star::uno			;
using namespace ::com::sun::star::util			;
using namespace ::cppu							;
using namespace ::osl							;
using namespace ::rtl							;
using namespace ::vos							;

//_________________________________________________________________________________________________________________
//	non exported const
//_________________________________________________________________________________________________________________
const char*     PROTOCOL_VALUE      = "vnd.sun.star.popup:";
const sal_Int32 PROTOCOL_LENGTH     = 19;

//_________________________________________________________________________________________________________________
//	non exported definitions
//_________________________________________________________________________________________________________________

//_________________________________________________________________________________________________________________
//	declarations
//_________________________________________________________________________________________________________________

//*****************************************************************************************************************
//	constructor
//*****************************************************************************************************************
PopupMenuDispatcher::PopupMenuDispatcher( 
    const uno::Reference< XMultiServiceFactory >& xFactory )
		//	Init baseclasses first
        :   ThreadHelpBase          ( &Application::GetSolarMutex()  )
        ,   OWeakObject             (                                )
        // Init member
        ,   m_xFactory              ( xFactory                       )
        ,   m_aListenerContainer    ( m_aLock.getShareableOslMutex() )
        ,   m_bAlreadyDisposed      ( sal_False                      )
        ,   m_bActivateListener     ( sal_False                      )
{
}

//*****************************************************************************************************************
//	destructor
//*****************************************************************************************************************
PopupMenuDispatcher::~PopupMenuDispatcher()
{
	// Warn programmer if he forgot to dispose this instance.
	// We must release all our references ...
	// and a dtor isn't the best place to do that!
}

//*****************************************************************************************************************
//	XInterface, XTypeProvider
//*****************************************************************************************************************
DEFINE_XINTERFACE_7     ( PopupMenuDispatcher                                     ,
                          ::cppu::OWeakObject					  		          ,
                          DIRECT_INTERFACE( XTypeProvider	                      ),
                          DIRECT_INTERFACE( XServiceInfo                          ),
                          DIRECT_INTERFACE( XDispatchProvider                     ),
                          DIRECT_INTERFACE( XDispatch		                      ),
                          DIRECT_INTERFACE( XEventListener                        ),
                          DIRECT_INTERFACE( XInitialization                       ),
                          DERIVED_INTERFACE( XFrameActionListener, XEventListener )
						)

DEFINE_XTYPEPROVIDER_7  (   PopupMenuDispatcher ,
							XTypeProvider		,
                            XServiceInfo        ,
                            XDispatchProvider   ,
							XDispatch			,
							XEventListener		,
                            XInitialization     ,
							XFrameActionListener
						)

DEFINE_XSERVICEINFO_MULTISERVICE( PopupMenuDispatcher                    ,
                                  ::cppu::OWeakObject                    ,
                                  SERVICENAME_PROTOCOLHANDLER            ,
                                  IMPLEMENTATIONNAME_POPUPMENUDISPATCHER )

DEFINE_INIT_SERVICE(PopupMenuDispatcher,
{
    /*Attention
    I think we don't need any mutex or lock here ... because we are called by our own static method impl_createInstance()
    to create a new instance of this class by our own supported service factory.
    see macro DEFINE_XSERVICEINFO_MULTISERVICE and "impl_initService()" for further informations!
    */
}
)

//*****************************************************************************************************************
//	XInitialization
//*****************************************************************************************************************
void SAL_CALL PopupMenuDispatcher::initialize( 
    const css::uno::Sequence< css::uno::Any >& lArguments ) 
throw( css::uno::Exception, css::uno::RuntimeException)
{
    css::uno::Reference< css::frame::XFrame > xFrame;
    
    /* SAFE { */
    WriteGuard aWriteLock(m_aLock);

    for (int a=0; a<lArguments.getLength(); ++a)
    {
        if (a==0)
        {
            lArguments[a] >>= xFrame;
            m_xWeakFrame = xFrame;

	        m_bActivateListener = sal_True;
            uno::Reference< css::frame::XFrameActionListener > xFrameActionListener( 
                (OWeakObject *)this, css::uno::UNO_QUERY );
            xFrame->addFrameActionListener( xFrameActionListener );
        }
    }

    aWriteLock.unlock();
    /* } SAFE */
}

//*****************************************************************************************************************
//	XDispatchProvider
//*****************************************************************************************************************
css::uno::Reference< css::frame::XDispatch > 
SAL_CALL PopupMenuDispatcher::queryDispatch( 
    const css::util::URL&  rURL    ,
    const ::rtl::OUString& sTarget ,
    sal_Int32              nFlags  ) 
throw( css::uno::RuntimeException )
{
    css::uno::Reference< css::frame::XDispatch > xDispatch;
    
    if ( rURL.Complete.compareToAscii( PROTOCOL_VALUE, PROTOCOL_LENGTH ) == 0 )
    {
        // --- SAFE ---
	    ResetableGuard aGuard( m_aLock );
        impl_RetrievePopupControllerQuery();
        impl_CreateUriRefFactory();
        
        css::uno::Reference< css::container::XNameAccess > xPopupCtrlQuery( m_xPopupCtrlQuery );
        css::uno::Reference< css::uri::XUriReferenceFactory > xUriRefFactory( m_xUriRefFactory );
        aGuard.unlock();
        // --- SAFE ---
        
        if ( xPopupCtrlQuery.is() )
        {
            try
            {
                // Just use the main part of the URL for popup menu controllers
                sal_Int32     nQueryPart( 0 );
                sal_Int32     nSchemePart( 0 );
                rtl::OUString aBaseURL( RTL_CONSTASCII_USTRINGPARAM( "vnd.sun.star.popup:" ));
                rtl::OUString aURL( rURL.Complete );
                
                nSchemePart = aURL.indexOf( ':' );
                if (( nSchemePart > 0 ) && 
                    ( aURL.getLength() > ( nSchemePart+1 )))
                {
                    nQueryPart  = aURL.indexOf( '?', nSchemePart );
                    if ( nQueryPart > 0 )
                        aBaseURL += aURL.copy( nSchemePart+1, nQueryPart-(nSchemePart+1) );
                    else if ( nQueryPart == -1 )
                        aBaseURL += aURL.copy( nSchemePart+1 );
                }
                
                css::uno::Reference< css::frame::XDispatchProvider > xDispatchProvider;
                
                // Find popup menu controller using the base URL
                xPopupCtrlQuery->getByName( aBaseURL ) >>= xDispatchProvider;
                aGuard.unlock();
                
                // Ask popup menu dispatch provider for dispatch object
                if ( xDispatchProvider.is() )
                    xDispatch = xDispatchProvider->queryDispatch( rURL, sTarget, nFlags );
            }
            catch ( RuntimeException& )
            {
                throw;
            }
            catch ( Exception& )
            {
            }
        }
    }
    return xDispatch;
}

css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > SAL_CALL 
PopupMenuDispatcher::queryDispatches( 
    const css::uno::Sequence< css::frame::DispatchDescriptor >& lDescriptor ) 
throw( css::uno::RuntimeException )
{
    sal_Int32 nCount = lDescriptor.getLength();
    css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > lDispatcher( nCount );
    for( sal_Int32 i=0; i<nCount; ++i )
    {
        lDispatcher[i] = this->queryDispatch(
                            lDescriptor[i].FeatureURL,
                            lDescriptor[i].FrameName,
                            lDescriptor[i].SearchFlags);
    }
    return lDispatcher;
}

//*****************************************************************************************************************
//	XDispatch
//*****************************************************************************************************************
void 
SAL_CALL PopupMenuDispatcher::dispatch(
    const URL&                        /*aURL*/            ,
	const Sequence< PropertyValue >&  /*seqProperties*/	) 
throw( RuntimeException )
{
}

//*****************************************************************************************************************
//	XDispatch
//*****************************************************************************************************************
void 
SAL_CALL PopupMenuDispatcher::addStatusListener(
    const uno::Reference< XStatusListener >& xControl,
	const URL&							aURL	)
throw( RuntimeException )
{
	// Ready for multithreading
	ResetableGuard aGuard( m_aLock );
	// Safe impossible cases
	// Add listener to container.
	m_aListenerContainer.addInterface( aURL.Complete, xControl );
}

//*****************************************************************************************************************
//	XDispatch
//*****************************************************************************************************************
void 
SAL_CALL PopupMenuDispatcher::removeStatusListener(
    const uno::Reference< XStatusListener >& xControl,
	const URL&							aURL	) 
throw( RuntimeException )
{
	// Ready for multithreading
	ResetableGuard aGuard( m_aLock );
	// Safe impossible cases
	// Add listener to container.
	m_aListenerContainer.removeInterface( aURL.Complete, xControl );
}

//*****************************************************************************************************************
//	 XFrameActionListener
//*****************************************************************************************************************

void 
SAL_CALL PopupMenuDispatcher::frameAction( 
    const FrameActionEvent& aEvent ) 
throw ( RuntimeException )
{
	ResetableGuard aGuard( m_aLock );

	if (( aEvent.Action == css::frame::FrameAction_COMPONENT_DETACHING ) ||
        ( aEvent.Action == css::frame::FrameAction_COMPONENT_ATTACHED  ))
	{
        // Reset query reference to requery it again next time
        m_xPopupCtrlQuery.clear();
	}
}

//*****************************************************************************************************************
//	 XEventListener
//*****************************************************************************************************************
void 
SAL_CALL PopupMenuDispatcher::disposing( const EventObject& ) throw( RuntimeException )
{
	// Ready for multithreading
	ResetableGuard aGuard( m_aLock );
	// Safe impossible cases
    LOG_ASSERT( !(m_bAlreadyDisposed==sal_True), "MenuDispatcher::disposing()\nObject already disposed .. don't call it again!\n" )

	if( m_bAlreadyDisposed == sal_False )
	{
		m_bAlreadyDisposed = sal_True;

		if ( m_bActivateListener )
		{
			uno::Reference< XFrame > xFrame( m_xWeakFrame.get(), UNO_QUERY );
			if ( xFrame.is() )
			{
				xFrame->removeFrameActionListener( uno::Reference< XFrameActionListener >( (OWeakObject *)this, UNO_QUERY ));
				m_bActivateListener = sal_False;
			}
		}

		// Forget our factory.
		m_xFactory = uno::Reference< XMultiServiceFactory >();
	}
}

void PopupMenuDispatcher::impl_RetrievePopupControllerQuery()
{
    if ( !m_xPopupCtrlQuery.is() )
    {
        css::uno::Reference< css::frame::XLayoutManager > xLayoutManager;
        css::uno::Reference< css::frame::XFrame > xFrame( m_xWeakFrame );
        
        if ( xFrame.is() )
        {
            css::uno::Reference< css::beans::XPropertySet > xPropSet( xFrame, css::uno::UNO_QUERY );
            if ( xPropSet.is() )
            {
                try
                {
                    xPropSet->getPropertyValue( FRAME_PROPNAME_LAYOUTMANAGER ) >>= xLayoutManager;

                    if ( xLayoutManager.is() )
                    {
                        css::uno::Reference< css::ui::XUIElement > xMenuBar;
                        rtl::OUString aMenuBar( RTL_CONSTASCII_USTRINGPARAM( "private:resource/menubar/menubar" ));
                        xMenuBar = xLayoutManager->getElement( aMenuBar );

                        m_xPopupCtrlQuery = css::uno::Reference< css::container::XNameAccess >( 
                                                xMenuBar, css::uno::UNO_QUERY );
                    }
                }
                catch ( css::uno::RuntimeException& )
                {
                    throw;
                }
                catch ( css::uno::Exception& )
                {
                }
            }
        }
    }
}

void PopupMenuDispatcher::impl_CreateUriRefFactory()
{
    if ( !m_xUriRefFactory.is() )
    {
        rtl::OUString aUriRefFactoryService( 
            RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.uri.UriReferenceFactory" ));
        
        m_xUriRefFactory = css::uno::Reference< css::uri::XUriReferenceFactory >(
                                m_xFactory->createInstance( aUriRefFactoryService ),
                                css::uno::UNO_QUERY);
        
    }
}

}		//	namespace framework
