/*************************************************************************
 *
 * 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.
 *
 ************************************************************************/
#include <cstdarg>

#include <sys/types.h>
#include <signal.h>
#include <sys/wait.h>
#include <osl/thread.h>

#include <plugin/impl.hxx>

extern NPNetscapeFuncs aNPNFuncs;

#include <tools/debug.hxx>

using namespace rtl;
using namespace plugstringhelper;

#if OSL_DEBUG_LEVEL > 1
void TRACE( char const * s );
void TRACEN( char const * s, long n );
#else
#define TRACE(x)
#define TRACEN(x,n)
#endif

struct FakeEventRecord : public EventRecord
{
    FakeEventRecord()
    {
        memset( this, 0, sizeof(EventRecord) );
        ::GetGlobalMouse( &where );
        when = ::TickCount();
        modifiers = ::GetCurrentEventKeyModifiers();
    }
};


@interface OOoPluginView : NSView
{
    XPlugin_Impl*   m_pImpl;
    MacPluginComm*  m_pCom;
}
-(id)initWithInstance: (XPlugin_Impl*)i_pImpl pluginComm: (MacPluginComm*)i_pCom frame: (NSRect)i_aRect;
-(void)drawRect: (NSRect)i_aRect;
-(MacOSBOOL)isOpaque;
-(MacOSBOOL)isFlipped;

// NSResponder
-(void)mouseMoved:   (NSEvent*)i_pEvent;
-(void)mouseDown:    (NSEvent*)i_pEvent;
-(void)mouseDragged: (NSEvent*)i_pEvent;
-(void)mouseUp:      (NSEvent*)i_pEvent;
-(void)rightMouseDown:    (NSEvent*)i_pEvent;
-(void)rightMouseDragged: (NSEvent*)i_pEvent;
-(void)rightMouseUp:      (NSEvent*)i_pEvent;
-(void)otherMouseDown:    (NSEvent*)i_pEvent;
-(void)otherMouseDragged: (NSEvent*)i_pEvent;
-(void)otherMouseUp:      (NSEvent*)i_pEvent;
-(void)mouseEntered: (NSEvent*)i_pEvent;
-(void)mouseExited:  (NSEvent*)i_pEvent;
@end

@implementation OOoPluginView
-(id)initWithInstance: (XPlugin_Impl*)i_pImpl pluginComm: (MacPluginComm*)i_pCom frame: (NSRect) i_aRect
{
    if( (self = [super initWithFrame: i_aRect]) )
    {
        m_pImpl = i_pImpl;
        m_pCom = i_pCom;
    }
    return self;
}

-(void)drawRect: (NSRect) i_aRect
{
    m_pCom->drawView( m_pImpl );
}

-(MacOSBOOL)isOpaque
{
    return NO;
}

-(MacOSBOOL)isFlipped
{
    return YES;
}

// NSResponder
-(void)mouseMoved:   (NSEvent*)i_pEvent
{
    FakeEventRecord aRec;
    aRec.what = osEvt + 18; // NPEventType_AdjustCursorEvent
    m_pCom->NPP_HandleEvent( m_pImpl->getNPPInstance(), &aRec );
}

-(void)mouseDown:    (NSEvent*)i_pEvent;
{
    FakeEventRecord aRec;
    aRec.what = mouseDown;
    m_pCom->NPP_HandleEvent( m_pImpl->getNPPInstance(), &aRec );
}

-(void)mouseDragged: (NSEvent*)i_pEvent;
{
    FakeEventRecord aRec;
    aRec.what = aRec.what = osEvt + 18; // NPEventType_AdjustCursorEvent
    m_pCom->NPP_HandleEvent( m_pImpl->getNPPInstance(), &aRec );
}

-(void)mouseUp:      (NSEvent*)i_pEvent;
{
    FakeEventRecord aRec;
    aRec.what = mouseUp;
    m_pCom->NPP_HandleEvent( m_pImpl->getNPPInstance(), &aRec );
}

-(void)rightMouseDown:    (NSEvent*)i_pEvent;
{
    FakeEventRecord aRec;
    aRec.what = mouseDown;
    m_pCom->NPP_HandleEvent( m_pImpl->getNPPInstance(), &aRec );
}

-(void)rightMouseDragged: (NSEvent*)i_pEvent;
{
    FakeEventRecord aRec;
    aRec.what = aRec.what = osEvt + 18; // NPEventType_AdjustCursorEvent
    m_pCom->NPP_HandleEvent( m_pImpl->getNPPInstance(), &aRec );
}

-(void)rightMouseUp:      (NSEvent*)i_pEvent;
{
    FakeEventRecord aRec;
    aRec.what = mouseUp;
    m_pCom->NPP_HandleEvent( m_pImpl->getNPPInstance(), &aRec );
}

-(void)otherMouseDown:    (NSEvent*)i_pEvent;
{
    FakeEventRecord aRec;
    aRec.what = mouseDown;
    m_pCom->NPP_HandleEvent( m_pImpl->getNPPInstance(), &aRec );
}

-(void)otherMouseDragged: (NSEvent*)i_pEvent;
{
    FakeEventRecord aRec;
    aRec.what = aRec.what = osEvt + 18; // NPEventType_AdjustCursorEvent
    m_pCom->NPP_HandleEvent( m_pImpl->getNPPInstance(), &aRec );
}

-(void)otherMouseUp:      (NSEvent*)i_pEvent;
{
    FakeEventRecord aRec;
    aRec.what = mouseUp;
    m_pCom->NPP_HandleEvent( m_pImpl->getNPPInstance(), &aRec );
}

-(void)mouseEntered: (NSEvent*)i_pEvent;
{
    FakeEventRecord aRec;
    aRec.what = aRec.what = osEvt + 18; // NPEventType_AdjustCursorEvent
    m_pCom->NPP_HandleEvent( m_pImpl->getNPPInstance(), &aRec );
}

-(void)mouseExited:  (NSEvent*)i_pEvent;
{
    FakeEventRecord aRec;
    aRec.what = aRec.what = osEvt + 18; // NPEventType_AdjustCursorEvent
    m_pCom->NPP_HandleEvent( m_pImpl->getNPPInstance(), &aRec );
}

@end

//--------------------------------------------------------------------------------------------------
MacPluginComm::MacPluginComm( const rtl::OUString& i_rMimetype, const rtl::OUString& i_rBundle, NSView* i_pParent )
    : PluginComm( OUStringToOString( i_rBundle, RTL_TEXTENCODING_UTF8 ) ),
      m_xBundle( NULL ),
      m_hPlugLib( NULL ),
      m_pNullTimer( NULL )
{
    // initialize plugin function table
    memset( &m_aNPPfuncs, 0, sizeof( m_aNPPfuncs ) );

    // load the bundle
    CFURLRef xURL = createURL( i_rBundle );
    m_xBundle = CFBundleCreate( NULL, xURL );
    CFRelease( xURL );
    if( m_xBundle )
    {
        // ask the plugin library
        // first get its location
        CFURLRef xLibURL = CFBundleCopyExecutableURL( m_xBundle );
        if( xLibURL )
        {
            // get the file system path
            rtl::OUString aModuleURL( CFURLtoOSLURL( xLibURL ) );
            CFRelease( xLibURL );
            m_hPlugLib = osl_loadModule( aModuleURL.pData, SAL_LOADMODULE_DEFAULT );
            #if OSL_DEBUG_LEVEL > 1
            if( ! m_hPlugLib )
                fprintf( stderr, "module %s could not be loaded\n", OUStringToOString( aModuleURL, RTL_TEXTENCODING_UTF8 ).getStr() );
            #endif
        }
        #if OSL_DEBUG_LEVEL > 1
        else
            fprintf( stderr, "bundle %s has no exectutable URL\n", OUStringToOString( i_rBundle, RTL_TEXTENCODING_UTF8 ).getStr() );
        #endif
    }
    else
    {
        #if OSL_DEBUG_LEVEL > 1
        fprintf( stderr, "bundle %s could not be loaded\n", OUStringToOString( i_rBundle, RTL_TEXTENCODING_UTF8 ).getStr() );
        #endif
    }

    DBG_ASSERT( m_xBundle && m_hPlugLib, "loading plugin bundle failed!" );


    m_aNPPfuncs.size = sizeof( m_aNPPfuncs );
    m_aNPPfuncs.version = 0;


    m_eCall = eNP_Initialize;
    execute();
}

//--------------------------------------------------------------------------------------------------
MacPluginComm::~MacPluginComm()
{
    if( m_hPlugLib )
    {
        // NPP_Shutdown();
        NPError (*pShutdown)();
        if( retrieveFunction( "NP_Shutdown", (void**)&pShutdown ) )
        {
            NPError nErr = (*pShutdown)(); (void)nErr;
            DBG_ASSERT( nErr == NPERR_NO_ERROR, "NP_Shutdown() failed!" );
        }
        osl_unloadModule( m_hPlugLib );
    }
    if( m_xBundle )
        CFRelease( m_xBundle );
}

//--------------------------------------------------------------------------------------------------
BOOL MacPluginComm::retrieveFunction( const char* i_pName, void** o_ppFunc ) const
{
    if( ! m_hPlugLib || ! o_ppFunc )
        return FALSE;

    *o_ppFunc = (void*)osl_getAsciiFunctionSymbol( m_hPlugLib, i_pName );

    if( ! *o_ppFunc && m_xBundle )
    {
        rtl::OUString aName( OUString::createFromAscii( *i_pName == '_' ? i_pName+1 : i_pName ) );
        CFStringRef xName = createString( aName );
        if( xName )
        {
            *o_ppFunc =  CFBundleGetFunctionPointerForName( m_xBundle, xName );
            CFRelease( xName );
        }
    }

    return (*o_ppFunc != NULL);
}

IMPL_LINK( MacPluginComm, NullTimerHdl, void*, EMPTYARG )
{
    // note: this is a Timer handler, we are already protected by the SolarMutex

    FakeEventRecord aRec;
    aRec.what = nullEvent;
    aRec.where.h = aRec.where.v = 20000;

    for( std::list< XPlugin_Impl* >::iterator it = m_aNullEventClients.begin();
         it != m_aNullEventClients.end(); ++it )
    {
        SysPlugData& rPlugData( (*it)->getSysPlugData() );
        if( rPlugData.m_pPlugView ) // for safety do not dispatch null events before first NPP_SetWindow
            (*m_aNPPfuncs.event)( (*it)->getNPPInstance(), &aRec );
    }

    return 0;
}

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

long MacPluginComm::doIt()
{
    long nRet = 0;
    switch( m_eCall )
    {
    case eNP_Initialize:
    {
        TRACE( "eNP_Initialize" );
        NPError (*pInit)( NPNetscapeFuncs* );
        if( retrieveFunction( "NP_Initialize", (void**)&pInit ) )
        {
            nRet = (*pInit)( &aNPNFuncs );

            NPError nErr = NPERR_NO_ERROR;
            NPError (*pEntry)( NPPluginFuncs* );
            retrieveFunction( "NP_GetEntryPoints", (void**)&pEntry );
            nErr = (*pEntry)( &m_aNPPfuncs );

            DBG_ASSERT( nErr == NPERR_NO_ERROR, "NP_GetEntryPoints() failed!" );
        }
        else
        {
            nRet = NPERR_GENERIC_ERROR;
        }
        DBG_ASSERT( nRet == NPERR_NO_ERROR, "### NP_Initialize() failed!" );
    }
    break;
    case eNPP_Destroy:
        if( m_aNullEventClients.empty() )
            delete m_pNullTimer, m_pNullTimer = NULL;

        TRACE( "eNPP_Destroy" );
        nRet = (m_aNPPfuncs.destroy
                ? (*m_aNPPfuncs.destroy)(
                    (NPP)m_aArgs[0],
                    (NPSavedData**)m_aArgs[1] )
                : NPERR_GENERIC_ERROR);
        break;
    case eNPP_DestroyStream:
        TRACE( "eNPP_DestroyStream" );
        nRet =  (m_aNPPfuncs.destroystream
                 ? (*m_aNPPfuncs.destroystream)(
                     (NPP)m_aArgs[0],
                     (NPStream*)m_aArgs[1],
                     (NPError)(sal_IntPtr)m_aArgs[2] )
                 : NPERR_GENERIC_ERROR);
        break;
    case eNPP_New:
        TRACE( "eNPP_New" );
        nRet = (m_aNPPfuncs.newp
                ? (*m_aNPPfuncs.newp)(
                    (NPMIMEType)m_aArgs[0],
                    (NPP)m_aArgs[1],
                    (uint16)(sal_IntPtr)m_aArgs[2],
                    (int16)(sal_IntPtr)m_aArgs[3],
                    (char**)m_aArgs[4],
                    (char**)m_aArgs[5],
                    (NPSavedData*)m_aArgs[6] )
                : NPERR_GENERIC_ERROR);

        if( ! m_pNullTimer && m_aNPPfuncs.event )
        {
            m_pNullTimer = new AutoTimer();
            m_pNullTimer->SetTimeout( 50 );
            m_pNullTimer->SetTimeoutHdl( LINK( this, MacPluginComm, NullTimerHdl ) );
            m_pNullTimer->Start();
        }

        break;
    case eNPP_NewStream:
        TRACE( "eNPP_NewStream" );
        nRet = (m_aNPPfuncs.newstream
                ? (*m_aNPPfuncs.newstream)(
                    (NPP)m_aArgs[0],
                    (NPMIMEType)m_aArgs[1],
                    (NPStream*)m_aArgs[2],
                    (NPBool)(sal_IntPtr)m_aArgs[3],
                    (uint16*)m_aArgs[4] )
                : NPERR_GENERIC_ERROR);
        break;
    case eNPP_Print:
        TRACE( "eNPP_Print" );
        if (m_aNPPfuncs.print)
            (*m_aNPPfuncs.print)(
                (NPP)m_aArgs[0],
                (NPPrint*)m_aArgs[1] );
        break;
    case eNPP_SetWindow:
    {
        TRACE( "eNPP_SetWindow" );
        nRet = (m_aNPPfuncs.setwindow
                ? (*m_aNPPfuncs.setwindow)(
                    (NPP)m_aArgs[0],
                    (NPWindow*)m_aArgs[1] )
                : NPERR_GENERIC_ERROR);

        break;
    }
    case eNPP_HandleEvent:
    {
        TRACE( "eNPP_HandleEvent" );
        nRet = (m_aNPPfuncs.event
                ? (*m_aNPPfuncs.event)(
                    (NPP)m_aArgs[0],
                    m_aArgs[1] )
                : NPERR_GENERIC_ERROR);

        break;
    }
    case eNPP_StreamAsFile:
        TRACE( "eNPP_StreamAsFile" );
        if (m_aNPPfuncs.asfile)
            (*m_aNPPfuncs.asfile)(
                (NPP)m_aArgs[0],
                (NPStream*)m_aArgs[1],
                (char*)m_aArgs[2] );
        break;
    case eNPP_URLNotify:
        TRACE( "eNPP_URLNotify" );
        if (m_aNPPfuncs.urlnotify)
            (*m_aNPPfuncs.urlnotify)(
                (NPP)m_aArgs[0],
                (char*)m_aArgs[1],
                (NPReason)(sal_IntPtr)m_aArgs[2],
                m_aArgs[3] );
        break;
    case eNPP_Write:
        TRACEN( "eNPP_Write n=", (int32)m_aArgs[3] );
        nRet = (m_aNPPfuncs.write
                ? (*m_aNPPfuncs.write)(
                    (NPP)m_aArgs[0],
                    (NPStream*)m_aArgs[1],
                    (int32)m_aArgs[2],
                    (int32)m_aArgs[3],
                    m_aArgs[4] )
                : 0);
        break;
    case eNPP_WriteReady:
        TRACE( "eNPP_WriteReady" );
        nRet = (m_aNPPfuncs.writeready
                ? (*m_aNPPfuncs.writeready)(
                    (NPP)m_aArgs[0],
                    (NPStream*)m_aArgs[1] )
                : 0);
        break;
    case eNPP_GetValue:
        TRACE( "eNPP_GetValue" );
        nRet = (m_aNPPfuncs.getvalue
                ? (*m_aNPPfuncs.getvalue)(
                    (NPP)m_aArgs[0],
                    (NPPVariable)(int)m_aArgs[1],
                    m_aArgs[2] )
                : NPERR_GENERIC_ERROR);
        break;
    case eNPP_SetValue:
        TRACE( "eNPP_SetValue" );
        nRet = (m_aNPPfuncs.setvalue
                ? (*m_aNPPfuncs.setvalue)(
                    (NPP)m_aArgs[0],
                    (NPNVariable)(int)m_aArgs[1],
                    m_aArgs[2] )
                : NPERR_GENERIC_ERROR);
        break;
    case eNPP_Shutdown:
    {
        TRACE( "eNPP_Shutdown" );
        NPP_ShutdownUPP pFunc;
        if (retrieveFunction( "NPP_Shutdown", (void**)&pFunc ))
            (*pFunc)();
    }
    break;
    case eNPP_Initialize:
        TRACE( "eNPP_Initialize" );
        OSL_ENSURE( false, "NPP_Initialize: not implemented!" );
        break;
    case eNPP_GetJavaClass:
        TRACE( "eNPP_GetJavaClass" );
        OSL_ENSURE( false, "NPP_GetJavaClass: not implemented!" );
        break;
    }
    return nRet;
}

//--------------------------------------------------------------------------------------------------
NPError MacPluginComm::NPP_Destroy( XPlugin_Impl* i_pImpl, NPSavedData** save )
{
    // remove from NullEvent timer
    m_aNullEventClients.remove( i_pImpl );

    NPError nErr = NPP_Destroy( i_pImpl->getNPPInstance(), save );

    // release plugin view
    SysPlugData& rPlugData( i_pImpl->getSysPlugData() );
    if( rPlugData.m_pPlugView )
    {
        [rPlugData.m_pPlugView removeFromSuperview];
        [rPlugData.m_pPlugView release];
        rPlugData.m_pPlugView = nil;
    }

    return nErr;
}


NPError MacPluginComm::NPP_Destroy( NPP instance, NPSavedData** save )
{
    DBG_ASSERT( m_aNPPfuncs.destroy, "### NPP_Destroy(): null pointer in NPP functions table!" );
    m_eCall = eNPP_Destroy;
    m_aArgs[0] = (void*)instance;
    m_aArgs[1] = (void*)save;
    return (NPError)execute();
}

//--------------------------------------------------------------------------------------------------
NPError MacPluginComm::NPP_DestroyStream( NPP instance, NPStream* stream, NPError reason )
{
    DBG_ASSERT( m_aNPPfuncs.destroystream, "### NPP_DestroyStream(): null pointer in NPP functions table!" );
    m_eCall = eNPP_DestroyStream;
    m_aArgs[0] = (void*)instance;
    m_aArgs[1] = (void*)stream;
    m_aArgs[2] = (void*)reason;
    return (NPError)execute();
}

//--------------------------------------------------------------------------------------------------
NPError MacPluginComm::NPP_New( NPMIMEType pluginType, NPP instance, uint16 mode, int16 argc,
                                  char* argn[], char* argv[], NPSavedData *saved )
{
    XPlugin_Impl* pImpl = XPluginManager_Impl::getXPluginFromNPP( instance );
    DBG_ASSERT( pImpl, "no instance found" );

    if( pImpl ) // sanity check
        m_aNullEventClients.push_back( pImpl );

    DBG_ASSERT( m_aNPPfuncs.newp, "### NPP_New(): null pointer in NPP functions table!" );
    #if OSL_DEBUG_LEVEL > 1
    fprintf( stderr, "NPP_New( %s. %p, %d, %d",
             pluginType, instance, (int)mode, (int)argc );
    for( int16 i = 0; i < argc; i++ )
    fprintf( stderr, "\n%s = %s", argn[i], argv[i] );
    fprintf( stderr, ", %p )\n", saved );
    #endif
    m_eCall = eNPP_New;
    m_aArgs[0] = (void*)pluginType;
    m_aArgs[1] = (void*)instance;
    m_aArgs[2] = (void*)mode;
    m_aArgs[3] = (void*)argc;
    m_aArgs[4] = (void*)argn;
    m_aArgs[5] = (void*)argv;
    m_aArgs[6] = (void*)saved;

    return (NPError)execute();
}

//--------------------------------------------------------------------------------------------------
NPError MacPluginComm::NPP_NewStream( NPP instance, NPMIMEType type, NPStream* stream,
                                        NPBool seekable, uint16* stype )
{
    DBG_ASSERT( m_aNPPfuncs.newstream, "### NPP_NewStream(): null pointer in NPP functions table!" );
    m_eCall = eNPP_NewStream;
    m_aArgs[0] = (void*)instance;
    m_aArgs[1] = (void*)type;
    m_aArgs[2] = (void*)stream;
    m_aArgs[3] = (void*)seekable;
    m_aArgs[4] = (void*)stype;
    return (NPError)execute();
}

//--------------------------------------------------------------------------------------------------
void MacPluginComm::NPP_Print( NPP instance, NPPrint* platformPrint )
{
    DBG_ASSERT( m_aNPPfuncs.print, "### NPP_Print(): null pointer in NPP functions table!" );
    m_eCall = eNPP_Print;
    m_aArgs[0] = (void*)instance;
    m_aArgs[1] = (void*)platformPrint;
    execute();
}

//--------------------------------------------------------------------------------------------------
NPError MacPluginComm::NPP_SetWindow( NPP instance, NPWindow* window )
{
    DBG_ASSERT( m_aNPPfuncs.setwindow, "### NPP_SetWindow(): null pointer in NPP functions table!" );
    m_eCall = eNPP_SetWindow;
    m_aArgs[0] = (void*)instance;
    m_aArgs[1] = (void*)window;
    return (NPError)execute();
}

//--------------------------------------------------------------------------------------------------
NPError MacPluginComm::NPP_HandleEvent( NPP instance, void* event )
{
    DBG_ASSERT( m_aNPPfuncs.event, "### NPP_HandleEvent(): null pointer in NPP functions table!" );
    m_eCall = eNPP_HandleEvent;
    m_aArgs[0] = (void*)instance;
    m_aArgs[1] = event;
    return (NPError)execute();
}

//--------------------------------------------------------------------------------------------------
void MacPluginComm::NPP_StreamAsFile( NPP instance, NPStream* stream, const char* fname )
{
    DBG_ASSERT( m_aNPPfuncs.asfile, "### NPP_StreamAsFile(): null pointer in NPP functions table!" );
    m_eCall = eNPP_StreamAsFile;
    m_aArgs[0] = (void*)instance;
    m_aArgs[1] = (void*)stream;
    m_aArgs[2] = (void*)fname;
    execute();
}

//--------------------------------------------------------------------------------------------------
void MacPluginComm::NPP_URLNotify( NPP instance, const char* url, NPReason reason, void* notifyData )
{
    DBG_ASSERT( m_aNPPfuncs.urlnotify, "### NPP_URLNotify(): null pointer in NPP functions table!" );
    m_eCall = eNPP_URLNotify;
    m_aArgs[0] = (void*)instance;
    m_aArgs[1] = (void*)url;
    m_aArgs[2] = (void*)reason;
    m_aArgs[3] = notifyData;
    execute();
}

//--------------------------------------------------------------------------------------------------
int32 MacPluginComm::NPP_Write( NPP instance, NPStream* stream, int32 offset, int32 len, void* buffer )
{
    DBG_ASSERT( m_aNPPfuncs.write, "### NPP_Write(): null pointer in NPP functions table!" );
    m_eCall = eNPP_Write;
    m_aArgs[0] = (void*)instance;
    m_aArgs[1] = (void*)stream;
    m_aArgs[2] = (void*)offset;
    m_aArgs[3] = (void*)len;
    m_aArgs[4] = buffer;
    return (NPError)execute();
}

//--------------------------------------------------------------------------------------------------
int32 MacPluginComm::NPP_WriteReady( NPP instance, NPStream* stream )
{
    DBG_ASSERT( m_aNPPfuncs.writeready, "### NPP_WriteReady(): null pointer in NPP functions table!" );
    m_eCall = eNPP_WriteReady;
    m_aArgs[0] = (void*)instance;
    m_aArgs[1] = (void*)stream;
    return execute();
}

//--------------------------------------------------------------------------------------------------
NPError MacPluginComm::NPP_GetValue( NPP instance, NPPVariable variable, void *ret_value )
{
    DBG_ASSERT( m_aNPPfuncs.getvalue, "### NPP_GetValue(): null pointer in NPP functions table!" );
    m_eCall = eNPP_GetValue;
    m_aArgs[0] = (void*)instance;
    m_aArgs[1] = (void*)variable;
    m_aArgs[2] = ret_value;
    return (NPError)execute();
}

//--------------------------------------------------------------------------------------------------
NPError MacPluginComm::NPP_SetValue( NPP instance, NPNVariable variable, void *set_value )
{
    DBG_ASSERT( m_aNPPfuncs.setvalue, "### NPP_SetValue(): null pointer in NPP functions table!" );
    m_eCall = eNPP_SetValue;
    m_aArgs[0] = (void*)instance;
    m_aArgs[1] = (void*)variable;
    m_aArgs[2] = set_value;
    return (NPError)execute();
}

//--------------------------------------------------------------------------------------------------
void * MacPluginComm::NPP_GetJavaClass()
{
    DBG_ERROR( "no java class available!" );
    return 0;
}

//--------------------------------------------------------------------------------------------------
NPError MacPluginComm::NPP_Initialize()
{
    return NPERR_NO_ERROR;
}

//--------------------------------------------------------------------------------------------------
void MacPluginComm::NPP_Shutdown()
{
    m_eCall = eNPP_Shutdown;
    execute();
}

//--------------------------------------------------------------------------------------------------
NPError MacPluginComm::NPP_SetWindow( XPlugin_Impl* i_pImpl )
{
    // update window NPWindow from view
    SysPlugData& rPlugData( i_pImpl->getSysPlugData() );

    // update plug view
    NSRect aPlugRect = [rPlugData.m_pParentView frame];
    aPlugRect.origin.x = aPlugRect.origin.y = 0;
    if( ! rPlugData.m_pPlugView )
    {
        rPlugData.m_pPlugView = [[OOoPluginView alloc] initWithInstance: i_pImpl pluginComm: this frame: aPlugRect];
        [rPlugData.m_pParentView addSubview: rPlugData.m_pPlugView];
    }
    else
        [rPlugData.m_pPlugView setFrame: aPlugRect];

    NPWindow* pNPWin = i_pImpl->getNPWindow();
    NSWindow* pWin = [rPlugData.m_pPlugView window];
    NSRect aWinRect = [pWin frame];
    NSRect aBounds = [rPlugData.m_pPlugView frame];
    NSRect aVisibleBounds = [rPlugData.m_pPlugView visibleRect];

    #if OSL_DEBUG_LEVEL > 1
    fprintf( stderr, "visible bounds = %d+%d+%dx%d\n",
             (int)aVisibleBounds.origin.x, (int)aVisibleBounds.origin.y,
             (int)aVisibleBounds.size.width, (int)aVisibleBounds.size.height );
    #endif

    NSPoint aViewOrigin = [rPlugData.m_pPlugView convertPoint: NSZeroPoint toView: nil];
    // save view origin so we can notice movement of the view in drawView
    // in case of a moved view we need to reset the port/context
    rPlugData.m_aLastPlugViewOrigin = aViewOrigin;

    // convert view origin to topdown coordinates
    aViewOrigin.y = aWinRect.size.height - aViewOrigin.y;

    // same for clipping
    NSPoint aClipOrigin = [rPlugData.m_pPlugView convertPoint: aVisibleBounds.origin toView: nil];
    aClipOrigin.y = aWinRect.size.height - aClipOrigin.y;

    #if OSL_DEBUG_LEVEL > 1
    fprintf( stderr, "view origin: %d+%d, clip origin = %d+%d\n",
             (int)aViewOrigin.x, (int)aViewOrigin.y,
             (int)aClipOrigin.x, (int)aClipOrigin.y );
    #endif

    pNPWin->x                = aViewOrigin.x;
    pNPWin->y                = aViewOrigin.y;
    pNPWin->width            = aBounds.size.width;
    pNPWin->height           = aBounds.size.height;
    pNPWin->clipRect.left    = aClipOrigin.x;
    pNPWin->clipRect.top     = aClipOrigin.y;
    pNPWin->clipRect.right   = aClipOrigin.x + aVisibleBounds.size.width;
    pNPWin->clipRect.bottom  = aClipOrigin.y + aVisibleBounds.size.height;

    if( rPlugData.m_nDrawingModel == 1 )
    {
        rPlugData.m_aCGContext.window = reinterpret_cast<WindowRef>([pWin windowRef]);
        pNPWin->window = &rPlugData.m_aCGContext;
        rPlugData.m_aCGContext.context = reinterpret_cast<CGContextRef>([[pWin graphicsContext] graphicsPort]);
        #if OSL_DEBUG_LEVEL > 1
        fprintf( stderr, "window is %p, context is %p\n",
                 rPlugData.m_aCGContext.window, rPlugData.m_aCGContext.context );
        #endif
    }
    else
    {
        rPlugData.m_aNPPort.port = GetWindowPort( reinterpret_cast<WindowRef>([pWin windowRef]) );
        rPlugData.m_aNPPort.portx = aClipOrigin.x;
        rPlugData.m_aNPPort.porty = aClipOrigin.y;
        pNPWin->window = &rPlugData.m_aNPPort;
        #if OSL_DEBUG_LEVEL > 1
        fprintf( stderr, "port is %p at (%d,%d)\n",
                 rPlugData.m_aNPPort.port, (int)rPlugData.m_aNPPort.portx, (int)rPlugData.m_aNPPort.porty );
        #endif
    }

    if( pNPWin->width == 0 || pNPWin->height == 0 || [rPlugData.m_pPlugView isHiddenOrHasHiddenAncestor] )
        rPlugData.m_bSetWindowOnDraw = true;

    NPError nErr = NPP_SetWindow( i_pImpl->getNPPInstance(), i_pImpl->getNPWindow() );

    return nErr;
}

void MacPluginComm::drawView( XPlugin_Impl* i_pImpl )
{
    SysPlugData& rPlugData( i_pImpl->getSysPlugData() );

    // check if the view was moved since the last SetWindow
    NSPoint aViewOrigin = [rPlugData.m_pPlugView convertPoint: NSZeroPoint toView: nil];
    if( rPlugData.m_bSetWindowOnDraw ||
        aViewOrigin.x != rPlugData.m_aLastPlugViewOrigin.x ||
        aViewOrigin.y != rPlugData.m_aLastPlugViewOrigin.y )
    {
        NPP_SetWindow( i_pImpl );
        rPlugData.m_bSetWindowOnDraw = false;
    }

    // send a paint event
    NSWindow* pWin = [rPlugData.m_pPlugView window];
    FakeEventRecord aRec;
    aRec.what       =  updateEvt;
    aRec.message    = (UInt32)[pWin windowRef];
    this->NPP_HandleEvent( i_pImpl->getNPPInstance(), &aRec );
}

