/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 * 
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/
#ifndef __PLUGIN_SOURCE_MGR_IMPL_HXX
#define __PLUGIN_SOURCE_MGR_IMPL_HXX

#ifdef SOLARIS
#include <limits>
#endif

#include "cppuhelper/weak.hxx"

#include "com/sun/star/awt/Key.hpp"
#include "com/sun/star/awt/KeyFunction.hpp"
#include "com/sun/star/beans/PropertyAttribute.hpp"
#include "com/sun/star/plugin/PluginMode.hpp"
#include "com/sun/star/plugin/PluginDescription.hpp"
#include "com/sun/star/plugin/PluginException.hpp"
#include "com/sun/star/plugin/PluginVariable.hpp"
#include "com/sun/star/plugin/XPlugin.hpp"
#include "com/sun/star/plugin/XPluginManager.hpp"
#include "com/sun/star/plugin/XPluginContext.hpp"
#include "com/sun/star/io/XConnectable.hpp"
#include "com/sun/star/io/XOutputStream.hpp"
#include "com/sun/star/io/XDataOutputStream.hpp"
#include "com/sun/star/io/XActiveDataControl.hpp"
#include "com/sun/star/io/XDataInputStream.hpp"
#include "com/sun/star/io/XMarkableStream.hpp"
#include "com/sun/star/io/XInputStream.hpp"
#include "com/sun/star/io/XStreamListener.hpp"
#include "com/sun/star/io/XActiveDataSink.hpp"
#include "com/sun/star/io/XActiveDataSource.hpp"
#include "com/sun/star/lang/XServiceName.hpp"
#include "com/sun/star/lang/XServiceInfo.hpp"
#include "com/sun/star/lang/XMultiServiceFactory.hpp"
#include "com/sun/star/lang/XSingleServiceFactory.hpp"
#include "com/sun/star/awt/GradientStyle.hpp"
#include "com/sun/star/awt/RasterOperation.hpp"
#include "com/sun/star/awt/Gradient.hpp"
#include "com/sun/star/awt/XGraphics.hpp"

#include "cppuhelper/implbase3.hxx"
#include "cppuhelper/implbase2.hxx"
#include "cppuhelper/implbase1.hxx"

#include <list>

#ifdef WNT 
#include "plugin/win/sysplug.hxx"
#endif

#ifdef WNT 
#include <plugin/win/sysplug.hxx>
#elif defined(OS2)
#include "plugin/os2/sysplug.hxx"
#elif defined(QUARTZ)
#include "plugin/aqua/sysplug.hxx"
#elif defined(UNX)
#include "plugin/unx/sysplug.hxx"
#endif

#if ! defined (QUARTZ)
// the QUARTZ implementation needs special instance data
typedef int SysPlugData;
#endif

#include "plugin/plctrl.hxx"
#include "plugin/model.hxx"

#include "vcl/sysdata.hxx"
#include "vcl/syschild.hxx"

#include "tools/link.hxx"
#include "tools/stream.hxx"


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

#define PROVIDING_NONE				0
#define PROVIDING_NOW				1
#define PROVIDING_MODEL_UPDATE		2

// forwards
namespace ucbhelper { class Content; }
class PluginStream;
class PluginInputStream;
class PluginOutputStream;
class XPlugin_Impl;
class PluginDisposer;
class PluginEventListener;

class XPlugin_Impl : public com::sun::star::plugin::XPlugin,
					 public PluginControl_Impl,
					 public com::sun::star::beans::XPropertyChangeListener
{
private:
	::osl::Mutex				m_aMutex;
	Reference< com::sun::star::lang::XMultiServiceFactory > 		m_xSMgr;
	Reference< com::sun::star::plugin::XPluginContext > 			m_rBrowserContext;

	PluginComm*					m_pPluginComm;
	NPP_t						m_aInstance;
	NPWindow					m_aNPWindow;
    SysPlugData                 m_aSysPlugData;
	rtl_TextEncoding			m_aEncoding;

	const char**				m_pArgv;
	const char**				m_pArgn;
	int							m_nArgs;
    rtl::OString				m_aLastGetUrl;

	Reference< com::sun::star::awt::XControlModel > 			m_xModel;

	::com::sun::star::plugin::PluginDescription			m_aDescription;
	sal_Int16						m_aPluginMode;

	int							m_nProvidingState;
	int							m_nCalledFromPlugin;
	PluginDisposer*				m_pDisposer;

	::std::list<PluginInputStream*>		m_aInputStreams;
	::std::list<PluginOutputStream*>	m_aOutputStreams;	
	::std::list<PluginEventListener*>	m_aPEventListeners;
	::rtl::OUString						m_aURL;

	sal_Bool						m_bIsDisposed;

    void prependArg( const char* pName, const char* pValue ); // arguments will be strdup'ed
    void initArgs( const Sequence< rtl::OUString >& argn,
                   const Sequence< rtl::OUString >& argv,
                   sal_Int16 mode );
    void freeArgs();
    void handleSpecialArgs();
    
	void loadPlugin();
	void destroyInstance();
	void modelChanged();

public:
	XPlugin_Impl( const Reference< com::sun::star::lang::XMultiServiceFactory >  & rSMgr );
	virtual ~XPlugin_Impl();

	::osl::Mutex& getMutex() { return m_aMutex; }
	
	void destroyStreams();

    void setLastGetUrl( const rtl::OString& rUrl ) { m_aLastGetUrl = rUrl; }

    com::sun::star::plugin::PluginDescription fitDescription( const rtl::OUString& rURL );

	::std::list<PluginInputStream*>& getInputStreams() { return m_aInputStreams; }
	::std::list<PluginOutputStream*>& getOutputStreams() { return m_aOutputStreams; }
	PluginComm*		getPluginComm() { return m_pPluginComm; }
	void			setPluginComm( PluginComm* comm )
		{
			if( ! m_pPluginComm )
			{
				m_pPluginComm = comm;
				m_pPluginComm->addRef();
			}
		}
	Reference< com::sun::star::lang::XMultiServiceFactory > getServiceManager() { return m_xSMgr; }
    const com::sun::star::plugin::PluginDescription& getDescription() const { return m_aDescription; }
	rtl_TextEncoding getTextEncoding() { return m_aEncoding; }
	NPP				getNPPInstance() { return &m_aInstance; }
	NPWindow*		getNPWindow() { return &m_aNPWindow; }
    SysPlugData&    getSysPlugData() { return m_aSysPlugData; }

	void			enterPluginCallback() { m_nCalledFromPlugin++; }
	void			leavePluginCallback() { m_nCalledFromPlugin--; }
	sal_Bool			isDisposable() { return m_nCalledFromPlugin < 1 ? sal_True : sal_False; }
	DECL_LINK( secondLevelDispose, XPlugin_Impl* );	

	void addPluginEventListener( PluginEventListener* pListener  )
		{ m_aPEventListeners.push_back( pListener ); }
	void checkListeners( const char* normalizedURL );
	
	void			initInstance( 
		const com::sun::star::plugin::PluginDescription& rDescription,
		const Sequence< rtl::OUString >& argn,
		const Sequence< rtl::OUString >& argv,
		sal_Int16 mode );
	void			initInstance( 
		const rtl::OUString& rURL,
		const Sequence< rtl::OUString >& argn,
		const Sequence< rtl::OUString >& argv,
		sal_Int16 mode );

	const rtl::OUString&	getRefererURL() { return m_aURL; }
	::rtl::OUString	getCreationURL();

	PluginStream* getStreamFromNPStream( NPStream* );

	const SystemEnvData* getSysChildSysData()
		{ return _pSysChild->GetSystemData(); }

	const Reference< com::sun::star::plugin::XPluginContext > & getPluginContext() const
		{ return m_rBrowserContext; }
	void setPluginContext( const Reference< com::sun::star::plugin::XPluginContext > & );

	void secondLevelDispose();
	
//	static const Reference< com::sun::star::reflection::XIdlClass > &	staticGetIdlClass();

	// XInterface
	virtual Any	SAL_CALL queryInterface( const Type& ) throw( com::sun::star::uno::RuntimeException );
	virtual void SAL_CALL acquire()  throw()
    { OWeakAggObject::acquire(); }
	virtual void SAL_CALL release()  throw()
    { OWeakAggObject::release(); }

	// OWeakAggObject
	virtual Any SAL_CALL queryAggregation( const Type& )
        throw( com::sun::star::uno::RuntimeException );

	// PluginContol_Impl
	virtual void SAL_CALL dispose() throw();
	virtual void SAL_CALL createPeer( const Reference< com::sun::star::awt::XToolkit > & xToolkit, const Reference< com::sun::star::awt::XWindowPeer > & Parent) throw( RuntimeException );

    virtual sal_Bool SAL_CALL setModel( const Reference< com::sun::star::awt::XControlModel > & Model ) throw( RuntimeException );
    virtual Reference< com::sun::star::awt::XControlModel > SAL_CALL getModel()throw( RuntimeException )
    { return m_xModel; }

	virtual void SAL_CALL setPosSize( sal_Int32 nX_, sal_Int32 nY_, sal_Int32 nWidth_, sal_Int32 nHeight_, sal_Int16 nFlags ) throw( RuntimeException );

	// com::sun::star::plugin::XPlugin
    virtual sal_Bool SAL_CALL provideNewStream(const rtl::OUString& mimetype, const Reference< com::sun::star::io::XActiveDataSource > & stream, const rtl::OUString& url, sal_Int32 length, sal_Int32 lastmodified, sal_Bool isfile) throw();

	// com::sun::star::beans::XPropertyChangeListener
	virtual void SAL_CALL disposing( const com::sun::star::lang::EventObject& rSource ) throw();
	virtual void SAL_CALL propertyChange( const com::sun::star::beans::PropertyChangeEvent& rEvent ) throw();
};

class PluginManager
{
private:
	Reference< com::sun::star::lang::XMultiServiceFactory > 		m_xSMgr;
	::std::list<PluginComm*>		m_aPluginComms;
	::std::list<XPlugin_Impl*>		m_aAllPlugins;
	::osl::Mutex					m_aPluginMutex;

	static PluginManager*		pManager;

	PluginManager();
public:

	static PluginManager& get();
	static void setServiceFactory( const Reference< com::sun::star::lang::XMultiServiceFactory >& xFactory );
	static const Sequence< rtl::OUString >& getAdditionalSearchPaths();

	::std::list<PluginComm*>& getPluginComms() { return m_aPluginComms; }
	::std::list<XPlugin_Impl*>& getPlugins() { return m_aAllPlugins; }
	::osl::Mutex& getPluginMutex() { return m_aPluginMutex; }
};

class XPluginManager_Impl :
	public cppu::WeakAggImplHelper1< com::sun::star::plugin::XPluginManager >
{
	Reference< com::sun::star::lang::XMultiServiceFactory > 	m_xSMgr;
public:
	XPluginManager_Impl( const Reference< com::sun::star::lang::XMultiServiceFactory >  & );
	virtual ~XPluginManager_Impl();

	static XPlugin_Impl* getXPluginFromNPP( NPP );
	static XPlugin_Impl* getPluginImplementation( const Reference< com::sun::star::plugin::XPlugin >& plugin );

	virtual Reference< com::sun::star::plugin::XPluginContext > SAL_CALL createPluginContext() throw();

	// has to be implemented per system
    virtual Sequence< com::sun::star::plugin::PluginDescription > SAL_CALL impl_getPluginDescriptions(void) throw();
    // calls system specific impl_getPluginDescriptions
    // checks whether plugins are disabled
    virtual Sequence< com::sun::star::plugin::PluginDescription > SAL_CALL getPluginDescriptions(void) throw();

    virtual Reference< com::sun::star::plugin::XPlugin > SAL_CALL createPlugin( const Reference< com::sun::star::plugin::XPluginContext > & acontext, sal_Int16 mode, const Sequence< rtl::OUString >& argn, const Sequence< rtl::OUString >& argv, const com::sun::star::plugin::PluginDescription& plugintype) throw( RuntimeException,::com::sun::star::plugin::PluginException );

	virtual Reference< com::sun::star::plugin::XPlugin > SAL_CALL createPluginFromURL( const Reference< com::sun::star::plugin::XPluginContext > & acontext, sal_Int16 mode, const Sequence< rtl::OUString >& argn, const Sequence< rtl::OUString >& argv, const Reference< com::sun::star::awt::XToolkit > & toolkit, const Reference< com::sun::star::awt::XWindowPeer > & parent, const rtl::OUString& url ) throw();

    virtual sal_Bool SAL_CALL supportsService(const rtl::OUString& ServiceName) throw();
	virtual rtl::OUString SAL_CALL getImplementationName() throw();
	
    Sequence< rtl::OUString > SAL_CALL getSupportedServiceNames(void) throw(  );
    static Sequence< rtl::OUString > getSupportedServiceNames_Static(void) throw(  );
	static rtl::OUString 				getImplementationName_Static() throw(  )
	{
		/** the soplayer uses this name in its source! maybe not after 5.2 */
		return rtl::OUString::createFromAscii( "com.sun.star.extensions.PluginManager" );
	}
};
Reference< XInterface >  SAL_CALL PluginManager_CreateInstance( const Reference< com::sun::star::lang::XMultiServiceFactory >  & ) throw( Exception );

enum PluginStreamType { InputStream, OutputStream };

class PluginStream
{
protected:
	XPlugin_Impl*		m_pPlugin;
	NPStream			m_aNPStream;
public:
	PluginStream( XPlugin_Impl* pPlugin,
				   const char* url, sal_uInt32 len, sal_uInt32 lastmod );
	virtual ~PluginStream();

	NPStream* getStream() { return &m_aNPStream; }
	XPlugin_Impl* getPlugin() { return m_pPlugin; }

	virtual PluginStreamType getStreamType() = 0;
};

class PluginInputStream :
	public PluginStream,
	public cppu::WeakAggImplHelper2<
				::com::sun::star::io::XOutputStream,
				::com::sun::star::io::XConnectable
				>
{
private:
	::ucbhelper::Content*		m_pContent;
	sal_Int32					m_nMode;
	sal_uInt32						m_nWritePos;

	Reference< com::sun::star::io::XActiveDataSource >	m_xSource;
	// hold a reference on input until closeOutput is called
	
	Reference< com::sun::star::io::XConnectable >			m_xPredecessor;
	Reference< com::sun::star::io::XConnectable >			m_xSuccessor;

    // needed to hold a reference to self in NP_SEEK mode
    Reference< com::sun::star::io::XOutputStream >			m_xSelf;
	
	SvFileStream				m_aFileStream;
public:
	PluginInputStream( XPlugin_Impl* pPlugin,
				   const char* url, sal_uInt32 len, sal_uInt32 lastmod );

	PluginInputStream() : PluginStream( NULL, NULL, 0, 0 ) {}
	
	virtual ~PluginInputStream();

	virtual PluginStreamType getStreamType();

	void setMode( sal_Int32 nMode );
	sal_uInt32 read( sal_uInt32 offset, sal_Int8* buffer, sal_uInt32 size );
	void setSource( const Reference< com::sun::star::io::XActiveDataSource >& xSource ) { m_xSource = xSource; }
	// get contents ot url via ucbhelper::Content
	void load();

    // clear reference
    bool releaseSelf() 
    { bool bRet = m_xSelf.is(); m_xSelf.clear();  return bRet; }

	// XOutputStream
	virtual void SAL_CALL writeBytes( const Sequence<sal_Int8>& ) throw();
	virtual void SAL_CALL flush() throw();
	virtual void SAL_CALL closeOutput() throw();

	// XConnectable
	virtual void SAL_CALL setPredecessor( const Reference< com::sun::star::io::XConnectable >& xPredecessor ) throw()
		{ m_xPredecessor = xPredecessor; }
	virtual Reference< com::sun::star::io::XConnectable > SAL_CALL getPredecessor() throw()
		{ return m_xPredecessor; }

	virtual void SAL_CALL setSuccessor( const Reference< com::sun::star::io::XConnectable >& xSuccessor ) throw()
		{ m_xSuccessor = xSuccessor; }
	virtual Reference< com::sun::star::io::XConnectable > SAL_CALL getSuccessor() throw()
		{ return m_xSuccessor; }
};

class PluginOutputStream : public PluginStream
{
private:
	Reference< com::sun::star::io::XOutputStream > 	m_xStream;
public:
	PluginOutputStream( XPlugin_Impl* pPlugin, const char* url,
						sal_uInt32 len, sal_uInt32 lastmod );
	virtual ~PluginOutputStream();

	virtual PluginStreamType getStreamType();

	Reference< com::sun::star::io::XOutputStream > & getOutputStream() { return m_xStream; }
};

class PluginEventListener :
	public cppu::WeakAggImplHelper1< com::sun::star::lang::XEventListener >
{
private:
	XPlugin_Impl*	m_pPlugin;
	Reference< com::sun::star::plugin::XPlugin > 		m_xPlugin; // just to hold the plugin
	char*			m_pUrl;
	char*			m_pNormalizedUrl;
	void*			m_pNotifyData;
public:
	PluginEventListener( XPlugin_Impl*,
						 const char* url,
						 const char* normurl,
						 void*  notifyData );
	virtual ~PluginEventListener();

	const char* getURL() { return m_pUrl; }
	const char* getNormalizedURL() { return m_pNormalizedUrl; }
	void*		getNotifyData() { return m_pNotifyData; }
	
	// com::sun::star::lang::XEventListener
	virtual void SAL_CALL disposing( const com::sun::star::lang::EventObject&  Source ) throw();
};

#endif
