/**************************************************************
 * 
 * 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.
 * 
 *************************************************************/



#ifndef EXTENSIONS_SOURCE_PROPCTRLR_PROPERTYHANDLER_HXX
#define EXTENSIONS_SOURCE_PROPCTRLR_PROPERTYHANDLER_HXX

#include "pcrcomponentcontext.hxx"
#include "pcrcommon.hxx"
#ifndef _EXTENSIONS_PROPCTRLR_MODULEPCR_HXX_
#include "modulepcr.hxx"
#endif

/** === begin UNO includes === **/
#include <com/sun/star/uno/XComponentContext.hpp>
#include <com/sun/star/beans/PropertyState.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/beans/Property.hpp>
#include <com/sun/star/script/XTypeConverter.hpp>
#include <com/sun/star/frame/XModel.hpp>
#include <com/sun/star/uno/Sequence.hxx>
#include <com/sun/star/uno/Any.hxx>
#include <com/sun/star/util/Date.hpp>
#include <com/sun/star/util/Time.hpp>
#include <com/sun/star/util/DateTime.hpp>
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
#include <com/sun/star/inspection/XPropertyHandler.hpp>
#include <com/sun/star/lang/XServiceInfo.hpp>
/** === end UNO includes === **/
#include <osl/interlck.h>
#include <cppuhelper/compbase1.hxx>
#include <cppuhelper/implbase1.hxx>
#include <comphelper/uno3.hxx>

#include <memory>
#include <vector>

namespace com { namespace sun { namespace star {
    namespace inspection {
        struct LineDescriptor;
        class XPropertyControlFactory;
    }
} } }

class Window;
//........................................................................
namespace pcr
{
//........................................................................

    typedef sal_Int32   PropertyId;

	//====================================================================
	//= PropertyHandler
	//====================================================================
    class OPropertyInfoService;
    typedef ::cppu::WeakComponentImplHelper1    <   ::com::sun::star::inspection::XPropertyHandler
                                                >   PropertyHandler_Base;
    /** the base class for property handlers
    */
    class PropertyHandler : public PropertyHandler_Base
    {
    private:
        /// cache for getSupportedProperties
        mutable StlSyntaxSequence< ::com::sun::star::beans::Property >
                                    m_aSupportedProperties;
        mutable bool                m_bSupportedPropertiesAreKnown;

        /// helper which ensures that we can access resources as long as the instance lives
        PcrClient       m_aEnsureResAccess;

    private:
        /// the property listener which has been registered
        PropertyChangeListeners                                                         m_aPropertyListeners;

    protected:
		mutable ::osl::Mutex                                                            m_aMutex;
        /// the context in which the instance was created
        ComponentContext                                                                m_aContext;
        /// the component we're inspecting
        ::com::sun::star::uno::Reference< ::com::sun::star::beans::XPropertySet >       m_xComponent;
        /// info about our component's properties
        ::com::sun::star::uno::Reference< ::com::sun::star::beans::XPropertySetInfo >   m_xComponentPropertyInfo;
        /// type converter, needed on various occasions
        ::com::sun::star::uno::Reference< ::com::sun::star::script::XTypeConverter >    m_xTypeConverter;
        /// access to property meta data
        ::std::auto_ptr< OPropertyInfoService >                                         m_pInfoService;

    protected:
        PropertyHandler(
            const ::com::sun::star::uno::Reference< ::com::sun::star::uno::XComponentContext >& _rxContext
        );
        ~PropertyHandler();

        // default implementations for XPropertyHandler
        virtual void SAL_CALL inspect( const ::com::sun::star::uno::Reference< ::com::sun::star::uno::XInterface >& _rxIntrospectee ) throw (::com::sun::star::uno::RuntimeException, ::com::sun::star::lang::NullPointerException);
        virtual ::com::sun::star::uno::Sequence< ::com::sun::star::beans::Property > SAL_CALL getSupportedProperties() throw (::com::sun::star::uno::RuntimeException);
        virtual ::com::sun::star::uno::Sequence< ::rtl::OUString > SAL_CALL getSupersededProperties( ) throw (::com::sun::star::uno::RuntimeException);
        virtual ::com::sun::star::uno::Sequence< ::rtl::OUString > SAL_CALL getActuatingProperties( ) throw (::com::sun::star::uno::RuntimeException);
        virtual ::com::sun::star::uno::Any SAL_CALL convertToPropertyValue( const ::rtl::OUString& _rPropertyName, const ::com::sun::star::uno::Any& _rControlValue ) throw (::com::sun::star::beans::UnknownPropertyException, ::com::sun::star::uno::RuntimeException);
        virtual ::com::sun::star::uno::Any SAL_CALL convertToControlValue( const ::rtl::OUString& _rPropertyName, const ::com::sun::star::uno::Any& _rPropertyValue, const ::com::sun::star::uno::Type& _rControlValueType ) throw (::com::sun::star::beans::UnknownPropertyException, ::com::sun::star::uno::RuntimeException);
        virtual ::com::sun::star::beans::PropertyState  SAL_CALL getPropertyState( const ::rtl::OUString& _rPropertyName ) throw (::com::sun::star::beans::UnknownPropertyException, ::com::sun::star::uno::RuntimeException);
        virtual ::com::sun::star::inspection::LineDescriptor SAL_CALL describePropertyLine( const ::rtl::OUString& _rPropertyName, const ::com::sun::star::uno::Reference< ::com::sun::star::inspection::XPropertyControlFactory >& _rxControlFactory ) throw (::com::sun::star::beans::UnknownPropertyException, ::com::sun::star::lang::NullPointerException, ::com::sun::star::uno::RuntimeException);
        virtual ::sal_Bool SAL_CALL isComposable( const ::rtl::OUString& _rPropertyName ) throw (::com::sun::star::beans::UnknownPropertyException, ::com::sun::star::uno::RuntimeException);
        virtual ::com::sun::star::inspection::InteractiveSelectionResult SAL_CALL onInteractivePropertySelection( const ::rtl::OUString& _rPropertyName, sal_Bool _bPrimary, ::com::sun::star::uno::Any& _rData, const ::com::sun::star::uno::Reference< ::com::sun::star::inspection::XObjectInspectorUI >& _rxInspectorUI ) throw (::com::sun::star::beans::UnknownPropertyException, ::com::sun::star::lang::NullPointerException, ::com::sun::star::uno::RuntimeException);
        virtual void SAL_CALL actuatingPropertyChanged( const ::rtl::OUString& _rActuatingPropertyName, const ::com::sun::star::uno::Any& _rNewValue, const ::com::sun::star::uno::Any& _rOldValue, const ::com::sun::star::uno::Reference< ::com::sun::star::inspection::XObjectInspectorUI >& _rxInspectorUI, sal_Bool _bFirstTimeInit ) throw (::com::sun::star::lang::NullPointerException, ::com::sun::star::uno::RuntimeException);
        virtual void SAL_CALL addPropertyChangeListener( const ::com::sun::star::uno::Reference< ::com::sun::star::beans::XPropertyChangeListener >& _rxListener ) throw (::com::sun::star::uno::RuntimeException);
        virtual void SAL_CALL removePropertyChangeListener( const ::com::sun::star::uno::Reference< ::com::sun::star::beans::XPropertyChangeListener >& _rxListener ) throw (::com::sun::star::uno::RuntimeException);
        virtual sal_Bool SAL_CALL suspend( sal_Bool _bSuspend ) throw (::com::sun::star::uno::RuntimeException);

        // XComponent
        DECLARE_XCOMPONENT()
        virtual void SAL_CALL disposing();

        // own overridables
        virtual ::com::sun::star::uno::Sequence< ::com::sun::star::beans::Property >
                    SAL_CALL doDescribeSupportedProperties() const = 0;

        /// called when XPropertyHandler::inspect has been called, and we thus have a new component to inspect
        virtual void onNewComponent();

    protected:
        /** fires the change in a property value to our listener (if any)
            @see addPropertyChangeListener
        */
        void    firePropertyChange( const ::rtl::OUString& _rPropName, PropertyId _nPropId,
                    const ::com::sun::star::uno::Any& _rOldValue, const ::com::sun::star::uno::Any& _rNewValue ) SAL_THROW(());

        /** retrieves a window which can be used as parent for dialogs
        */
        Window* impl_getDefaultDialogParent_nothrow() const;

        /** retrieves the property id for a given property name
            @throw com::sun::star::beans::UnknownPropertyException
                if the property name is not known to our ->m_pInfoService
        */
        PropertyId impl_getPropertyId_throw( const ::rtl::OUString& _rPropertyName ) const;

        //-------------------------------------------------------------------------------
        // helper for implementing doDescribeSupportedProperties
        /** adds a description for the given string property to the given property vector
            Most probably to be called from within getSupportedProperties
        */
        inline void addStringPropertyDescription(
                    ::std::vector< ::com::sun::star::beans::Property >& _rProperties,
                    const ::rtl::OUString& _rPropertyName,
                    sal_Int16 _nAttribs = 0
                ) const;

        /** adds a description for the given int32 property to the given property vector
        */
        inline void addInt32PropertyDescription(
                    ::std::vector< ::com::sun::star::beans::Property >& _rProperties,
                    const ::rtl::OUString& _rPropertyName,
                    sal_Int16 _nAttribs = 0
                ) const;

        /** adds a description for the given int16 property to the given property vector
        */
        inline void addInt16PropertyDescription(
                    ::std::vector< ::com::sun::star::beans::Property >& _rProperties,
                    const ::rtl::OUString& _rPropertyName,
                    sal_Int16 _nAttribs = 0
                ) const;

        /** adds a description for the given double property to the given property vector
        */
        inline void addDoublePropertyDescription(
                    ::std::vector< ::com::sun::star::beans::Property >& _rProperties,
                    const ::rtl::OUString& _rPropertyName,
                    sal_Int16 _nAttribs = 0
                ) const;

        /** adds a description for the given date property to the given property vector
        */
        inline void addDatePropertyDescription(
                    ::std::vector< ::com::sun::star::beans::Property >& _rProperties,
                    const ::rtl::OUString& _rPropertyName,
                    sal_Int16 _nAttribs = 0
                ) const;

        /** adds a description for the given time property to the given property vector
        */
        inline void addTimePropertyDescription(
                    ::std::vector< ::com::sun::star::beans::Property >& _rProperties,
                    const ::rtl::OUString& _rPropertyName,
                    sal_Int16 _nAttribs = 0
                ) const;

        /** adds a description for the given DateTime property to the given property vector
        */
        inline void addDateTimePropertyDescription(
                    ::std::vector< ::com::sun::star::beans::Property >& _rProperties,
                    const ::rtl::OUString& _rPropertyName,
                    sal_Int16 _nAttribs = 0
                ) const;

        /// adds a Property, given by name only, to a given vector of Properties
        void implAddPropertyDescription(
                    ::std::vector< ::com::sun::star::beans::Property >& _rProperties,
                    const ::rtl::OUString& _rPropertyName,
                    const ::com::sun::star::uno::Type& _rType,
                    sal_Int16 _nAttribs = 0
                ) const;

        //-------------------------------------------------------------------------------
        // helper for accessing and maintaining meta data about our supported properties

        /** retrieves a property given by handle

            @return
                a pointer to the descriptor for the given properties, if it is one of our
                supported properties, <NULL/> else.

            @see doDescribeSupportedProperties
            @see impl_getPropertyFromId_throw
        */
        const ::com::sun::star::beans::Property*
                    impl_getPropertyFromId_nothrow( PropertyId _nPropId ) const;

        /** retrieves a property given by handle

            @throws UnknownPropertyException
                if the handler does not support a property with the given handle

            @seealso doDescribeSupportedProperties
            @see impl_getPropertyFromId_nothrow
        */
        const ::com::sun::star::beans::Property&
                    impl_getPropertyFromId_throw( PropertyId _nPropId ) const;

        /** determines whether a given property id is part of our supported properties
            @see getSupportedProperties
            @see doDescribeSupportedProperties
        */
        inline bool impl_isSupportedProperty_nothrow( PropertyId _nPropId ) const
        {
            return impl_getPropertyFromId_nothrow( _nPropId ) != NULL;
        }

        /** retrieves a property given by name

            @throws UnknownPropertyException
                if the handler does not support a property with the given name

            @seealso doDescribeSupportedProperties
        */
        const ::com::sun::star::beans::Property&
                    impl_getPropertyFromName_throw( const ::rtl::OUString& _rPropertyName ) const;

        /** get the name of a property given by handle
        */
        inline ::rtl::OUString
                    impl_getPropertyNameFromId_nothrow( PropertyId _nPropId ) const;

        /** returns the value of the ContextDocument property in the ComponentContext which was used to create
            this handler.
        */
        inline ::com::sun::star::uno::Reference< ::com::sun::star::frame::XModel >
                    impl_getContextDocument_nothrow() const
        {
            return ::com::sun::star::uno::Reference< ::com::sun::star::frame::XModel >(
                m_aContext.getContextValueByAsciiName( "ContextDocument" ), ::com::sun::star::uno::UNO_QUERY );
        }

        /** marks the context document as modified

            @see impl_getContextDocument_nothrow
        */
        void impl_setContextDocumentModified_nothrow() const;

        /// determines whether our component has a given property
        bool impl_componentHasProperty_throw( const ::rtl::OUString& _rPropName ) const;

        /** determines the default measure unit for the document in which our component lives
        */
        sal_Int16 impl_getDocumentMeasurementUnit_throw() const;

    private:
        PropertyHandler();                                    // never implemented
        PropertyHandler( const PropertyHandler& );            // never implemented
        PropertyHandler& operator=( const PropertyHandler& ); // never implemented
    };

    //--------------------------------------------------------------------
    inline void PropertyHandler::addStringPropertyDescription( ::std::vector< ::com::sun::star::beans::Property >& _rProperties, const ::rtl::OUString& _rPropertyName, sal_Int16 _nAttribs ) const
    {
        implAddPropertyDescription( _rProperties, _rPropertyName, ::getCppuType( static_cast< ::rtl::OUString* >( NULL ) ), _nAttribs );
    }

    inline void PropertyHandler::addInt32PropertyDescription( ::std::vector< ::com::sun::star::beans::Property >& _rProperties, const ::rtl::OUString& _rPropertyName, sal_Int16 _nAttribs ) const
    {
        implAddPropertyDescription( _rProperties, _rPropertyName, ::getCppuType( static_cast< sal_Int32* >( NULL ) ), _nAttribs );
    }

    inline void PropertyHandler::addInt16PropertyDescription( ::std::vector< ::com::sun::star::beans::Property >& _rProperties, const ::rtl::OUString& _rPropertyName, sal_Int16 _nAttribs ) const
    {
        implAddPropertyDescription( _rProperties, _rPropertyName, ::getCppuType( static_cast< sal_Int16* >( NULL ) ), _nAttribs );
    }

    inline void PropertyHandler::addDoublePropertyDescription( ::std::vector< ::com::sun::star::beans::Property >& _rProperties, const ::rtl::OUString& _rPropertyName, sal_Int16 _nAttribs ) const
    {
        implAddPropertyDescription( _rProperties, _rPropertyName, ::getCppuType( static_cast< double* >( NULL ) ), _nAttribs );
    }

    inline void PropertyHandler::addDatePropertyDescription( ::std::vector< ::com::sun::star::beans::Property >& _rProperties, const ::rtl::OUString& _rPropertyName, sal_Int16 _nAttribs ) const
    {
        implAddPropertyDescription( _rProperties, _rPropertyName, ::getCppuType( static_cast< com::sun::star::util::Date* >( NULL ) ), _nAttribs );
    }

    inline void PropertyHandler::addTimePropertyDescription( ::std::vector< ::com::sun::star::beans::Property >& _rProperties, const ::rtl::OUString& _rPropertyName, sal_Int16 _nAttribs ) const
    {
        implAddPropertyDescription( _rProperties, _rPropertyName, ::getCppuType( static_cast< com::sun::star::util::Time* >( NULL ) ), _nAttribs );
    }

    inline void PropertyHandler::addDateTimePropertyDescription( ::std::vector< ::com::sun::star::beans::Property >& _rProperties, const ::rtl::OUString& _rPropertyName, sal_Int16 _nAttribs ) const
    {
        implAddPropertyDescription( _rProperties, _rPropertyName, ::getCppuType( static_cast< com::sun::star::util::DateTime* >( NULL ) ), _nAttribs );
    }

    inline ::rtl::OUString PropertyHandler::impl_getPropertyNameFromId_nothrow( PropertyId _nPropId ) const
    {
        const ::com::sun::star::beans::Property* pProp = impl_getPropertyFromId_nothrow( _nPropId );
        return pProp ? pProp->Name : ::rtl::OUString();
    }

    //====================================================================
	//= PropertyHandlerComponent
	//====================================================================
    typedef ::cppu::ImplHelper1 <   ::com::sun::star::lang::XServiceInfo
                                >   PropertyHandlerComponent_Base;
    /** PropertyHandler implementation which additionally supports XServiceInfo
    */
    class PropertyHandlerComponent  :public PropertyHandler
                                    ,public PropertyHandlerComponent_Base
    {
    protected:
        PropertyHandlerComponent(
            const ::com::sun::star::uno::Reference< ::com::sun::star::uno::XComponentContext >& _rxContext
        );

        DECLARE_XINTERFACE()
        DECLARE_XTYPEPROVIDER()

        // XServiceInfo
        virtual ::rtl::OUString SAL_CALL getImplementationName(  ) throw (::com::sun::star::uno::RuntimeException) = 0;
        virtual ::sal_Bool SAL_CALL supportsService( const ::rtl::OUString& ServiceName ) throw (::com::sun::star::uno::RuntimeException);
        virtual ::com::sun::star::uno::Sequence< ::rtl::OUString > SAL_CALL getSupportedServiceNames(  ) throw (::com::sun::star::uno::RuntimeException) = 0;
    };

    //====================================================================
	//= HandlerComponentBase
	//====================================================================
    /** a PropertyHandlerComponent implementation which routes XServiceInfo::getImplementationName and
        XServiceInfo::getSupportedServiceNames to static versions of those methods, which are part of
        the derived class.

        Additionally, a method <member>Create</member> is provided which takes a component context, and returns a new
        instance of the derived class. This <member>Create</member> is used to register the implementation
        of the derived class at the <type>PcrModule</type>.

        Well, every time we're talking about derived class, we in fact mean the template argument of
        <type>HandlerComponentBase</type>. But usually this equals your derived class:
        <pre>
        class MyHandler;
        typedef HandlerComponentBase< MyHandler > MyHandler_Base;
        class MyHandler : MyHandler_Base
        {
            ...
        public:
            static ::rtl::OUString SAL_CALL getImplementationName_static(  ) throw (::com::sun::star::uno::RuntimeException);
            static ::com::sun::star::uno::Sequence< ::rtl::OUString > SAL_CALL getSupportedServiceNames_static(  ) throw (::com::sun::star::uno::RuntimeException);
        };
        </pre>
    */
    template < class HANDLER >
    class HandlerComponentBase : public PropertyHandlerComponent
    {
    protected:
        HandlerComponentBase( const ::com::sun::star::uno::Reference< ::com::sun::star::uno::XComponentContext >& _rxContext )
            :PropertyHandlerComponent( _rxContext )
        {
        }

    protected:
        // XServiceInfo
        virtual ::rtl::OUString SAL_CALL getImplementationName(  ) throw (::com::sun::star::uno::RuntimeException);
        virtual ::com::sun::star::uno::Sequence< ::rtl::OUString > SAL_CALL getSupportedServiceNames(  ) throw (::com::sun::star::uno::RuntimeException);
        static ::com::sun::star::uno::Reference< ::com::sun::star::uno::XInterface > SAL_CALL Create( const ::com::sun::star::uno::Reference< ::com::sun::star::uno::XComponentContext >& _rxContext );

    public:
        /** registers the implementation of HANDLER at the <type>PcrModule</type>
        */
        static void registerImplementation();
    };

	//--------------------------------------------------------------------
    template < class HANDLER >
    ::rtl::OUString SAL_CALL HandlerComponentBase< HANDLER >::getImplementationName(  ) throw (::com::sun::star::uno::RuntimeException)
    {
        return HANDLER::getImplementationName_static();
    }

	//--------------------------------------------------------------------
    template < class HANDLER >
    ::com::sun::star::uno::Sequence< ::rtl::OUString > SAL_CALL HandlerComponentBase< HANDLER >::getSupportedServiceNames(  ) throw (::com::sun::star::uno::RuntimeException)
    {
        return HANDLER::getSupportedServiceNames_static();
    }

	//--------------------------------------------------------------------
    template < class HANDLER >
    void HandlerComponentBase< HANDLER >::registerImplementation()
    {
        PcrModule::getInstance().registerImplementation(
            HANDLER::getImplementationName_static(),
            HANDLER::getSupportedServiceNames_static(),
            HANDLER::Create
        );
    }

	//--------------------------------------------------------------------
    template < class HANDLER >
    ::com::sun::star::uno::Reference< ::com::sun::star::uno::XInterface > SAL_CALL HandlerComponentBase< HANDLER >::Create( const ::com::sun::star::uno::Reference< ::com::sun::star::uno::XComponentContext >& _rxContext )
    {
        return *( new HANDLER( _rxContext ) );
    }

//........................................................................
} // namespace pcr
//........................................................................

#endif // EXTENSIONS_SOURCE_PROPCTRLR_PROPERTYHANDLER_HXX

