// Copyright (c) 1999 Microsoft Corporation. All rights reserved.
//
// Declaration of CActiveScriptManager.
//

// CActiveScriptManager handles interfacing with VBScript or any activeX scripting
// language.  It intializes an IActiveScript object, sends it code, and sets and gets
// the values of variables.  Used by CDirectMusicScript.

#pragma once
#include "ole2.h"
#include "activscp.h"
#include "scriptthread.h"
#include "..\shared\dmusicp.h"

// forward declaration
class CDirectMusicScript;

// little helper class to cache routine and variable names for EnumItem
class ScriptNames
{
public:
	ScriptNames() : m_prgbstr(NULL) {}
	~ScriptNames() { Clear(); }
	HRESULT Init(bool fUseOleAut, DWORD cNames);
	operator bool() { return !!m_prgbstr; }
	DWORD size() { return m_dwSize; }
	void Clear();
	BSTR &operator[](DWORD dwIndex) { assert(m_prgbstr && dwIndex < m_dwSize); return m_prgbstr[dwIndex]; }

private:
	bool m_fUseOleAut;
	DWORD m_dwSize;
	BSTR *m_prgbstr;
};

class CActiveScriptManager
  : public IActiveScriptSite,
	public ScriptManager
{
public:
	CActiveScriptManager(
		bool fUseOleAut,
		const WCHAR *pwszLanguage,
		const WCHAR *pwszSource,
		CDirectMusicScript *pParentScript,
		HRESULT *phr,
		DMUS_SCRIPT_ERRORINFO *pErrorInfo);
	HRESULT Start(DMUS_SCRIPT_ERRORINFO *pErrorInfo);
	HRESULT CallRoutine(const WCHAR *pwszRoutineName, DMUS_SCRIPT_ERRORINFO *pErrorInfo);
	HRESULT ScriptTrackCallRoutine(
				const WCHAR *pwszRoutineName,
				IDirectMusicSegmentState *pSegSt,
				DWORD dwVirtualTrackID,
				bool fErrorPMsgsEnabled,
				__int64 i64IntendedStartTime,
				DWORD dwIntendedStartTimeFlags);
	HRESULT SetVariable(const WCHAR *pwszVariableName, VARIANT varValue, bool fSetRef, DMUS_SCRIPT_ERRORINFO *pErrorInfo);
	HRESULT GetVariable(const WCHAR *pwszVariableName, VARIANT *pvarValue, DMUS_SCRIPT_ERRORINFO *pErrorInfo);
	HRESULT EnumItem(bool fRoutine, DWORD dwIndex, WCHAR *pwszName, int *pcItems);
	HRESULT DispGetIDsOfNames(REFIID riid, LPOLESTR __RPC_FAR *rgszNames, UINT cNames, LCID lcid, DISPID __RPC_FAR *rgDispId);
	HRESULT DispInvoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS __RPC_FAR *pDispParams, VARIANT __RPC_FAR *pVarResult, EXCEPINFO __RPC_FAR *pExcepInfo, UINT __RPC_FAR *puArgErr);
	void Close(); // Releases all references in preparation for shutdown

	// IUnknown
	STDMETHOD(QueryInterface)(const IID &iid, void **ppv);
	STDMETHOD_(ULONG, AddRef)();
	STDMETHOD_(ULONG, Release)();

	// IActiveScriptSite
	STDMETHOD(GetLCID)(/* [out] */ LCID __RPC_FAR *plcid);
	STDMETHOD(GetItemInfo)(
		/* [in] */ LPCOLESTR pstrName,
		/* [in] */ DWORD dwReturnMask,
		/* [out] */ IUnknown __RPC_FAR *__RPC_FAR *ppiunkItem,
		/* [out] */ ITypeInfo __RPC_FAR *__RPC_FAR *ppti);
	STDMETHOD(GetDocVersionString)(/* [out] */ BSTR __RPC_FAR *pbstrVersion);
	STDMETHOD(OnScriptTerminate)(
		/* [in] */ const VARIANT __RPC_FAR *pvarResult,
		/* [in] */ const EXCEPINFO __RPC_FAR *pexcepinfo);
	STDMETHOD(OnStateChange)(/* [in] */ SCRIPTSTATE ssScriptState);
	STDMETHOD(OnScriptError)(/* [in] */ IActiveScriptError __RPC_FAR *pscripterror);
	STDMETHOD(OnEnterScript)();
	STDMETHOD(OnLeaveScript)();

	// Retrieve context for the currently running script.
	// Some automation model functions need access to the context from which the
	//    currently running routine was called.  For example, they may need to operate
	//    on the implied global performance.
	// Be sure to addref the returned pointer if holding onto it.
	static IDirectMusicPerformance8 *GetCurrentPerformanceNoAssertWEAK();
	static IDirectMusicPerformance8 *GetCurrentPerformanceWEAK() { IDirectMusicPerformance8 *pPerf = CActiveScriptManager::GetCurrentPerformanceNoAssertWEAK(); if (!pPerf) {assert(false);} return pPerf; }
	static IDirectMusicObject *GetCurrentScriptObjectWEAK();
	static IDirectMusicComposer8 *GetComposerWEAK();
	static void GetCurrentTimingContext(__int64 *pi64IntendedStartTime, DWORD *pdwIntendedStartTimeFlags);

private:
	// Functions
	HRESULT GetIDOfName(const WCHAR *pwszName, DISPID *pdispid); // returns S_FALSE for unknown name
	void ClearErrorInfo();
	void SetErrorInfo(ULONG ulLineNumber, LONG ichCharPosition, BSTR bstrSourceLine, const EXCEPINFO &excepinfo);
	void ContributeErrorInfo(const WCHAR *pwszActivity, const WCHAR *pwszSubject, const EXCEPINFO &excepinfo);
	HRESULT ReturnErrorInfo(HRESULT hr, DMUS_SCRIPT_ERRORINFO *pErrorInfo);
	static CActiveScriptManager *GetCurrentContext();
	static HRESULT SetCurrentContext(CActiveScriptManager *pActiveScriptManager, CActiveScriptManager **ppActiveScriptManagerPrevious); // remember to restore the previous pointer after the call
	HRESULT EnsureEnumItemsCached(bool fRoutine);

	// Data
	long m_cRef;

	// Pointer back to the containing script object
	CDirectMusicScript *m_pParentScript;

	// Active Scripting
	bool m_fUseOleAut;
	IActiveScript *m_pActiveScript;
	IDispatch *m_pDispatchScript;

	// Errors (managed via ClearErrorInfo, SetErrorInfo, and ContributeErrorInfo)
	bool m_fError;
	HRESULT m_hrError;
	ULONG m_ulErrorLineNumber;
	LONG m_ichErrorCharPosition;
	BSTR m_bstrErrorSourceComponent;
	BSTR m_bstrErrorDescription;
	BSTR m_bstrErrorSourceLineText;
	BSTR m_bstrHelpFile;

	// Context
	struct ThreadContextPair
	{
		DWORD dwThreadId;
		CActiveScriptManager *pActiveScriptManager;
	};
	static SmartRef::Vector<ThreadContextPair> ms_svecContext;

	// Timing context for a routine call from a script track.  (Sets the play/stop time of segments, songs, and playingsegments
	// to the time of the routine in the script track.)
	__int64 m_i64IntendedStartTime;
	DWORD m_dwIntendedStartTimeFlags;

	// cached names from enum methods
	ScriptNames m_snamesRoutines;
	ScriptNames m_snamesVariables;
};