// View.cpp : Implementation of cView
#include "stdafx.h"
#include <stdio.h>
#include "Inject.h"
#include "View.h"

#include "RootLayer.h"
#include "Manager.h"

#include "InjectApi.h"

/////////////////////////////////////////////////////////////////////////////
// cView

cView::~cView( )
{
	m_bActivated = false;
	m_pRoot->m_pBars->RemoveBar( m_nViewID );
	m_pPanel->RemoveView( m_nViewID );
	m_pRoot->removeView( this );

	long nActiveView;
	m_pPanel->get_ActiveView( &nActiveView );
	m_pRoot->SelectBar( nActiveView );
	m_pRoot->SelectBar( nActiveView );

	m_pPanel.Release();

	if( cManager::_p->m_bContainer )
		cManager::_p->clearDestroyList( );
}

long cView::loadSchema( BSTR strSchema, IUnknown **ppRootControl )
{
	USES_CONVERSION;

	_ASSERTE( strSchema != NULL );

	// Create a new XML document and load this up
	MSXML::IXMLDOMDocumentPtr pDoc;
	pDoc.CreateInstance( __uuidof( MSXML::DOMDocument ), NULL, CLSCTX_INPROC_SERVER );

	VARIANT_BOOL bSuccess;
	if( strSchema[ 0 ] == OLESTR( '<' ) )
		bSuccess = pDoc->loadXML( strSchema );
	else
	{
		// Load from a file source
		pDoc->async = VARIANT_FALSE;

		TCHAR szPath[ MAX_PATH ];
		bSuccess = pDoc->load( ::InjectMapPath( eInjectPathAgent, OLE2T( strSchema ), szPath ) );
	}

	if( !bSuccess )
	{
		USES_CONVERSION;

		// The document failed to load, get the error info for posterity
		MSXML::IXMLDOMParseErrorPtr pErr = pDoc->parseError;

		long nCode = pErr->errorCode;
		long nFilePos = pErr->filepos;
		long nLine = pErr->line;
		long nLinePos = pErr->linepos;
		_bstr_t strReason = pErr->reason;
		_bstr_t strText = pErr->srcText;

		TCHAR szError[ 1024 ];
		::_stprintf( szError, _T( "0x%08X (%i, %i): %s" ),
			nCode, nLine, nLinePos, OLE2T( strReason ) );

		::MessageBox( cManager::_p->m_hMain, szError, _T( "XML Parse Error" ), MB_ICONERROR | MB_OK );

		// Give the user a chance to break and look at this lovely info
		_ASSERTE( FALSE );
	  pDoc.Release( );

		return 0;
	}

	// Get the root element and check it out
	long lViewFlags = loadSchemaObject( pDoc->documentElement, ppRootControl );
	pDoc.Release( );

	return lViewFlags;
}

long cView::loadSchemaObject( IUnknown *pObject, IUnknown **ppRootControl )
{
	MSXML::IXMLDOMElementPtr pRoot = pObject;

	_ASSERTE( pRoot->tagName == _bstr_t( _T( "view" ) ) );

	// Get the view parameters
	_variant_t vIconModule = pRoot->getAttribute( _T( "iconlibrary" ) ),
		vIcon = pRoot->getAttribute( _T( "icon" ) ),
		vTitle = pRoot->getAttribute( _T( "title" ) ),
	  vLeft = pRoot->getAttribute( _T( "left" ) ),
	  vTop = pRoot->getAttribute( _T( "top" ) ),
	  vWidth = pRoot->getAttribute( _T( "width" ) ),
	  vHeight = pRoot->getAttribute( _T( "height" ) ),
	  vTrans = pRoot->getAttribute( _T( "transparent" ) );

	// We *must* have a title
	_ASSERTE( vTitle.vt == VT_BSTR );

	// Fill this into a view param
	if( vIconModule.vt == VT_BSTR )
		cManager::_p->LoadResourceModule( vIconModule.bstrVal, &m_VP.iconLibrary );
	else
		m_VP.iconLibrary = 0;

// GKusnick: Handle no-icon case without asserting.
//	_ASSERTE( vIcon.vt != VT_NULL );
	m_VP.icon = ( vIcon.vt != VT_NULL ) ? static_cast< long >( vIcon ) : 0;
	// Para: shouldn't add  0x06000000 if there's an iconlibrary
	if (m_VP.iconLibrary == 0 && m_VP.icon != 0)
	  m_VP.icon +=  0x06000000;

	if(vLeft.vt != VT_NULL)
		m_VP.left = static_cast< long >(vLeft);
	else
		m_VP.left = 25;

	if(vTop.vt != VT_NULL)
		m_VP.top = static_cast< long >(vTop);
	else
		m_VP.top = 25;

	if(vWidth.vt == VT_NULL)
		m_VP.width = 180;
	else
		m_VP.width = static_cast< long >(vWidth);

	if(vHeight.vt == VT_NULL)
	{
		SIZE szScreen;
		cManager::_p->GetScreenSize( &szScreen );
		m_VP.height = szScreen.cy/2;
	}
	else
		m_VP.height = static_cast< long >(vHeight);

	if(vTrans.vt == VT_NULL)
		m_bTransparent = false;
	else
		m_bTransparent = static_cast< bool >( vTrans );

	if( m_bTransparent )
		m_VP.alpha = 255;
	else
		m_VP.alpha = -1;

	m_VP.label = _bstr_t(vTitle.bstrVal).copy();

	// The properly made schema should have a single control child
	MSXML::IXMLDOMElementPtr pControl = pRoot->selectSingleNode( _T( "control" ) );

	_ASSERTE( pControl.GetInterfacePtr() != NULL );

	pControl->QueryInterface( ppRootControl );

	long lViewFlags = 0;

	if( m_bTransparent )
		lViewFlags |= eTransparent;

	return lViewFlags;
}

STDMETHODIMP cView::get_Control(BSTR strName, IControl **pVal)
{
	_ASSERTE( strName != NULL );
	_ASSERTE( pVal != NULL );

	// Search for a matching control name
	for( cNamedControlList::iterator i = m_controls.begin(); i != m_controls.end(); ++ i )
	{
		if( i->m_strName == _bstr_t( strName ) )
		{
			*pVal = i->m_pControl;
			( *pVal )->AddRef();

			return S_OK;
		}
	}

	// The name was not found
	return E_INVALIDARG;
}

STDMETHODIMP cView::putref_Control(BSTR strName, IControl *newVal)
{
	_ASSERTE( strName != NULL );

	// First look for a matching name
	for( cNamedControlList::iterator i = m_controls.begin(); i != m_controls.end(); ++ i )
	{
		if( i->m_strName == _bstr_t( strName ) )
		{
			if( newVal == NULL )
				m_controls.erase( i );
			else
				i->m_pControl = newVal;

			return S_OK;
		}
	}

	if( newVal == NULL )
		// No value to set
		return S_OK;

	// Add in a new record
	cNamedControl nc;
	nc.m_strName = strName;
	nc.m_pControl = newVal;

	m_controls.push_back( nc );

	return S_OK;
}

STDMETHODIMP cView::LoadControl(ILayerSite *pParent, long nID, IUnknown *pSource, long *pAssignedID)
{
	// Usual parameter validation
	_ASSERTE( pParent != NULL );
	_ASSERTE( pSource != NULL );
	_ASSERTE( pAssignedID != NULL );

	MSXML::IXMLDOMElementPtr pElement = pSource;

	// Make sure we're starting from the correct tag name
	_ASSERTE( pElement->tagName == _bstr_t( _T( "control" ) ) );

	// Get the progid and make an instance
	_variant_t strProgID = pElement->getAttribute( _T( "progid" ) );
	_ASSERTE( strProgID.vt == VT_BSTR );

	CLSID clsid;
	HRESULT hRes = ::CLSIDFromProgID( strProgID.bstrVal, &clsid );

	_ASSERTE( SUCCEEDED( hRes ) );

	CComPtr< ILayer > pChildLayer;
	hRes = ::CoCreateInstance( clsid, NULL, CLSCTX_INPROC_SERVER, IID_ILayer,
		reinterpret_cast< void ** >( &pChildLayer ) );

	_ASSERTE( SUCCEEDED( hRes ) );

	// Get some position metrics - note, these are optional and will be
	// loaded with 0s - this is for cases when the control is formatted
	// entirely by the parent
	_variant_t vLeft = pElement->getAttribute( _T( "left" ) ), 
		vTop = pElement->getAttribute( _T( "top" ) ),
		vWidth = pElement->getAttribute( _T( "width" ) ), 
		vHeight = pElement->getAttribute( _T( "height" ) ), 
		vUnclipped = pElement->getAttribute( _T( "unclipped" ) ),
		vID = pElement->getAttribute( _T( "ID" ) );
	// check for lower case, since all other attributes are lower case. And
	// Legiondale_Superman can't seem to get it right...
	if (vID.vt == VT_NULL ) {
		vID = pElement->getAttribute( _T( "id" ) );
	}

	long nRealID = ( vID.vt == VT_NULL ) ? nID : static_cast< long >( vID );
	*pAssignedID = nRealID;

	POINT pt = { ( vLeft.vt != VT_NULL ) ? static_cast< long >( vLeft ) : 0,
		( vTop.vt != VT_NULL ) ? static_cast< long >( vTop ) : 0 };

	LayerParams lp = { nRealID, { pt.x, pt.y, pt.x + ( ( vWidth.vt != VT_NULL ) ? static_cast< long >( vWidth ) : 0 ),
		pt.y + ( ( vHeight.vt != VT_NULL ) ? static_cast< long >( vHeight ) : 0 ) }, 
		( vUnclipped.vt != VT_NULL ) ? 0 : eRenderClipped };

	pParent->CreateChild( &lp, pChildLayer );

	// Check if this child is named
	_variant_t vName = pElement->getAttribute( _T( "name" ) );

	if( vName.vt == VT_BSTR )
	{
		// Add the named control
		CComPtr< IControl > pControl;
		pChildLayer->QueryInterface( &pControl );
		putref_Control( vName.bstrVal, pControl );
	}

	// Ok, the child is made - trick it into loading it's own parameters
	CComPtr< ILayerSchema > pSchema;

	if( SUCCEEDED( pChildLayer->QueryInterface( &pSchema ) ) )
		pSchema->SchemaLoad( this, pSource );

	return S_OK;
}

STDMETHODIMP cView::LoadSchema(BSTR strXMLSchema)
{
	long nPreviousView;
	m_pPanel->get_ActiveView( &nPreviousView );
	m_pPanel->RemoveView( m_nViewID );

	CComPtr< IUnknown > pRootControl;
	loadSchema( strXMLSchema, &pRootControl );

	// Set the bar params
	m_pRoot->m_pBars->put_Bar( m_nViewID, &m_VP );

	long lViewFlags = 0;

	if( m_bTransparent )
		lViewFlags |= eTransparent;
  
	// Last, create all the controls
	m_pPanel->LoadViewEx( m_nViewID, this, pRootControl, lViewFlags );

	if( nPreviousView == m_nViewID )
		// Reactivate the view
		m_pRoot->SelectBar( m_nViewID );

	return S_OK;
}

STDMETHODIMP cView::get_Title(BSTR *pVal)
{
	USES_CONVERSION;

	*pVal = OLE2BSTR( m_VP.label );

	return S_OK;
}

STDMETHODIMP cView::put_Title(BSTR newVal)
{
	_ASSERTE( newVal != NULL );

	bool bActivated = m_bActivated;

	m_VP.label = _bstr_t( newVal ).copy( );
	m_pRoot->m_pBars->put_Bar( m_nViewID, &m_VP );

	if( bActivated )
			m_pPanel->Deactivate(), m_pRoot->SelectBar( m_nViewID );

	return S_OK;
}

STDMETHODIMP cView::Alert()
{
	return S_OK;
}

STDMETHODIMP cView::SetIcon(long icon, VARIANT iconlibrary)
{
	USES_CONVERSION;

	m_VP.icon = icon;

	switch( iconlibrary.vt )
	{
	case VT_ERROR:
	case VT_EMPTY:
	case VT_NULL:
		m_VP.iconLibrary = 0;
		break;

	case VT_BSTR:
		m_VP.iconLibrary = reinterpret_cast< long >( ::GetModuleHandle( OLE2T( iconlibrary.bstrVal ) ) );
		break;

	default:
		// Try and convert everything else to a number
		try
		{
			_variant_t v = iconlibrary;
			m_VP.iconLibrary = v;
		}
		catch( ... )
		{
			m_VP.iconLibrary = 0;
			return E_INVALIDARG;
		}

		break;
	}

	m_pRoot->m_pBars->put_Bar( m_nViewID, &m_VP );
	
	return S_OK;
}

STDMETHODIMP cView::Activate()
{
	long nActiveView;
	m_pPanel->get_ActiveView( &nActiveView );

	if (nActiveView != m_nViewID)
		m_pRoot->SelectBar( m_nViewID );

	return S_OK;
}

STDMETHODIMP cView::Deactivate()
{
	m_pPanel->Deactivate();
	if(cManager::_p->m_pKeyboard!=NULL)
	{
		cManager::_p->m_pKeyboard->sendKeyboardEndCapture( VARIANT_TRUE );
		cManager::_p->m_pKeyboard=NULL;
	}
	return S_OK;
}

STDMETHODIMP cView::put_Position(RECT *newVal)
{
	m_VP.left = newVal->left;
	m_VP.top = newVal->top;
	m_VP.width = newVal->right;
	m_VP.height = newVal->bottom;

	/* Drakier: make sure they aren't being bad little kittens and
							moving the view offscreen! */
	// Drakier: Get the pSite and Screen Size

	CComPtr< IPluginSite > pPlugin;
	CComPtr< IACHooks > pHooks;
	m_pRoot->m_pSite->get_PluginSite( &pPlugin );

	SIZE szScreen, sz;
	pPlugin->GetScreenSize( &sz );
	szScreen.cx = sz.cx;
	szScreen.cy = sz.cy;

	long lX = sz.cx, lY = sz.cy;
	HRESULT hW = S_FALSE, hH = S_FALSE;

	// Drakier: if we are not in the container, get the 3D Area
	if ( !cManager::_p->m_bContainer )
	{
		pPlugin->get_Hooks( &pHooks );
		hH = pHooks->get_Area3DHeight( &lY );
		hW = pHooks->get_Area3DWidth( &lX );
		pHooks.Release();
	}
	pPlugin.Release();

	// Drakier: if we are in the container, or the 3D area's are NULL
	if ( (cManager::_p->m_bContainer) || (hW != S_OK) || (hH != S_OK) )
	{
		szScreen.cx = sz.cx - 308;	// 308 = PANEL_SIZE
		szScreen.cy = sz.cy;
	}
	else // Drakier: If everything comes out right and we are not in container
	{
		if( (hW == S_OK) &&  (lX > 200) && (lX < 2000) )
			szScreen.cx = lX;
		if( (hH == S_OK) && (lY > 200) && (lY < 2000) )
			szScreen.cy = lY;
	}

	if ( (szScreen.cx > 200) && (szScreen.cx < 2000) && (szScreen.cy > 200) && (szScreen.cy < 2000) )
	{
		if ( (m_VP.left + m_VP.width) > szScreen.cx )
			m_VP.left = szScreen.cx - m_VP.width;
		if ( (m_VP.top + m_VP.height) > szScreen.cy )
			m_VP.top = szScreen.cy - m_VP.height;
		if ( m_VP.left < 0 )
			m_VP.left = 0;
		if ( m_VP.top < 0 )
			m_VP.top = 0;

		long nActiveView;	
			m_pPanel->get_ActiveView( &nActiveView ); 
	 
		if( nActiveView == m_nViewID )	
			m_pPanel->ActivateView(m_nViewID, &m_VP, (long*)this);
	}

	return S_OK;
}

STDMETHODIMP cView::get_Position(RECT *pVal)
{
	pVal->left = m_VP.left;
	pVal->top = m_VP.top;
	pVal->right = m_VP.width;
	pVal->bottom = m_VP.height;
	
	return S_OK;
}

STDMETHODIMP cView::get_Alpha(long *pVal)
{
	if( pVal == NULL )
		return E_POINTER;

	if (m_bTransparent) {
		*pVal = m_lOldAlpha ;
	} else {
		*pVal = m_VP.alpha;
	}
	return S_OK;
}

STDMETHODIMP cView::put_Alpha(long Alpha)
{
	if( m_bTransparent )
		return S_FALSE;

	m_VP.alpha = Alpha;

	long nActiveView;	
	m_pPanel->get_ActiveView( &nActiveView );	
	 
	 if( nActiveView == m_nViewID )	
		m_pPanel->ActivateView(m_nViewID, &m_VP, (long*)this); 

	return S_OK;
}

STDMETHODIMP cView::get_Activated(VARIANT_BOOL *pVal)
{
	*pVal = ( m_bActivated ? VARIANT_TRUE : VARIANT_FALSE );
	return S_OK;
}

STDMETHODIMP cView::put_Activated(VARIANT_BOOL newVal)
{
	if( newVal == VARIANT_FALSE )
		return Deactivate();

	else
		return Activate();
}

STDMETHODIMP cView::get_Transparent(VARIANT_BOOL *pVal)
{
	*pVal = ( m_bTransparent ? VARIANT_TRUE : VARIANT_FALSE );
	return S_OK;
}

STDMETHODIMP cView::put_Transparent(VARIANT_BOOL newVal)
{
	if( newVal == VARIANT_FALSE )
	{
		if( !m_bTransparent )
			return S_OK;

		m_bTransparent = false;
		m_VP.alpha = m_lOldAlpha;
		m_pPanel->put_Transparent( VARIANT_FALSE );
	}
	else
	{
		if( m_bTransparent )
			return S_OK;

		m_bTransparent = true;
		m_lOldAlpha = m_VP.alpha;
		m_VP.alpha = 255;
		m_pPanel->put_Transparent( VARIANT_TRUE );
	}

	return S_OK;
}