// This is a part of the Active Template Library.
// Copyright (C) Microsoft Corporation
// All rights reserved.
//
// This source code is only intended as a supplement to the
// Active Template Library Reference and related
// electronic documentation provided with the library.
// See these sources for detailed information regarding the
// Active Template Library product.

#ifndef __ATLSHAREDSVC_H__
#define __ATLSHAREDSVC_H__

#pragma once

#include <atltime.h>
#include <atlsoap.h>
#pragma pack(push,_ATL_PACKING)
namespace ATL{

#ifndef ATL_SHAREDBLOBCACHE_TIMEOUT
	#define ATL_SHAREDBLOBCACHE_TIMEOUT 36000000000 // in 100 nano second intervals
													 // each entry will be free'd if 
													// no access in 1 hour.
#endif

// Interface used by to access the shared blob cache.
[ uuid("AB4AF9CD-8DB1-4974-A617-CF0449578FB9"), object ]
__interface ISharedBlobCache
{
	[id(0)] STDMETHOD(AddItem)([in] BSTR szItemName, [in] BSTR szData);
	[id(1)] STDMETHOD(GetItem)([in] BSTR szItemName, [out,retval] BSTR *szData); 
};

class CSharedCache: 
	public CBlobCache<CWorkerThread<>, CStdStatClass >,
	public IMemoryCacheClient,
	public ISharedBlobCache
{
	typedef CBlobCache<CWorkerThread<>, CStdStatClass > basecache;
public:

	// IMemoryCacheClient method, frees data in the memory cache.
	STDMETHOD( Free )(const void *pvData)
	{
		if (pvData)
		{
			::SysFreeString((BSTR)pvData);
		}
		return S_OK;
	}


	STDMETHODIMP AddItem(BSTR szItemName, BSTR szData)
	{

		HRESULT hr = E_UNEXPECTED;

		// We make a copy of the BSTR and stick it in the cache.
		// The BSTR will be freed in our IMemoryCacheClient::Free
		// implementation above.
		BSTR szEntry = SysAllocString(szData);
		if(szEntry)
		{
			USES_CONVERSION_EX;
			// create a time span and for the entry
			CFileTime tm = CFileTime::GetCurrentTime();
			CFileTimeSpan span;
			span.SetTimeSpan(ATL_SHAREDBLOBCACHE_TIMEOUT);
			tm += span;
			HCACHEITEM h;
			hr = basecache::Add(OLE2A_EX(szItemName, _ATL_SAFE_ALLOCA_DEF_THRESHOLD), szEntry, sizeof(BSTR), 
				&tm, _AtlBaseModule.m_hInst, &h, static_cast<IMemoryCacheClient*>(this));

			if (hr == S_OK)
			{
				// On successful add, we have to release our 
				// reference on the entry.
				basecache::ReleaseEntry(h);
			}
		}
		return hr;
	}

	STDMETHODIMP GetItem(BSTR szItemName, BSTR *szData)
	{
		USES_CONVERSION_EX;
		HRESULT hr = E_UNEXPECTED;
		HCACHEITEM hEntry = NULL;

		if (!szItemName || !szData)
			return hr;

		hr = basecache::LookupEntry(OLE2A_EX(szItemName, _ATL_SAFE_ALLOCA_DEF_THRESHOLD), &hEntry);
		if (hr == S_OK)
		{
			void *pData = NULL;
			DWORD dwSize = 0;
			hr = basecache::GetData(hEntry, &pData, &dwSize);
			if (hr == S_OK)
			{
				// make a copy of the string
				*szData = ::SysAllocString((BSTR)pData);
			}
			basecache::ReleaseEntry(hEntry);
		}
		return hr;
	}


	STDMETHODIMP QueryInterface(REFIID riid, void **ppv)
	{
		HRESULT hr = E_NOINTERFACE;
		if (InlineIsEqualGUID(__uuidof(IMemoryCacheClient), riid)||
			InlineIsEqualGUID(__uuidof(IUnknown), riid))
		{
			*ppv = static_cast<void*>(static_cast<IMemoryCacheClient*>(this));
			hr = S_OK;
		}
		else if( InlineIsEqualGUID(__uuidof(ISharedBlobCache), riid))
		{
			*ppv = static_cast<void*>(static_cast<ISharedBlobCache*>(this));
			hr = S_OK;
		}
		return hr;
	}
	ULONG STDMETHODCALLTYPE AddRef()
	{
		return 1;
	}
	ULONG STDMETHODCALLTYPE Release()
	{
		return 1;
	}
};


// This class implements the SOAP interface for the shared blob cache.
[
	soap_handler(
					name="SharedBlobCache", 
					namespace="http://www.microsoft.com/vc/atlserver/soap/SharedBlobCache",
					protocol="soap"
				),
	request_handler(
					name="SharedBlobCache",
					sdl="GenSharedBlobCacheWSDL"
					)
]
class CSharedCacheHandler:
	public ISharedBlobCache
{
public:
	[soap_method]
	STDMETHOD(AddItem)(BSTR szItemName, BSTR szData)
	{
		if (!m_spMemCache)
			return E_UNEXPECTED;
		return m_spMemCache->AddItem(szItemName, szData);
	}

	[soap_method]
	STDMETHOD(GetItem)(BSTR szItemName, BSTR *szData)
	{	
		if (!m_spMemCache)
			return E_UNEXPECTED;
		return m_spMemCache->GetItem(szItemName, szData);
	}

	HTTP_CODE Initialize(IServiceProvider *pProvider)
	{
		ATLASSERT(pProvider); // should never be NULL
		if (!pProvider)
			return HTTP_ERROR(500, ISE_SUBERR_UNEXPECTED);

		if (m_spMemCache)
			return HTTP_SUCCESS; // already initialized

		pProvider->QueryService(__uuidof(ISharedBlobCache), &m_spMemCache);
		return m_spMemCache ? HTTP_SUCCESS : HTTP_ERROR(500, ISE_SUBERR_UNEXPECTED);
	}

	// override HandleRequest to Initialize our m_spServiceProvider
	// and to handle authorizing the client.
	HTTP_CODE HandleRequest(AtlServerRequest *pRequestInfo, IServiceProvider *pProvider)
	{
		HTTP_CODE dwErr = Initialize(pProvider);
		if (dwErr != HTTP_SUCCESS)
			return dwErr;

		dwErr = CSoapHandler<CSharedCacheHandler>::HandleRequest(pRequestInfo,
								pProvider);
		return dwErr;
	}
	CComPtr<ISharedBlobCache> m_spMemCache;
};

} //ATL

#pragma pack(pop)

#endif // __ATLSHAREDSVC_H__
