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



// SOActiveX.cpp : Implementation of CSOActiveX

#include "stdafx2.h"
#include "so_activex.h"
#include "SOActiveX.h"
#include "SOComWindowPeer.h"

#define STAROFFICE_WINDOWCLASS "SOParentWindow"

#define BARS_NUMBER  3
#define BARS_TO_SHOW 2

OLECHAR* pSlotUrl[BARS_NUMBER] = 
						{L"slot:5910" // SID_TOGGLEFUNCTIONBAR
						,L"slot:5920" // SID_TOGGLESTATUSBAR
						,L"slot:6661" // SID_TOGGLE_MENUBAR
//						,L"slot:10603" // SID_HYPERLINK_INSERT
						};

OLECHAR* pSlotName[BARS_NUMBER] = 
						{L"FunctionBarVisible"		// SID_TOGGLEFUNCTIONBAR
						,L"StatusBarVisible"		// SID_TOGGLESTATUSBAR
						,L"MenuBarVisible"			// SID_TOGGLE_MENUBAR
//						,L"InsertHyperlink"			// SID_HYPERLINK_INSERT
						};



/////////////////////////////////////////////////////////////////////////////

HRESULT ExecuteFunc( IDispatch* idispUnoObject, 
					 OLECHAR* sFuncName, 
					 CComVariant* params, 
					 unsigned int count, 
					 CComVariant* pResult )
{
	if( !idispUnoObject )
		return E_FAIL;

	DISPID id;
	HRESULT hr = idispUnoObject->GetIDsOfNames( IID_NULL, &sFuncName, 1, LOCALE_USER_DEFAULT, &id);
	if( !SUCCEEDED( hr ) ) return hr;

	DISPPARAMS dispparams= { params, 0, count, 0};

	// DEBUG
	EXCEPINFO myInfo;
	return idispUnoObject->Invoke( id, IID_NULL,LOCALE_USER_DEFAULT, DISPATCH_METHOD, 
                    &dispparams, pResult, &myInfo, 0);
}

HRESULT GetIDispByFunc( IDispatch* idispUnoObject, 
					  	OLECHAR* sFuncName, 
					  	CComVariant* params, 
					  	unsigned int count, 
					  	CComPtr<IDispatch>& pdispResult )
{
	if( !idispUnoObject )
		return E_FAIL;

	CComVariant result;
	HRESULT hr = ExecuteFunc( idispUnoObject, sFuncName, params, count, &result );
	if( !SUCCEEDED( hr ) ) return hr;

	if( result.vt != VT_DISPATCH || result.pdispVal == NULL )
		return hr;

	pdispResult = CComPtr<IDispatch>( result.pdispVal );

	return S_OK;
}

HRESULT PutPropertiesToIDisp( IDispatch* pdispObject, 
							  OLECHAR** sMemberNames, 
							  CComVariant* pVariant, 
							  unsigned int count )
{
	for( unsigned int ind = 0; ind < count; ind++ )
	{
		DISPID id;
		HRESULT hr = pdispObject->GetIDsOfNames( IID_NULL, &sMemberNames[ind], 1, LOCALE_USER_DEFAULT, &id );
		if( !SUCCEEDED( hr ) ) return hr;

		hr = CComDispatchDriver::PutProperty( pdispObject, id, &pVariant[ind] );
		if( !SUCCEEDED( hr ) ) return hr;
	}

	return S_OK;
}

/////////////////////////////////////////////////////////////////////////////
// CSOActiveX

CSOActiveX::CSOActiveX()
: mCookie(0)
, mCurFileUrl( L"private:factory/swriter" )
, mbLoad( FALSE )
, mParentWin( NULL )
, mOffWin( NULL )
, mbViewOnly( FALSE )
{
	CLSID clsFactory = {0x82154420,0x0FBF,0x11d4,{0x83, 0x13,0x00,0x50,0x04,0x52,0x6A,0xB4}}; 
	HRESULT hr = CoCreateInstance( clsFactory, NULL, CLSCTX_ALL, __uuidof(IDispatch), (void**)&mpDispFactory);

    mPWinClass.style			= CS_HREDRAW|CS_VREDRAW; 
    mPWinClass.lpfnWndProc		= ::DefWindowProc; 
    mPWinClass.cbClsExtra		= 0; 
    mPWinClass.cbWndExtra		= 0; 
    mPWinClass.hInstance		= (HINSTANCE) GetModuleHandle(NULL); //myInstance; 
    mPWinClass.hIcon			= NULL; 
    mPWinClass.hCursor			= NULL; 
    mPWinClass.hbrBackground	= (HBRUSH) COLOR_BACKGROUND; 
    mPWinClass.lpszMenuName	    = NULL; 
    mPWinClass.lpszClassName	= STAROFFICE_WINDOWCLASS;

	RegisterClass(&mPWinClass);
}

CSOActiveX::~CSOActiveX()
{
	Cleanup();

}

HRESULT CSOActiveX::Cleanup()
{
	if( mpDispFrame && mbViewOnly )
	{
		ShowSomeBars();
		mbViewOnly = FALSE;
	}

	if( mpDispFrame )
	{
		// mpDispFrame->dispose();
		CComVariant dummyResult;
		ExecuteFunc( mpDispFrame, L"dispose", NULL, 0, &dummyResult );
		mpDispFrame = CComPtr< IDispatch >();
	}

	if( ::IsWindow( mOffWin ) )
		::DestroyWindow( mOffWin );

	return S_OK;
}


STDMETHODIMP CSOActiveX::InitNew ()
{
	mbLoad = TRUE;
    return S_OK;
}

STDMETHODIMP CSOActiveX::Load ( LPSTREAM pStm )
{
	mbLoad = TRUE;

	// may be later?
	// for now just ignore
	
    return S_OK;
}

STDMETHODIMP CSOActiveX::Load( LPPROPERTYBAG pPropBag, LPERRORLOG pErrorLog )
{
    IPropertyBag2* pPropBag2;
    HRESULT hr = pPropBag->QueryInterface( IID_IPropertyBag2, (void**)&pPropBag2 );
    ATLASSERT( hr >= 0 );
    
    if( !SUCCEEDED( hr ) )
        return hr;
    
    unsigned long aNum;
    hr = pPropBag2->CountProperties( &aNum );
    ATLASSERT( hr >= 0 );
    if( !SUCCEEDED( hr ) )
        return hr;
        
    PROPBAG2* aPropNames = new PROPBAG2[aNum];
    unsigned long aReaded;
    
    hr = pPropBag2->GetPropertyInfo( 0,
                                     aNum,
                                     aPropNames,
                                     &aReaded );
    ATLASSERT( hr >= 0 );
    if( !SUCCEEDED( hr ) )
    {
        delete[] aPropNames;
        return hr;
    }

	CComVariant* aVal = new CComVariant[aNum];
    HRESULT*     hvs = new HRESULT[aNum];
	hr = pPropBag2->Read( aNum,
                          aPropNames,
                          NULL,
                          aVal, 
                          hvs );
    ATLASSERT( hr >= 0 );
    if( !SUCCEEDED( hr ) )
    {
        delete[] hvs;
        delete[] aVal;
        delete[] aPropNames;
        return hr;
    }

	USES_CONVERSION;
    for( unsigned long ind = 0; ind < aNum; ind++ )
    {
		// all information from the 'object' tag is in strings
		if( aVal[ind].vt == VT_BSTR && !strcmp( OLE2T( aPropNames[ind].pstrName ), "src" ) )
		{
            mCurFileUrl = wcsdup( aVal[ind].bstrVal );
		}
		else if( aVal[ind].vt == VT_BSTR 
				&& !strcmp( OLE2T( aPropNames[ind].pstrName ), "readonly" ) )
		{
			if( !strcmp( OLE2T( aVal[ind].bstrVal ), "true" ) )
			{
				mbViewOnly = TRUE;
			}
			else
			{
				// the default value
				mbViewOnly = FALSE;
			}
		}
    }

    delete[] hvs;
    delete[] aVal;
    delete[] aPropNames;

	if( !mpDispFactory )
		return hr;
	
	mbLoad = TRUE;

    Invalidate();  
    UpdateWindow();

	return hr;
}

HRESULT CSOActiveX::GetUnoStruct( OLECHAR* sStructName, CComPtr<IDispatch>& pdispResult )
{
	return GetIDispByFunc( mpDispFactory, L"Bridge_GetStruct", &CComVariant( sStructName ), 1, pdispResult );
}

HRESULT CSOActiveX::GetUrlStruct( OLECHAR* sUrl, CComPtr<IDispatch>& pdispUrl )
{
	HRESULT hr = GetUnoStruct( L"com.sun.star.util.URL", pdispUrl );
	if( !SUCCEEDED( hr ) ) return hr;

	OLECHAR* sURLMemberName = L"Complete";
	DISPID nURLID;
	hr = pdispUrl->GetIDsOfNames( IID_NULL, &sURLMemberName, 1, LOCALE_USER_DEFAULT, &nURLID );
	if( !SUCCEEDED( hr ) ) return hr;
	hr = CComDispatchDriver::PutProperty( pdispUrl, nURLID, &CComVariant( sUrl ) );
	if( !SUCCEEDED( hr ) ) return hr;

	CComPtr<IDispatch> pdispTransformer;
	hr = GetIDispByFunc( mpDispFactory, 
						 L"createInstance", 
						 &CComVariant( L"com.sun.star.util.URLTransformer" ), 
						 1, 
						 pdispTransformer );
	if( !SUCCEEDED( hr ) ) return hr;

	CComVariant dummyResult;
	CComVariant aInOutParam;
	aInOutParam.ppdispVal = &pdispUrl;
	aInOutParam.vt = VT_DISPATCH | VT_BYREF;
	hr = ExecuteFunc( pdispTransformer, L"parseStrict", &aInOutParam, 1, &dummyResult );
	if( !SUCCEEDED( hr ) || dummyResult.vt != VT_BOOL || !dummyResult.boolVal ) return hr;

	return S_OK;
}


HRESULT CSOActiveX::CreateFrameOldWay( HWND hwnd, int width, int height )
{
	if( !mpDispFactory )
		return E_FAIL;

	// create window handle holder
	CComPtr< CComObject< SOComWindowPeer > > pPeerToSend = new CComObject<SOComWindowPeer>( hwnd );
	pPeerToSend->SetHWNDInternally( hwnd );
	CComQIPtr< IDispatch, &IID_IDispatch > pIDispToSend( pPeerToSend );

	// create rectangle structure
	CComPtr<IDispatch> pdispRectangle;
	HRESULT hr = GetUnoStruct( L"com.sun.star.awt.Rectangle", pdispRectangle );
	if( !SUCCEEDED( hr ) ) return hr;

	OLECHAR* sRectMemberNames[4] = { L"X", 
								 	 L"Y", 
								 	 L"Width", 
								 	 L"Height" };
	CComVariant pRectVariant[4];
	pRectVariant[0] = pRectVariant[1] = pRectVariant[2] = pRectVariant[3] = CComVariant( 0 );

	hr = PutPropertiesToIDisp( pdispRectangle, sRectMemberNames, pRectVariant, 4 );
	if( !SUCCEEDED( hr ) ) return hr;

	// create WindowDescriptor structure
	CComPtr<IDispatch> pdispWinDescr;
	hr = GetUnoStruct( L"com.sun.star.awt.WindowDescriptor", pdispWinDescr );
	if( !SUCCEEDED( hr ) ) return hr;

	// fill in descriptor with info
	OLECHAR* sDescriptorMemberNames[6] = { L"Type", 
								 L"WindowServiceName", 
								 L"ParentIndex", 
								 L"Parent", 
								 L"Bounds",
								 L"WindowAttributes" };
	CComVariant pDescriptorVar[6];
	pDescriptorVar[0] = CComVariant( 0 );
	pDescriptorVar[1] = CComVariant( L"workwindow" );
	pDescriptorVar[2] = CComVariant( 1 );
	pDescriptorVar[3] = CComVariant( pIDispToSend );
	pDescriptorVar[4] = CComVariant( pdispRectangle );
	pDescriptorVar[5] = CComVariant( 33 );
	hr = PutPropertiesToIDisp( pdispWinDescr, sDescriptorMemberNames, pDescriptorVar, 6 );
	if( !SUCCEEDED( hr ) ) return hr;

	// create XToolkit instance
	CComPtr<IDispatch> pdispToolkit;
	hr = GetIDispByFunc( mpDispFactory, L"createInstance", &CComVariant( L"com.sun.star.awt.Toolkit" ), 1, pdispToolkit );
	if( !SUCCEEDED( hr ) ) return hr;

	// create window with toolkit
	hr = GetIDispByFunc( pdispToolkit, L"createWindow", &CComVariant( pdispWinDescr ), 1, mpDispWin );
	if( !SUCCEEDED( hr ) ) return hr;

	// create frame
	hr = GetIDispByFunc( mpDispFactory, L"createInstance", &CComVariant( L"com.sun.star.frame.Task" ), 1, mpDispFrame );
	if( !SUCCEEDED( hr ) || !mpDispFrame )
	{
		// the interface com.sun.star.frame.Task is removed in 6.1
		// but the interface com.sun.star.frame.Frame has some bugs in 6.0
		hr = GetIDispByFunc( mpDispFactory, L"createInstance", &CComVariant( L"com.sun.star.frame.Frame" ), 1, mpDispFrame );
		if( !SUCCEEDED( hr ) ) return hr;
	}

	// initialize frame
	CComVariant dummyResult;
	hr = ExecuteFunc( mpDispFrame, L"initialize", &CComVariant( mpDispWin ), 1, &dummyResult );
	if( !SUCCEEDED( hr ) ) return hr;

	// create desktop
	CComPtr<IDispatch> pdispDesktop;
	hr = GetIDispByFunc( mpDispFactory, L"createInstance", &CComVariant( L"com.sun.star.frame.Desktop" ), 1, pdispDesktop );
	if( !SUCCEEDED( hr ) ) return hr;

	// create tree of frames
	CComPtr<IDispatch> pdispChildren;
	hr = GetIDispByFunc( pdispDesktop, L"getFrames", NULL, 0, pdispChildren );
	if( !SUCCEEDED( hr ) ) return hr;

	// insert new frame into desctop hierarchy
	hr = ExecuteFunc( pdispChildren, L"append", &CComVariant( mpDispFrame ), 1, &dummyResult );
	if( !SUCCEEDED( hr ) ) return hr;

	// initialize window
	hr = ExecuteFunc( mpDispWin, L"setBackground", &CComVariant( (long)0xFFFFFFFF ), 1, &dummyResult );
	if( !SUCCEEDED( hr ) ) return hr;
	
	hr = ExecuteFunc( mpDispWin, L"setVisible", &CComVariant( TRUE ), 1, &dummyResult );
	if( !SUCCEEDED( hr ) ) return hr;

	CComVariant aPosArgs[5];
	aPosArgs[4] = CComVariant( 0 );
	aPosArgs[3] = CComVariant( 0 );
	aPosArgs[2] = CComVariant( width );
	aPosArgs[1] = CComVariant( height );
	aPosArgs[0] = CComVariant( 12 );
	hr = ExecuteFunc( mpDispWin, L"setPosSize", aPosArgs, 5, &dummyResult );
	if( !SUCCEEDED( hr ) ) return hr;


	return S_OK;
}

HRESULT CSOActiveX::CallDispatch1PBool( OLECHAR* sUrl, OLECHAR* sArgName, BOOL sArgVal )
{
	CComPtr<IDispatch> pdispURL;
	HRESULT hr = GetUrlStruct( sUrl, pdispURL );
	if( !SUCCEEDED( hr ) ) return hr;

	CComPtr<IDispatch> pdispXDispatch;
	CComVariant aArgs[3];
	aArgs[2] = CComVariant( pdispURL );
	aArgs[1] = CComVariant( L"" );
	aArgs[0] = CComVariant( (int)0 );
	hr = GetIDispByFunc( mpDispFrame, 
						 L"queryDispatch", 
						 aArgs, 
						 3, 
						 pdispXDispatch );
	if( !SUCCEEDED( hr ) ) return hr;

	SAFEARRAY FAR* pPropVals = SafeArrayCreateVector( VT_DISPATCH, 0, 1 );
	long ix = 0;
	CComPtr<IDispatch> pdispPropVal;
	hr = GetUnoStruct( L"com.sun.star.beans.PropertyValue", pdispPropVal );
	if( !SUCCEEDED( hr ) ) return hr;

	OLECHAR* 	sPropMemberNames[2] = { L"Name", L"Value" };
	CComVariant pPropVar[2];
	pPropVar[0] = CComVariant( sArgName );
	pPropVar[1] = CComVariant(); pPropVar[1].vt = VT_BOOL; pPropVar[1].boolVal = sArgVal ? VARIANT_TRUE : VARIANT_FALSE ;
	hr = PutPropertiesToIDisp( pdispPropVal, sPropMemberNames, pPropVar, 2 );
	if( !SUCCEEDED( hr ) ) return hr;

	SafeArrayPutElement( pPropVals, &ix, pdispPropVal );
	
	CComVariant aDispArgs[2];
	aDispArgs[1] = CComVariant( pdispURL );
	// aDispArgs[0] = CComVariant( pPropVals ); such constructor is not defined ??!
	aDispArgs[0] = CComVariant(); aDispArgs[0].vt = VT_ARRAY | VT_DISPATCH; aDispArgs[0].parray = pPropVals;

	CComVariant dummyResult;
	hr = ExecuteFunc( pdispXDispatch, L"dispatch", aDispArgs, 2, &dummyResult );
	if( !SUCCEEDED( hr ) ) return hr;

	return S_OK;
}

HRESULT CSOActiveX::ShowSomeBars()
{
	// show FunctionBar and StatusBar
	for( int ind = 0; ind < BARS_TO_SHOW; ind ++ )
	{
		HRESULT hr = CallDispatch1PBool( pSlotUrl[ind], pSlotName[ind], TRUE );
		if( !SUCCEEDED( hr ) ) return hr;
	}

	return S_OK;
}

HRESULT CSOActiveX::HideAllBars()
{
	for( int ind = 0; ind < BARS_NUMBER; ind ++ )
	{
		HRESULT hr = CallDispatch1PBool( pSlotUrl[ind], pSlotName[ind], FALSE );
		if( !SUCCEEDED( hr ) ) return hr;
	}

	return S_OK;
}

HRESULT CSOActiveX::LoadURLToFrame( )
{
	HRESULT hr = CallDispatch1PBool( mCurFileUrl, L"ReadOnly", mbViewOnly );
	if( !SUCCEEDED( hr ) ) return hr;

	if( mbViewOnly )
		HideAllBars();

	return S_OK;
}
		
HRESULT CSOActiveX::OnDrawAdvanced( ATL_DRAWINFO& di )
{
    if( m_spInPlaceSite && mCurFileUrl )
    {
    	HWND hwnd;
	    HRESULT hr = m_spInPlaceSite->GetWindow( &hwnd );
		if( !SUCCEEDED( hr ) ) return hr;

        if( mParentWin != hwnd || !mOffWin )
        {
			if( mpDispFrame )
			{
				CComVariant dummyResult;
				ExecuteFunc( mpDispFrame, L"dispose", NULL, 0, &dummyResult );
				mpDispFrame = CComPtr<IDispatch>();
			}

            mParentWin = hwnd;
            mOffWin = CreateWindow( 
								STAROFFICE_WINDOWCLASS,
								"OfficeContainer",
								WS_CHILD | WS_CLIPCHILDREN | WS_BORDER,
								di.prcBounds->left,
								di.prcBounds->top,
								di.prcBounds->right - di.prcBounds->left,
								di.prcBounds->bottom - di.prcBounds->top,
								mParentWin,
								NULL,
								NULL,
								NULL );

			::ShowWindow( mOffWin, SW_SHOW );
        }
        else
        {
            RECT aRect;
            ::GetWindowRect( mOffWin, &aRect );
            
            if( aRect.left !=  di.prcBounds->left || aRect.top != di.prcBounds->top
             || aRect.right != di.prcBounds->right || aRect.bottom != di.prcBounds->bottom )
			{
				// on this state the office window should exist already
                ::SetWindowPos( mOffWin,
                              HWND_TOP,
							  di.prcBounds->left, 
							  di.prcBounds->top, 
							  di.prcBounds->right - di.prcBounds->left, 
							  di.prcBounds->bottom - di.prcBounds->top,
                              SWP_NOZORDER );

				CComVariant aPosArgs[5];
				aPosArgs[4] = CComVariant( 0 );
				aPosArgs[3] = CComVariant( 0 );
				aPosArgs[2] = CComVariant( int(di.prcBounds->right - di.prcBounds->left) );
				aPosArgs[1] = CComVariant( int(di.prcBounds->bottom - di.prcBounds->top) );
				aPosArgs[0] = CComVariant( 12 );
				CComVariant dummyResult;
				hr = ExecuteFunc( mpDispWin, L"setPosSize", aPosArgs, 5, &dummyResult );
				if( !SUCCEEDED( hr ) ) return hr;
			}
        }                      

		if( ! mpDispFrame )
		{
			hr = CreateFrameOldWay( mOffWin,
							di.prcBounds->right - di.prcBounds->left,
							di.prcBounds->bottom - di.prcBounds->top );
			if( !SUCCEEDED( hr ) ) return hr;
		}

		if( mbLoad )
		{
			hr = LoadURLToFrame();
			if( !SUCCEEDED( hr ) ) return hr;
			mbLoad = FALSE;
		}
	}

	return S_OK;
}


STDMETHODIMP CSOActiveX::SetClientSite( IOleClientSite* aClientSite )
{
	HRESULT hr = IOleObjectImpl<CSOActiveX>::SetClientSite( aClientSite );

	if( !aClientSite )
	{
		ATLASSERT( mWebBrowser2 );
		if( mWebBrowser2 )
			AtlUnadvise( mWebBrowser2, DIID_DWebBrowserEvents2, mCookie );
		return hr;
	}

	CComPtr<IOleContainer> aContainer;
	m_spClientSite->GetContainer( &aContainer );
	ATLASSERT( aContainer );

	if( SUCCEEDED( hr )  && aContainer )
	{
		CComQIPtr<IServiceProvider, &IID_IServiceProvider> aServiceProvider( aContainer );
		ATLASSERT( aServiceProvider );

		if( aServiceProvider )
		{
			aServiceProvider->QueryService( SID_SInternetExplorer,
											IID_IWebBrowser,
											(void**)&mWebBrowser2 );
			ATLASSERT( mWebBrowser2 );
			if( mWebBrowser2 )
				AtlAdvise( mWebBrowser2, GetUnknown(), DIID_DWebBrowserEvents2, &mCookie );
		}
	}

	return hr;
}

STDMETHODIMP CSOActiveX::Invoke(DISPID dispidMember, 
							    REFIID riid, 
							    LCID lcid, 
                                WORD wFlags, 
							    DISPPARAMS* pDispParams,
                                VARIANT* pvarResult, 
							    EXCEPINFO* pExcepInfo,
                                UINT* puArgErr)
{
    if (riid != IID_NULL)
        return DISP_E_UNKNOWNINTERFACE;

    if (!pDispParams)
        return DISP_E_PARAMNOTOPTIONAL;

	if ( dispidMember == DISPID_ONQUIT )
		Cleanup();
	
    IDispatchImpl<ISOActiveX, &IID_ISOActiveX, 
                  &LIBID_SO_ACTIVEXLib>::Invoke(
             dispidMember, riid, lcid, wFlags, pDispParams,
             pvarResult, pExcepInfo, puArgErr);

    return S_OK;
}

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

