/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */

/*
 *  Copyright (C) 2008 OMC Denmark ApS.
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#if _MSC_VER > 1000
#pragma once
#endif

#ifndef _ISTREAM_UTILS_H_
#define _ISTREAM_UTILS_H_

#include "IStreamC.h"
#include <objidl.h>

#include "guid.h"
#define MAPIGUID_H // do not include mapiguid.h
#include <mapix.h>

class CStream : public virtual IStream {
public:
	CStream(BRUTUS::IStream_ptr BrutusStream)
		: ref_count_(0),
		  brutus_stream_(BRUTUS::IStream::_duplicate(BrutusStream))
		{
			InitializeCriticalSection(&critical_section_);
			AddRef();
		};

	STDMETHODIMP QueryInterface(REFIID  riid,
				    LPVOID *ppvObj)
		{
			HRESULT hr = E_NOINTERFACE;
			*ppvObj = 0;

			if ((IID_IUnknown == riid) || (IID_ISequentialStream == riid) || (IID_IStream == riid)) {
				*ppvObj = (IStream*)this;
				AddRef();
				hr = S_OK;
			}

			return hr;
		};


	STDMETHODIMP_(ULONG) AddRef()
		{
			EnterCriticalSection(&critical_section_);
			ULONG count = ++ref_count_;
			LeaveCriticalSection(&critical_section_);

			return count;
		};


	STDMETHODIMP_(ULONG) Release()
		{
			EnterCriticalSection(&critical_section_);
			ULONG count = --ref_count_;
			LeaveCriticalSection(&critical_section_);
			if (!count)
				delete this;

			return count;
		};

	virtual HRESULT STDMETHODCALLTYPE Seek(LARGE_INTEGER dlibMove,
					       DWORD dwOrigin,
					       ULARGE_INTEGER *plibNewPosition);

	virtual HRESULT STDMETHODCALLTYPE SetSize(ULARGE_INTEGER libNewSize);

	virtual HRESULT STDMETHODCALLTYPE CopyTo(IStream *pstm,
						 ULARGE_INTEGER cb,
						 ULARGE_INTEGER *pcbRead,
						 ULARGE_INTEGER *pcbWritten);

	virtual HRESULT STDMETHODCALLTYPE Commit(DWORD grfCommitFlags);

	virtual HRESULT STDMETHODCALLTYPE Revert(void);

	virtual HRESULT STDMETHODCALLTYPE LockRegion(ULARGE_INTEGER libOffset,
						     ULARGE_INTEGER cb,
						     DWORD dwLockType);

	virtual HRESULT STDMETHODCALLTYPE UnlockRegion(ULARGE_INTEGER libOffset,
						       ULARGE_INTEGER cb,
						       DWORD dwLockType);

	virtual HRESULT STDMETHODCALLTYPE Stat(STATSTG *pstatstg,
					       DWORD grfStatFlag);

	virtual HRESULT STDMETHODCALLTYPE Clone(IStream **ppstm);

	virtual HRESULT STDMETHODCALLTYPE Read(void *pv,
					       ULONG cb,
					       ULONG *pcbRead);

	virtual HRESULT STDMETHODCALLTYPE Write(const void *pv,
						ULONG cb,
						ULONG *pcbWritten);

private:
	~CStream()
		{
			DeleteCriticalSection(&critical_section_);
			brutus_stream_->Destroy(0);
		};

	LONG ref_count_;
	CRITICAL_SECTION critical_section_;
	BRUTUS::IStream_var brutus_stream_;
};

#endif //  _ISTREAM_UTILS_H_
