//+---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1992 - 1995
//
//  File:       bsscript.h
//
//  Contents:   Script engine classes
//
//----------------------------------------------------------------------------

class CScriptHost;
class CProcessThread;

// Helper class to make initialization
// and freeing of VARIANTARGs foolproof.
// Can be used anywhere a VARIANTARG
// would be used.
class AutoVariant : public VARIANTARG
{
public:
    AutoVariant()
    {
        VariantInit( (VARIANTARG *) this);
    }
    BOOL Set(long value)
    {
        V_VT(this) = VT_I4;
        V_I4(this) = value;
        return TRUE;
    }
    BOOL Set(TCHAR *value)
    {
        V_VT(this) = VT_BSTR;
        V_BSTR(this) = SysAllocString(value); // NULL is a valid value for BSTR
        if (value && !V_BSTR(this))
            return FALSE;
        return TRUE;
    }
    ~AutoVariant()
    {
        VariantClear( (VARIANTARG *) this);
    }
};

//+------------------------------------------------------------------------
//
//  Class:      CScriptSite
//
//  Purpose:    Active scripting site
//
//-------------------------------------------------------------------------

class CScriptSite :
    public IActiveScriptSite,
    public IActiveScriptSiteWindow,
    public IActiveScriptSiteDebug,
    public IProvideMultipleClassInfo,
    public IConnectionPointContainer,
    public IGlobalMTScript
{
public:

    DECLARE_MEMCLEAR_NEW_DELETE();

    CScriptSite(CScriptHost * pSH);
   ~CScriptSite();

    HRESULT Init(LPWSTR pszName);
    void    Close();
    void    Abort();

    // IUnknown methods

    STDMETHOD_(ULONG, AddRef)();
    STDMETHOD_(ULONG, Release)();
    STDMETHOD(QueryInterface)(REFIID, void **);

    // IActiveScriptSite methods

    STDMETHOD(GetLCID)(LCID *plcid);
    STDMETHOD(GetItemInfo)(LPCOLESTR pstrName, DWORD dwReturnMask, IUnknown **ppiunkItem, ITypeInfo **ppti);
    STDMETHOD(GetDocVersionString)(BSTR *pszVersion);
    STDMETHOD(RequestItems)(void);
    STDMETHOD(RequestTypeLibs)(void);
    STDMETHOD(OnScriptTerminate)(const VARIANT *pvarResult, const EXCEPINFO *pexcepinfo);
    STDMETHOD(OnStateChange)(SCRIPTSTATE ssScriptState);
    STDMETHOD(OnScriptError)(IActiveScriptError *pscripterror);
    STDMETHOD(OnEnterScript)(void);
    STDMETHOD(OnLeaveScript)(void);

    // IActiveScriptSiteWindow methods

    STDMETHOD(GetWindow)(HWND *phwnd);
    STDMETHOD(EnableModeless)(BOOL fEnable);

    // IActiveScriptSiteDebug methods

    STDMETHOD(GetDocumentContextFromPosition)(DWORD dwSourceContext,
                                              ULONG uCharacterOffset,
                                              ULONG uNumChars,
                                              IDebugDocumentContext **ppsc);

    STDMETHOD(GetApplication)(IDebugApplication **ppda);
    STDMETHOD(GetRootApplicationNode)(IDebugApplicationNode **ppdanRoot);
    STDMETHOD(OnScriptErrorDebug)(IActiveScriptErrorDebug *pErrorDebug,
                                  BOOL *pfEnterDebugger,
                                  BOOL *pfCallOnScriptErrorWhenContinuing);

    // IProvideClassInfo methods

    STDMETHOD(GetClassInfo)(ITypeInfo **);
    STDMETHOD(GetGUID)(DWORD dwGuidKind, GUID * pGUID);

    // IProvideMultipleClassInfo methods

    STDMETHOD(GetMultiTypeInfoCount)(ULONG *pcti);
    STDMETHOD(GetInfoOfIndex)(ULONG iti, DWORD dwFlags, ITypeInfo** pptiCoClass, DWORD* pdwTIFlags, ULONG* pcdispidReserved, IID* piidPrimary, IID* piidSource);

    // IConnectionPointContainer methods

    STDMETHOD(EnumConnectionPoints)(LPENUMCONNECTIONPOINTS*);
    STDMETHOD(FindConnectionPoint)(REFIID, LPCONNECTIONPOINT*);

    // IBServer methods
    // We need to implement these on a separate identity from
    // the main pad object in order to prevent ref count loops
    // with the script engine.

    STDMETHOD(GetTypeInfoCount)(UINT FAR* pctinfo);

    STDMETHOD(GetTypeInfo)(
      UINT itinfo,
      LCID lcid,
      ITypeInfo FAR* FAR* pptinfo);

    STDMETHOD(GetIDsOfNames)(
      REFIID riid,
      OLECHAR FAR* FAR* rgszNames,
      UINT cNames,
      LCID lcid,
      DISPID FAR* rgdispid);

    STDMETHOD(Invoke)(
      DISPID dispidMember,
      REFIID riid,
      LCID lcid,
      WORD wFlags,
      DISPPARAMS FAR* pdispparams,
      VARIANT FAR* pvarResult,
      EXCEPINFO FAR* pexcepinfo,
      UINT FAR* puArgErr);

    STDMETHOD(get_PublicData)(VARIANT *);
    STDMETHOD(put_PublicData)(VARIANT);
    STDMETHOD(get_PrivateData)(VARIANT *);
    STDMETHOD(put_PrivateData)(VARIANT);
    STDMETHOD(ExitProcess)();
    STDMETHOD(Restart)();
    STDMETHOD(get_LocalMachine)(BSTR *);
    STDMETHOD(Include)(BSTR);
    STDMETHOD(CallScript)(BSTR, VARIANT *);
    STDMETHOD(SpawnScript)(BSTR, VARIANT *);
    STDMETHOD(get_ScriptParam)(VARIANT *);
    STDMETHOD(get_ScriptPath)(BSTR *);
    STDMETHOD(CallExternal)(BSTR, BSTR, VARIANT *, long *);
    STDMETHOD(ResetSync)(const BSTR);
    STDMETHOD(WaitForSync)(BSTR, long, VARIANT_BOOL *);
    STDMETHOD(WaitForMultipleSyncs)(const BSTR, VARIANT_BOOL, long, long *);
    STDMETHOD(SignalThreadSync)(BSTR);
    STDMETHOD(TakeThreadLock)(BSTR);
    STDMETHOD(ReleaseThreadLock)(BSTR);
    STDMETHOD(DoEvents)();
    STDMETHOD(MessageBoxTimeout)(BSTR, long, BSTR, long, long, VARIANT_BOOL, VARIANT_BOOL, long *);
    STDMETHOD(RunLocalCommand)(BSTR, BSTR, BSTR, VARIANT_BOOL, VARIANT_BOOL, VARIANT_BOOL, VARIANT_BOOL, VARIANT_BOOL, long *);
    STDMETHOD(GetLastRunLocalError)(long *);
    STDMETHOD(GetProcessOutput)(long, BSTR *);
    STDMETHOD(GetProcessExitCode)(long, long *);
    STDMETHOD(TerminateProcess)(long);
    STDMETHOD(SendToProcess)(long, BSTR, BSTR, long *);
    STDMETHOD(SendMail)(BSTR, BSTR, BSTR, BSTR, BSTR, BSTR, BSTR, BSTR, long *);
    STDMETHOD(SendSMTPMail)(BSTR, BSTR, BSTR, BSTR, BSTR, BSTR, long *);
    STDMETHOD(ASSERT)(VARIANT_BOOL, BSTR);
    STDMETHOD(OUTPUTDEBUGSTRING)(BSTR);
    STDMETHOD(UnevalString)(BSTR, BSTR*);
    STDMETHOD(CopyOrAppendFile)(BSTR bstrSrc,BSTR bstrDst,long nSrcOffset,long nSrcLength,VARIANT_BOOL  fAppend,long *nSrcFilePosition);
    STDMETHOD(Sleep)(int);
    STDMETHOD(Reboot)();
    STDMETHOD(NotifyScript)(BSTR, VARIANT);
    STDMETHOD(RegisterEventSource)(IDispatch *pDisp, BSTR bstrProgID);
    STDMETHOD(UnregisterEventSource)(IDispatch *pDisp);
    STDMETHOD(get_HostMajorVer)(long *pVer);
    STDMETHOD(get_HostMinorVer)(long *pVer);
    STDMETHOD(get_StatusValue)(long nIndex, long *pnStatus);
    STDMETHOD(put_StatusValue)(long nIndex, long nStatus);

    // Other methods

    HRESULT ExecuteScriptStr(TCHAR * pchScript);
    HRESULT ExecuteScriptFile(TCHAR *pchPath);
    HRESULT SetScriptState(SCRIPTSTATE ss);

    CScriptHost * ScriptHost() { return _pSH; }

    // Member variables
    CStr                        _cstrName;
    ULONG                       _ulRefs;
    CScriptSite *               _pScriptSitePrev;
    IActiveScript *             _pScript;
    CScriptHost*                _pSH;
    TCHAR                       _achPath[MAX_PATH];
    VARIANT                     _varParam;
    IDispatch *                 _pDispSink;
    IDebugDocumentHelper      * _pDDH;  // Script Debugging helper
    DWORD                       _dwSourceContext;
    BOOL                        _fInDebugError;

private:
    BOOL                        _fInScriptError;
};

class AutoCriticalSection : public CRITICAL_SECTION
{
public:
    AutoCriticalSection()
    {
        InitializeCriticalSection(this);
    }
    ~AutoCriticalSection()
    {
        DeleteCriticalSection(this);
    }
};

class CScriptHost :
        public CThreadComm,
        public IGlobalMTScript
{
    friend class CScriptEventSink;

public:
    DECLARE_MEMCLEAR_NEW_DELETE();

    CScriptHost(CMTScript *  pBS,
                BOOL         fPrimary,
                BOOL         fDispatchOnly);

   ~CScriptHost();

    DECLARE_STANDARD_IUNKNOWN(CScriptHost);

    // Script management

    HRESULT LoadTypeLibrary();
    HRESULT PushScript(TCHAR *pchType);
    HRESULT PopScript();
    HRESULT CloseScripts();
    HRESULT AbortScripts();
    HRESULT ExecuteTopLevelScript(TCHAR *pchPath, VARIANT *pvarParams);
    HRESULT ExecuteTopLevelScriptlet(TCHAR *pchScript);

    long    FireScriptErrorEvent(
                                TCHAR *bstrFile,
                                long nLine,
                                long nChar,
                                TCHAR *bstrText,
                                long sCode,
                                TCHAR *bstrSource,
                                TCHAR *bstrDescription);
    long    FireScriptErrorEvent(TCHAR *szMsg);
    void    FireProcessEvent(THREADMSG mt, CProcessThread *pProc);
    void    FireMachineEvent(MACHPROC_EVENT_DATA *pmed, BOOL fExec);
    void    FireEvent(DISPID, UINT cArg, VARIANTARG *pvararg, VARIANTARG *pvarResult);
    void    FireEvent(DISPID, UINT cArg, VARIANTARG *pvararg);
    void    FireEvent(DISPID, LPCTSTR);
    void    FireEvent(DISPID, BOOL);
    void    FireEvent(DISPID, IDispatch *pDisp);

    BOOL    GetMachineDispatch(LPSTR achName,
                               IConnectedMachine **ppMach);

    static HRESULT GetSyncEventName(int nEvent, CStr *pCStr, HANDLE *phEvent);
    static HRESULT GetSyncEvent(LPCTSTR pszName, HANDLE *phEvent);
    // IDispatch interface

    STDMETHOD(GetTypeInfoCount)(UINT FAR* pctinfo);

    STDMETHOD(GetTypeInfo)(
      UINT itinfo,
      LCID lcid,
      ITypeInfo FAR* FAR* pptinfo);

    STDMETHOD(GetIDsOfNames)(
      REFIID riid,
      OLECHAR FAR* FAR* rgszNames,
      UINT cNames,
      LCID lcid,
      DISPID FAR* rgdispid);

    STDMETHOD(Invoke)(
      DISPID dispidMember,
      REFIID riid,
      LCID lcid,
      WORD wFlags,
      DISPPARAMS FAR* pdispparams,
      VARIANT FAR* pvarResult,
      EXCEPINFO FAR* pexcepinfo,
      UINT FAR* puArgErr);

    // IGlobalMTScript interface

    STDMETHOD(get_PublicData)(VARIANT *);
    STDMETHOD(put_PublicData)(VARIANT);
    STDMETHOD(get_PrivateData)(VARIANT *);
    STDMETHOD(put_PrivateData)(VARIANT);
    STDMETHOD(ExitProcess)();
    STDMETHOD(Restart)();
    STDMETHOD(get_LocalMachine)(BSTR *);
    STDMETHOD(Include)(BSTR);
    STDMETHOD(CallScript)(BSTR, VARIANT *);
    STDMETHOD(SpawnScript)(BSTR, VARIANT *);
    STDMETHOD(get_ScriptParam)(VARIANT *);
    STDMETHOD(get_ScriptPath)(BSTR *);
    STDMETHOD(CallExternal)(BSTR, BSTR, VARIANT *, long *);
    STDMETHOD(ResetSync)(const BSTR);
    STDMETHOD(WaitForSync)(BSTR, long, VARIANT_BOOL *);
    STDMETHOD(WaitForMultipleSyncs)(const BSTR, VARIANT_BOOL, long, long *);
    STDMETHOD(SignalThreadSync)(BSTR);
    STDMETHOD(TakeThreadLock)(BSTR);
    STDMETHOD(ReleaseThreadLock)(BSTR);
    STDMETHOD(DoEvents)();
    STDMETHOD(MessageBoxTimeout)(BSTR, long, BSTR, long, long, VARIANT_BOOL, VARIANT_BOOL, long *);
    STDMETHOD(RunLocalCommand)(BSTR, BSTR, BSTR, VARIANT_BOOL, VARIANT_BOOL, VARIANT_BOOL, VARIANT_BOOL, VARIANT_BOOL, long *);
    STDMETHOD(GetLastRunLocalError)(long *);
    STDMETHOD(GetProcessOutput)(long, BSTR *);
    STDMETHOD(GetProcessExitCode)(long, long *);
    STDMETHOD(TerminateProcess)(long);
    STDMETHOD(SendToProcess)(long, BSTR, BSTR, long *);
    STDMETHOD(SendMail)(BSTR, BSTR, BSTR, BSTR, BSTR, BSTR, BSTR, BSTR, long *);
    STDMETHOD(SendSMTPMail)(BSTR, BSTR, BSTR, BSTR, BSTR, BSTR, long *);
    STDMETHOD(ASSERT)(VARIANT_BOOL, BSTR);
    STDMETHOD(OUTPUTDEBUGSTRING)(BSTR);
    STDMETHOD(UnevalString)(BSTR, BSTR*);
    STDMETHOD(CopyOrAppendFile)(BSTR bstrSrc,BSTR bstrDst,long nSrcOffset,long nSrcLength,VARIANT_BOOL  fAppend,long *nSrcFilePosition);
    STDMETHOD(Sleep)(int);
    STDMETHOD(Reboot)();
    STDMETHOD(NotifyScript)(BSTR, VARIANT);
    STDMETHOD(RegisterEventSource)(IDispatch *pDisp, BSTR bstrProgID);
    STDMETHOD(UnregisterEventSource)(IDispatch *pDisp);
    STDMETHOD(get_HostMajorVer)(long *pVer);
    STDMETHOD(get_HostMinorVer)(long *pVer);
    STDMETHOD(get_StatusValue)(long nIndex, long *pnStatus);
    STDMETHOD(put_StatusValue)(long nIndex, long nStatus);

    CScriptSite *  GetSite() { return _pScriptSite; }
    void GetScriptPath(CStr *pcstrPath);

    CMTScript *                 _pMT;
    CScriptSite *               _pScriptSite;

    BOOL                        _fIsPrimaryScript;
    BOOL                        _fMustExitThread;
    BOOL                        _fDontHandleEvents;
    ITypeInfo *                 _pTypeInfoIGlobalMTScript;
    ITypeInfo *                 _pTypeInfoCMTScript;
    ITypeLib *                  _pTypeLibEXE;

    VARIANT                     _vPubCache;
    VARIANT                     _vPrivCache;
    DWORD                       _dwPublicSN;
    DWORD                       _dwPrivateSN;

    long                        _lTimerInterval;

    HRESULT                     _hrLastRunLocalError;

    CStackPtrAry<CScriptEventSink*, 5> _aryEvtSinks;

protected:
    virtual DWORD ThreadMain();
    void HandleThreadMessage();

    enum MEP_RETURN
    {
        MEP_TIMEOUT,       // Timeout period expired
        MEP_EXIT,          // Thread is terminating
        MEP_FALLTHROUGH,   // No event occurred (fWait==FALSE only)
        MEP_EVENT_0,       // The given event(s) are signaled
    };

    DWORD MessageEventPump(BOOL     fWait,
                           UINT     cEvents   = 0,
                           HANDLE * pEvents   = NULL,
                           BOOL     fAll      = FALSE,
                           DWORD    dwTimeout = INFINITE,
                           BOOL     fNoEvents = FALSE);


    HRESULT StringToEventArray(const wchar_t *pszNameList, CStackPtrAry<HANDLE, 5> *aryEvents);
    HRESULT GetLockCritSec(LPTSTR             pszName,
                           CRITICAL_SECTION **ppcs,
                           DWORD            **ppdwOwner);

    struct SYNCEVENT
    {
        CStr   _cstrName;
        HANDLE _hEvent;
    };

    struct THREADLOCK
    {
        CStr             _cstrName;
        CRITICAL_SECTION _csLock;
        DWORD            _dwOwner;
    };

    // MAX_LOCKS is used because you can't move critical section objects
    // in memory once you've initialized them, thus making it impossible to
    // use the dynamic array class.
    #define MAX_LOCKS 10

    // The primary thread owns initialization and cleanup of these objects.
    static CStackDataAry<SYNCEVENT, 5> s_arySyncEvents;
    static THREADLOCK                  s_aThreadLocks[MAX_LOCKS];
    static UINT                        s_cThreadLocks;
    static AutoCriticalSection         s_csSync;
};

//+---------------------------------------------------------------------------
//
//  Class:      CConnectionPoint (ccp)
//
//  Purpose:    Implements IConnectionPoint for the script site
//
//----------------------------------------------------------------------------

class CConnectionPoint : public IConnectionPoint
{
public:

    CConnectionPoint(CScriptSite *pSite);
   ~CConnectionPoint();

    DECLARE_STANDARD_IUNKNOWN(CConnectionPoint);

    STDMETHOD(GetConnectionInterface)(IID * pIID);
    STDMETHOD(GetConnectionPointContainer)(IConnectionPointContainer ** ppCPC);
    STDMETHOD(Advise)(LPUNKNOWN pUnkSink, DWORD * pdwCookie);
    STDMETHOD(Unadvise)(DWORD dwCookie);
    STDMETHOD(EnumConnections)(LPENUMCONNECTIONS * ppEnum);

    CScriptSite *_pSite;
};


//+---------------------------------------------------------------------------
//
//  Class:      CMTEventSink (ces)
//
//  Purpose:    Class which sinks events from objects registered with
//              RegisterEventSource().
//
//----------------------------------------------------------------------------

class CScriptEventSink : public IDispatch
{
public:
    DECLARE_MEMCLEAR_NEW_DELETE();

    CScriptEventSink(CScriptHost *pSH);
   ~CScriptEventSink();

    // IUnknown methods
    DECLARE_STANDARD_IUNKNOWN(CScriptEventSink);

    HRESULT Connect(IDispatch *pSource, BSTR bstrProgID);
    void    Disconnect();

    BOOL    IsThisYourSource(IDispatch * pSource)
                { return pSource == _pDispSource; }

    // IDispatch interface

    STDMETHOD(GetTypeInfoCount)(UINT FAR* pctinfo);

    STDMETHOD(GetTypeInfo)(
      UINT itinfo,
      LCID lcid,
      ITypeInfo FAR* FAR* pptinfo);

    STDMETHOD(GetIDsOfNames)(
      REFIID riid,
      OLECHAR FAR* FAR* rgszNames,
      UINT cNames,
      LCID lcid,
      DISPID FAR* rgdispid);

    STDMETHOD(Invoke)(
      DISPID dispidMember,
      REFIID riid,
      LCID lcid,
      WORD wFlags,
      DISPPARAMS FAR* pdispparams,
      VARIANT FAR* pvarResult,
      EXCEPINFO FAR* pexcepinfo,
      UINT FAR* puArgErr);

private:

    CScriptHost * _pSH;
    IDispatch   * _pDispSource;
    DWORD         _dwSinkCookie;
    IID           _clsidEvents;

};