/* -*- 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 _ADVISE_UTIL_H_
#define _ADVISE_UTIL_H_

#include <vector>
#include <ace/Synch.h>
#include "IMAPIAdviseSinkC.h"
#include "utils/macros.h"

#include "guid.h"

#include <tao/PortableServer/PortableServer.h>

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

class CMAPIAdviseSink : public virtual IMAPIAdviseSink {
public:
	CMAPIAdviseSink(BRUTUS::IMAPIAdviseSink_ptr BrutusSink,
			LPMAPISESSION MAPISession,
			PortableServer::POA_ptr Poa)
		: ref_count_(1),
		  connection_(0),
		  mapi_session_(MAPISession),
		  poa_(PortableServer::POA::_duplicate(Poa)),
		  brutus_sink_(BRUTUS::IMAPIAdviseSink::_duplicate(BrutusSink))
		{
			mapi_session_->AddRef();
		};

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

			if ((IID_IUnknown == riid) || (IID_IMAPIAdviseSink == riid)) {
				*ppvObj = (LPVOID)this;
				AddRef();
				hr = S_OK;
			}

			return hr;
		};


	STDMETHODIMP_(ULONG) AddRef()
		{
			return InterlockedIncrement(&ref_count_);
		};


	STDMETHODIMP_(ULONG) Release()
		{
			ULONG count = InterlockedDecrement(&ref_count_);
			if (!count)
				delete this;

			return count;
		};

	STDMETHODIMP_(ULONG) OnNotify(ULONG cNotify,
				      LPNOTIFICATION lpNotifications);
private:
	~CMAPIAdviseSink()
		{
			try {
				mapi_session_->Release();
			}
			catch (...) {
				BRUTUS_LOG_ERR("Exception caught");
			}
		};

	LONG ref_count_;
	LONG connection_;
	LPMAPISESSION mapi_session_;
	PortableServer::POA_var poa_;
	BRUTUS::IMAPIAdviseSink_var brutus_sink_;
};


class Sinks {
public:
	Sinks()
		{
			advise_sinks_.resize(0);
		};

	~Sinks()
		{
			ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
			advise_sinks_.resize(0);
		};

	void AddSink(const unsigned long SinkID)
		{
			ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
			advise_sinks_.push_back(SinkID);
		};

	void RemoveSink(const unsigned long SinkID)
		{
			ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
			for (unsigned long n = 0; n < advise_sinks_.size(); n++)
				if (advise_sinks_[n] == SinkID) {
					advise_sinks_.erase(advise_sinks_.begin() + n);
					break;
				}
		};

	void Clear()
		{
			ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
			advise_sinks_.resize(0);
		}

	unsigned long length() const
		{
			return (unsigned long)advise_sinks_.size();
		};

	unsigned long operator[](const unsigned long Index) const
		{
			return advise_sinks_[Index];
		};

private:
	ACE_RW_Mutex mutex_;
	std::vector<unsigned long> advise_sinks_;
};

#endif // _ADVISE_UTIL_H_
