// ScriptPlugin.cpp : Implementation of cScriptPlugin
#include "stdafx.h"
#include "PlainText.h"
#include "ScriptPlugin.h"
#include "../Inject/Inject.h"
#include "ScriptView.h"

/////////////////////////////////////////////////////////////////////////////
// cScriptPlugin

void cScriptPlugin::getCLSID( BSTR *pbstrCLSID )
{
   MSXML::IXMLDOMAttributePtr pAttr = m_pPlugin->selectSingleNode( _T( "/plugin/@clsid" ) );

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

   *pbstrCLSID = OLE2BSTR( pAttr->value.bstrVal );
}

void cScriptPlugin::getLanguage( BSTR *pbstrLanguage )
{
   MSXML::IXMLDOMAttributePtr pAttr = m_pPlugin->selectSingleNode( _T( "/plugin/@language" ) );

   if( pAttr.GetInterfacePtr() == NULL )
      *pbstrLanguage = T2BSTR( _T( "VBScript" ) );
   else
      *pbstrLanguage = OLE2BSTR( pAttr->value.bstrVal );
}

HRESULT cScriptPlugin::LoadScript(BSTR strFilename)
{
   USES_CONVERSION;

   HRESULT hRes = ::CoCreateInstance( __uuidof( MSXML::DOMDocument ), NULL, CLSCTX_INPROC_SERVER,
      __uuidof( MSXML::IXMLDOMDocument ), reinterpret_cast< void ** >( &m_pPlugin ) );

   if( FAILED( hRes ) )
      return hRes;

   bool bLoad = !!m_pPlugin->load( strFilename );

   if( !bLoad )
   {
      // The document failed to load, get the error info for posterity
      MSXML::IXMLDOMParseErrorPtr pErr = m_pPlugin->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( NULL, 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 E_FAIL;
   }

   // Looking good

	return S_OK;
}

STDMETHODIMP cScriptPlugin::CreateInstance(IDecalEnum *pInitData, REFIID iid, LPVOID *pObject)
{
   CComVariant vFile;
   HRESULT hRes = pInitData->get_Property( _bstr_t( "File" ), &vFile );
   if( FAILED( hRes ) )
      return hRes;

   if( vFile.vt != VT_BSTR )
      return E_FAIL;

   m_strFilename = vFile.bstrVal;
   return static_cast< IDecalSurrogate * >( this )->QueryInterface( iid, pObject );
}

STDMETHODIMP cScriptPlugin::Initialize( IPluginSite2 *pSite )
{
   USES_CONVERSION;
   m_pSite = pSite;

   // First, map the map and load the script file
   CComPtr< IDecal > pDecal;
   m_pSite->get_Decal( &pDecal );

   CComBSTR strTruePath;
   HRESULT hRes = pDecal->MapPath( m_strFilename, &strTruePath );
   if( FAILED( hRes ) )
      return hRes;

   hRes = LoadScript( strTruePath );
   if( FAILED( hRes ) )
      return hRes;

   // Set up the scripting environment, the default scripting language
   // is VBScript
   CComBSTR strScript;
   
   getLanguage( &strScript );
   hRes = createScriptEngine( strScript );

   _ASSERTE( SUCCEEDED( hRes ) );

   // Load the script text
   CComPtr< IActiveScriptParse > pParse;
   m_pScript->QueryInterface( &pParse );

   pParse->InitNew();

   addNamedItem( _bstr_t( _T( "Site" ) ), static_cast< IDispatch * >( this ) );

   MSXML::IXMLDOMElementPtr pScript = m_pPlugin->selectSingleNode( _T( "/plugin/script" ) );

   // Load the main script block
   if( pScript.GetInterfacePtr() != NULL )
   {
      EXCEPINFO ei;
      HRESULT hRes = pParse->ParseScriptText( pScript->text, NULL, NULL, NULL, 0, 0, 
         SCRIPTITEM_ISVISIBLE, NULL, &ei );

      _ASSERTE( SUCCEEDED( hRes ) );
   } else {
		::MessageBox(NULL, "Couldn't load the main script block", "cScriptPlugin::Initialize", MB_OK);
   }

   // Walk through the intrinsic object
   MSXML::IXMLDOMNodeListPtr pNetworks = m_pPlugin->selectNodes( _T( "/plugin/decal" ) );
   for( MSXML::IXMLDOMElementPtr pNetwork = pNetworks->nextNode(); pNetwork.GetInterfacePtr() != NULL; pNetwork = pNetworks->nextNode() )
   {
      CComPtr< IDispatch > pObject;
      HRESULT hRes = pDecal->get_Object( pNetwork->getAttribute( _T( "path" ) ).bstrVal, IID_IDispatch, reinterpret_cast< void ** >( &pObject ) );
      if( FAILED( hRes ) )
      {
			::MessageBox(NULL, "SCRIPT ERROR: Couldn't load decal object", OLE2A(pNetwork->getAttribute(_T("path")).bstrVal), MB_OK);
			continue;
		}

      // Add the item
      addNamedItem( pNetwork->getAttribute( _T( "name" ) ).bstrVal, pObject );
   }

   // Walk through the instance objects
   MSXML::IXMLDOMNodeListPtr pObjects = m_pPlugin->selectNodes( _T( "/plugin/object" ) );
   for( MSXML::IXMLDOMElementPtr pObject = pObjects->nextNode(); pObject.GetInterfacePtr() != NULL; pObject = pObjects->nextNode() )
   {
      _variant_t vProgid = pObject->getAttribute( _T( "progid" ) );

      if( vProgid.vt == VT_NULL )
      {
         _ASSERT( FALSE );
         continue;
      }

      CComPtr< IDispatch > pInstance;
      CLSID clsid;
      HRESULT hRes = ::CLSIDFromProgID( vProgid.bstrVal, &clsid );

      if( FAILED( hRes ) )
      {
         _ASSERT( FALSE );
         continue;
      }

      hRes = ::CoCreateInstance( clsid, NULL, CLSCTX_INPROC_SERVER, IID_IDispatch, reinterpret_cast< void ** >( &pInstance ) );

      if( FAILED( hRes ) )
      {
         _ASSERT( FALSE );
         continue;
      }

      // Add the item
      addNamedItem( pObject->getAttribute( _T( "name" ) ).bstrVal, pInstance );
   }

   // Start up the plugin
   m_pScript->SetScriptState(SCRIPTSTATE_CONNECTED);
   Fire_Initialize();

   return S_OK;
}

STDMETHODIMP cScriptPlugin::Terminate()
{
   // Shut down the plugin
   Fire_Terminate();

   m_pScript->SetScriptState( SCRIPTSTATE_DISCONNECTED );
   m_pScript->Close();

   m_pSite.Release();
   m_pPlugin.Release();

   return S_OK;
}

 STDMETHODIMP cScriptPlugin::CreateObject( BSTR strProgID, LPDISPATCH *pDisp )
 {
    if( pDisp == NULL )
    {
      _ASSERT( FALSE );
       return E_POINTER;
    }
 
    HRESULT hRes;
    CLSID clsid;
    if( strProgID[ 0 ] == OLESTR( '{' ) )
       hRes = ::CLSIDFromString( strProgID, &clsid );
    else
       hRes = ::CLSIDFromProgID( strProgID, &clsid );
 
    if( FAILED( hRes ) )
      return hRes;

   return ::CoCreateInstance( clsid, NULL, CLSCTX_INPROC_SERVER, IID_IDispatch, reinterpret_cast< void ** >( pDisp ) );
}

STDMETHODIMP cScriptPlugin::WriteToChatWindow(BSTR szText, long lColor)
{
	getPluginSite()->WriteToChatWindow(szText, lColor);

	return S_OK;
}

STDMETHODIMP cScriptPlugin::MessageBox(BSTR Title, BSTR Text)
{
	USES_CONVERSION;

	::MessageBox(NULL, OLE2A(Text), OLE2A(Title), MB_OK);
	return S_OK;
}

IPluginSite * cScriptPlugin::getPluginSite()
{
   CComPtr< IDecal > pDecal;
   m_pSite->get_Decal ( &pDecal );

   CComPtr< IInjectService > pInject;
   CComPtr< IPluginSite > pSite;
   HRESULT hRes = pDecal->get_Object ( _bstr_t ( _T( "services\\DecalPlugins.InjectService" ) ),
      __uuidof ( IInjectService ), reinterpret_cast< LPVOID * > ( &pInject ) );

   pInject->get_Site(&pSite);
	return pSite;
}

STDMETHODIMP cScriptPlugin::LoadView(BSTR Text, IView **pView)
{
	getPluginSite()->LoadView(Text, pView);
	return S_OK;
}

void cScriptPlugin::removeView( CScriptView *pView )
{
   for( cViewList::iterator i = m_views.begin(); i != m_views.end(); ++ i )
   {
      if( *i == pView )
      {
         m_views.erase( i );
         return;
      }
   }

   // The view was not found in the list
   _ASSERTE( FALSE );
}

void cScriptPlugin::postRemoveView( CScriptView *pView )
{
   m_destroy.push_back( pView );
}

STDMETHODIMP cScriptPlugin::CreateView(BSTR strTemplateName, LPDISPATCH *ppNewView)
{
   USES_CONVERSION;

   TCHAR szQuery[ 255 ];
   ::_stprintf( szQuery, _T( "/plugin/view[@name='%s']" ), OLE2T( strTemplateName ) );

   MSXML::IXMLDOMElementPtr pView = m_pPlugin->selectSingleNode( szQuery );

   if( pView.GetInterfacePtr() == NULL )
      return E_INVALIDARG;

   CComObject< CScriptView > *pNewView;
   CComObject< CScriptView >::CreateInstance( &pNewView );

   CComPtr< IDispatch > pDispPlugin;
   m_pScript->GetScriptDispatch( NULL, &pDispPlugin );

   CComBSTR strLanguage;
   getLanguage( &strLanguage );

   pNewView->loadView( this, pView, ppNewView );

	return S_OK;
}