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

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

#include "InjectApi.h"

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

cView::~cView()
{
   m_pRoot->m_pBars->RemoveBar( m_nViewID );
   m_pRoot->m_pPanel->RemoveView( m_nViewID );

   m_pRoot->removeView( this );
}

void 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 );

      return;
   }

   // Get the root element and check it out
   loadSchemaObject( pDoc->documentElement, ppRootControl );
}

void 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" ) );

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

   // Fill this into a view param
   _ASSERTE( vIcon.vt != VT_NULL );
   m_nIcon = ( vIcon.vt != VT_NULL ) ? static_cast< long >( vIcon ) + 0x06000000 : 0;

   if( vIconModule.vt == VT_BSTR )
      cManager::_p->LoadResourceModule( vIconModule.bstrVal, &m_nIconModule );
   else
      m_nIconModule = 0;

   if(vLeft.vt == VT_NULL)
	   m_nLeft = 0;
   else
	   m_nLeft = static_cast< long >(vLeft);

   if(vTop.vt == VT_NULL)
	   m_nTop = 70;
   else
	   m_nTop = static_cast< long >(vTop);

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

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

   m_strTitle = vTitle.bstrVal;

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

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

   pControl->QueryInterface( ppRootControl );
}

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" ) );

   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_pRoot->m_pPanel->get_ActiveView( &nPreviousView );
   m_pRoot->m_pPanel->RemoveView( m_nViewID );

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

   // Set the bar params
   ViewParams vp = { m_nIcon, m_nIconModule, m_strTitle, m_nLeft, m_nTop, m_nWidth, m_nHeight };
   m_pRoot->m_pBars->put_Bar( m_nViewID, &vp );

   // Last, create all the controls
   m_pRoot->m_pPanel->LoadView( m_nViewID, this, pRootControl );

   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_strTitle );

	return S_OK;
}

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

   m_strTitle = newVal;

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

   ViewParams vp = { m_nIcon, m_nIconModule, m_strTitle, m_nLeft, m_nTop, m_nWidth, m_nHeight };
   m_pRoot->m_pBars->put_Bar( m_nViewID, &vp );

//   if( nActiveView == m_nViewID )
      // Reload the view
//      m_pRoot->SelectBar( m_nViewID );

	return S_OK;
}

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

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

   m_nIcon = icon;

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

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

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

      break;
   }

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

   ViewParams vp = { m_nIcon, m_nIconModule, m_strTitle, m_nLeft, m_nTop, m_nWidth, m_nHeight};
   m_pRoot->m_pBars->put_Bar( m_nViewID, &vp );

   if( nActiveView == m_nViewID )
      // Reload the view
      m_pRoot->SelectBar( m_nViewID );

	return S_OK;
}

STDMETHODIMP cView::Activate()
{
	long nActiveView;
	m_pRoot->m_pPanel->get_ActiveView( &nActiveView );
	if (nActiveView != m_nViewID)
	{
		m_pRoot->SelectBar( m_nViewID );
	}

	return S_OK;
}

STDMETHODIMP cView::Deactivate()
{
	m_pRoot->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(LPRECT pVal)
{

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

	if( nActiveView == m_nViewID )
	{
	   m_nLeft = pVal->left;
	   m_nTop = pVal->top;
	   m_nWidth = pVal->right;
	   m_nHeight = pVal->bottom;

	   ViewParams vp = {m_nIcon, m_nIconModule, m_strTitle, m_nLeft, m_nTop, m_nWidth, m_nHeight};
	   m_pRoot->m_pPanel->ActivateView(m_nViewID, &vp );
	   Fire_Activate();
	}
	return S_OK;
}

STDMETHODIMP cView::get_Position(LPRECT pVal)
{
	pVal->left = m_nLeft;
	pVal->top = m_nTop;
	pVal->right = m_nWidth;
	pVal->bottom = m_nHeight;
	return S_OK;
}
