/*
**	wcsutil.h
**	Sean P. Nolan
**	
**	server-side utils
*/

#ifndef _WCSUTIL_H_
#define _WCSUTIL_H_

#include <windows.h>
#include <ole2.h>
#include <winsock.h>
#include <httpext.h>
#include "wcguids.h"

/*--------------------------------------------------------------------------+
|	Utilities Provided by the Framework										|
+--------------------------------------------------------------------------*/

extern LPSTR g_szSSOProgID;

// Logging to application event log -- see ReportEvent() in win32 sdk
// documentation for information on wType and dwEventID.
void LogEvent(WORD wType, DWORD dwEventID, char *sz);

// Asserts
#ifdef DEBUG
extern void AssertProc(PCSTR szFile, DWORD dwLine, PCSTR szMsg, DWORD dwFlags);
#define AssertSzFlg(f, sz, dwFlag)		( (f) ? 0 : AssertProc(__FILE__, __LINE__, sz, dwFlag))
#else // !DEBUG
#define AssertSzFlg(f, sz, dwFlg)
#endif // !DEBUG

#define AP_GETLASTERROR		0x00000001

// under non-debug, AssertSzFlg is defined away, so these #defines don't
// have to be #ifdef DEBUG
#define AssertSz(f, sz)				AssertSzFlg(f, sz, 0)
#define AssertEx(f)					AssertSz((f), "!(" #f ")")
#define Assert(f)					AssertEx(f)
#define AssertSzGLE(f, sz)			AssertSzFlg(f, sz, AP_GETLASTERROR)
#define AssertExGLE(f)				AssertSzGLE((f), "!(" #f ")")

// Debug Printing
#ifdef DEBUG
int dbgprintf(PCSTR pFormat, ...);
#else
#define dbgprintf()
#endif

// Memory Mgmt
LPVOID _MsnAlloc(DWORD cb);
LPVOID _MsnRealloc(LPVOID pv, DWORD cb);
void _MsnFree(LPVOID pv);

// Data Munging
LPSTR _SzFromVariant(VARIANT *pvar);
BSTR _BstrFromVariant(VARIANT *pvar, BOOL *pfFree);
BOOL _FIntFromVariant(VARIANT *pvar, int *pi);

// String Manipulation / Parsing											
void _AnsiStringFromGuid(REFGUID rguid, LPSTR sz);
LPSTR _SkipWhiteSpace(LPSTR sz);
LPSTR _FindEndOfLine(LPSTR sz);
DWORD _atoi(LPSTR sz);

// URL Fiddling
int UrlType(char *szUrl);
#define URL_TYPE_ABSOLUTE		0		// e.g. http://foo/bar or just //foo.com/bar.htm
#define URL_TYPE_LOCAL_ABSOLUTE	1		// e.g. /foo/bar/baz.htm
#define URL_TYPE_RELATIVE		2		// e.g. xyzzy/plugh.html

// IIS Hacks
#ifdef _HTTPEXT_H_
BOOL _FTranslateVirtualRoot(EXTENSION_CONTROL_BLOCK *pecb, LPSTR szPathIn, 
							LPSTR szPathTranslated, DWORD cbPathTranslated);
#endif // _HTTPEXT_H_

// Critical Sections
class CCritSec
	{
	private:
		CRITICAL_SECTION	m_cs;

	public:
		CCritSec(void)		{ ::InitializeCriticalSection(&m_cs);	}
		~CCritSec(void)		{ ::DeleteCriticalSection(&m_cs);		}
		void Lock(void)		{ ::EnterCriticalSection(&m_cs);		}
		void Unlock(void)	{ ::LeaveCriticalSection(&m_cs);		}
	};

/*--------------------------------------------------------------------------+
|	CThingWatcher/CFileWatcher/CRegKeyWatcher								|
+--------------------------------------------------------------------------*/

typedef void (*PFNFILECHANGED)(LPSTR szFile, LONG lUser);
typedef void (*PFNREGKEYCHANGED)(HKEY hkey, LONG lUser);

typedef int (__stdcall *PFNCLOSEHEVTNOTIFY)(HANDLE hevtNotify);

class CThingWatcher
{
	friend DWORD ThingWatcherThread(CThingWatcher *pfw);

public:
	CThingWatcher(PFNCLOSEHEVTNOTIFY pfnCloseHevtNotify);
	~CThingWatcher();
	
	BOOL				FWatchHandle(HANDLE hevtNotify);

private:	
	virtual BOOL		FireChange(DWORD dwWait)			= 0;

	HANDLE				m_rghWait[2];
	HINSTANCE           m_hModule;
	PFNCLOSEHEVTNOTIFY	m_pfnCloseHevtNotify;
};

class CFileWatcher : public CThingWatcher
{
private:
	char			m_szPath[MAX_PATH];
	FILETIME		m_ftLastWrite;
	LONG			m_lUser;
	PFNFILECHANGED	m_pfnChanged;
	BOOL			m_fDirectory;
	HANDLE			m_hevtNotify;

public:
	CFileWatcher(void);
	~CFileWatcher(void);

	BOOL FStartWatching(LPSTR szPath, LONG lUser, PFNFILECHANGED pfnChanged); 

private:
	virtual BOOL FireChange(DWORD dwWait);
};
	
class CRegKeyWatcher : public CThingWatcher
{
public:
	CRegKeyWatcher();
	~CRegKeyWatcher();

	BOOL				FStartWatching(HKEY hkey, BOOL fSubTree, DWORD dwNotifyFilter, LONG lUser, PFNREGKEYCHANGED pfnChanged);

private:
	virtual BOOL		FireChange(DWORD dwWait);
	
	HKEY				m_hkey;
	LONG				m_lUser;
	PFNREGKEYCHANGED	m_pfnChanged;
	HANDLE				m_hevtNotify;
};

/*--------------------------------------------------------------------------+
|	CDataFile																|
+--------------------------------------------------------------------------*/

class CDataFile
	{
	friend class CDataFileGroup;
	friend void DataFileChanged(LPSTR szFile, LONG lUser);

	private:
		ULONG			m_cRef;

		CDataFile	   *m_pdfNext;
		CDataFile	   *m_pdfPrev;
		CDataFileGroup *m_pfg;

	protected:
		CCritSec		m_cs;
		char			m_szDataPath[MAX_PATH];
		CFileWatcher	m_fw;

	public:
		CDataFile(LPSTR szDataPath, CDataFileGroup *pfg);
		~CDataFile(void);

		ULONG		AddRef(void);
		ULONG		Release(void);

		BOOL		FWatchFile(PFNFILECHANGED pfnChanged = NULL);

		virtual void FreeDataFile(void) { delete this; }

	private:
		ULONG		GetRefCount(void);
		BOOL		FMatch(LPSTR szDataPath);

		CDataFile	*GetNext(void)				{ return(m_pdfNext);	}
		CDataFile	*GetPrev(void)				{ return(m_pdfPrev);	}
		void		SetNext(CDataFile *pdf)		{ m_pdfNext = pdf;		}
		void		SetPrev(CDataFile *pdf)		{ m_pdfPrev = pdf;		}
	};

/*--------------------------------------------------------------------------+
|	CDataFileGroup															|
+--------------------------------------------------------------------------*/

#define NUM_GROUP_BUCKETS (1024)

typedef struct _HashBucket
	{
	CDataFile *pdfHead;
	CDataFile *pdfTail;
	}
	HB;

class CDataFileGroup
	{
	private:
		CCritSec	m_cs;
		HB			m_rghb[NUM_GROUP_BUCKETS];
				
	public:
		CDataFileGroup(void);
		~CDataFileGroup(void);

		CDataFile  *GetDataFile(LPSTR szDataPath);
		void		ForgetDataFile(CDataFile *pdf);

		virtual CDataFile *CreateDataFile(LPSTR szDataPath) = 0;

	private:
		void		RememberDataFile(CDataFile *pdf, HB *phb = NULL);
		HB		   *GetHashBucket(LPSTR szDataPath);
	};
	
/*--------------------------------------------------------------------------+
|	CGenericHash															|
+--------------------------------------------------------------------------*/

typedef struct _HashItem
	{
	BSTR				bstrName;
	LPVOID				pvData;
	struct _HashItem	*phiNext;
	struct _HashItem	*phiPrev;
	}
	HITEM;

class CGenericHash
	{
	protected:
		DWORD		m_chi;
		HITEM		**m_rgphi;
		CCritSec	m_cs;

	public:
		CGenericHash(DWORD cBuckets);
		~CGenericHash(void);

		LPVOID PvFind(OLECHAR *wszName);

		BOOL FAdd(OLECHAR *wszName, LPVOID pv);
		void Remove(OLECHAR *wszName);
		void RemoveAll(void);

		void Lock(void)		{ m_cs.Lock(); }
		void Unlock(void)	{ m_cs.Unlock(); }

		virtual DWORD	GetHashValue(OLECHAR *wsz);
		virtual void	FreeHashData(LPVOID pv);

	private:
		HITEM	*FindItem(OLECHAR *wszName, HITEM ***ppphiHead);
		void	RemoveItem(HITEM **pphiHead, HITEM *phi);
		BOOL	FEnsureBuckets(void);
	};

/*--------------------------------------------------------------------------+
|	CResourceCollection														|
+--------------------------------------------------------------------------*/

typedef struct _resource
{
	BOOL			fInUse;
	BOOL			fValid;
	PVOID			pv;
} RS, *PRS;

typedef PRS HRS;
#define hrsNil NULL

class CResourceCollection
{
public:
	CResourceCollection();
	~CResourceCollection();
	
	BOOL			FInit(int cRsrc);
	BOOL			FTerm();
	
	HRS				HrsGetResource();
	void			ReleaseResource(BOOL fReset, HRS hrs);

	void			CleanupAll(PVOID pvNil);
	BOOL			FValid(HRS hrs);

private:
	PRS				PrsFree();
	void			WaitForRs();

	CCritSec		m_cs;
	HANDLE			m_hsem;
	
	int				m_crs;
	PRS				m_rgrs;

	virtual BOOL	FInitResource(PRS prs)		= 0;
	virtual void	CleanupResource(PRS prs)	= 0;
};

/*--------------------------------------------------------------------------+
|	CSocketCollection														|
+--------------------------------------------------------------------------*/

class CSocketCollection : public CResourceCollection
{
public:
	CSocketCollection() : CResourceCollection()		{};
	~CSocketCollection()							{};
	
	BOOL			FInit(int cRsrc, char *szServer, USHORT usPort); // usPort should be in host byte order
	BOOL			FReinit(char *szServer, USHORT usPort);
	
	SOCKET			ScFromHrs(HRS hrs);
	
private:
	char			m_szServer[MAX_PATH];
	int				m_usPort;
	SOCKADDR_IN		m_sin;

	virtual BOOL	FInitResource(PRS prs);
	virtual void	CleanupResource(PRS prs);
};

#endif // _WCSUTIL_H_