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

#include <vector>

#include "IMAPISessionS.h"
#include "BrutusLogonC.h"
#include "utils/macros.h"
#include "utils/poa_utils.h"
#include "advise_utils.h"

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

#include "Brutus_Loader_Impl_Export.h"

class Brutus_Loader_Impl_Export BRUTUS_IMAPISession_i : public virtual POA_BRUTUS::IMAPISession,
							public virtual ::PortableServer::RefCountServantBase
{
public:
	BRUTUS_IMAPISession_i(LPMAPISESSION MAPISession,
			      ::PortableServer::POA_ptr Poa,
			      ::CORBA::ORB_ptr orb,
			      ::BRUTUS::BrutusCheck_ptr LifeLine,
			      const unsigned long LifeCheckDelay,
			      void (*shutDown)(void));

	virtual ::BRUTUS::BRESULT GetLastError(::BRUTUS::BRESULT ReturnCode,
					       ::BRUTUS::BDEFINE ulFlags,
					       ::BRUTUS::MAPIERROR_out lppMAPIError);

	virtual ::BRUTUS::BRESULT GetMsgStoresTable(::BRUTUS::BDEFINE ulFlags,
						    ::BRUTUS::IMAPITable_out lppTable);

	virtual ::BRUTUS::BRESULT OpenMsgStore(const ::BRUTUS::ENTRYID &lpEntryID,
					       const char *lpInterface,
					       ::BRUTUS::BDEFINE ulFlags,
					       ::BRUTUS::IMsgStore_out lppMDB);

	virtual ::BRUTUS::BRESULT OpenAddressBook(const char *lpInterface,
						  ::BRUTUS::BDEFINE ulFlags,
						  ::BRUTUS::IAddrBook_out lppAdrBook);

	virtual ::BRUTUS::BRESULT OpenProfileSection(const char *lpUID,
						     const char *lpInterface,
						     ::BRUTUS::BDEFINE ulFlags,
						     ::BRUTUS::IProfSect_out lppProfSect);

	virtual ::BRUTUS::BRESULT GetStatusTable(::BRUTUS::BDEFINE ulFlags,
						 ::BRUTUS::IMAPITable_out lppTable);

	virtual ::BRUTUS::BRESULT OpenEntry(const ::BRUTUS::ENTRYID &lpEntryID,
					    const char *lpInterface,
					    ::BRUTUS::BDEFINE ulFlags,
					    ::BRUTUS::BDEFINE_out lpulObjType,
					    ::BRUTUS::IUnknown_out lppUnk);

	virtual ::BRUTUS::BRESULT CompareEntryIDs(const ::BRUTUS::ENTRYID &lpEntryID1,
						  const ::BRUTUS::ENTRYID &lpEntryID2,
						  ::BRUTUS::BDEFINE ulFlags,
						  ::CORBA::Boolean_out lpulResult);

	virtual ::BRUTUS::BRESULT Advise(const ::BRUTUS::ENTRYID &lpEntryID,
					 ::BRUTUS::BDEFINE ulEventMask,
					 ::BRUTUS::IMAPIAdviseSink_ptr lpAdviseSink,
					 ::CORBA::ULong_out lpulConnection);

	virtual ::BRUTUS::BRESULT Unadvise(::CORBA::ULong ulConnection);

	virtual ::BRUTUS::BRESULT QueryDefaultMessageOpt(const char *lpszAdrType,
							 ::BRUTUS::BDEFINE ulFlags,
							 ::BRUTUS::seq_SPropValue_out lppOptions);

	virtual ::BRUTUS::BRESULT EnumAdrTypes(::BRUTUS::BDEFINE ulFlags,
					       ::BRUTUS::seq_string_out lpppszAdrTypes);

	virtual ::BRUTUS::BRESULT QueryIdentity(::BRUTUS::ENTRYID_out lppEntryID);

	virtual ::BRUTUS::BRESULT SetDefaultStore(::BRUTUS::BDEFINE ulFlags,
						  const ::BRUTUS::ENTRYID &lpEntryID);

	virtual ::BRUTUS::BRESULT AdminServices(::BRUTUS::BDEFINE ulFlags,
						::BRUTUS::IMsgServiceAdmin_out lppServiceAdmin);

	virtual ::CORBA::ULong addLifeline(::BRUTUS::BrutusCheck_ptr LifeLine);

	virtual void ping(void);

	virtual ::BRUTUS::BRESULT CreateIProp(::BRUTUS::IPropData_out lppPropData);

	virtual ::BRUTUS::BRESULT OpenTnefStreamEx(::BRUTUS::IStream_ptr lpStreamName,
						   const char *lpszStreamName,
						   ::BRUTUS::BDEFINE ulFlags,
						   ::BRUTUS::IMessage_ptr lpMessage,
						   ::CORBA::Short wKeyVal,
						   ::BRUTUS::IAddrBook_ptr lpAdressBook,
						   ::BRUTUS::ITnef_out lppTNEF);

	virtual ::BRUTUS::BRESULT MAPIAdminProfiles(::BRUTUS::BDEFINE ulFlags,
						    ::BRUTUS::IProfAdmin_out ProfAdmin);

	virtual ::BRUTUS::BRESULT GetDefaultMsgStore(::BRUTUS::BDEFINE ulFlags,
						     ::BRUTUS::IMsgStore_out lpMDB);

	virtual ::BRUTUS::BRESULT GetPublicMsgStore(::BRUTUS::BDEFINE ulFlags,
						    ::BRUTUS::IMsgStore_out lpMDB);

	virtual ::BRUTUS::BRESULT OpenStoreFromGuid(const char *lpGUID,
						    ::BRUTUS::BDEFINE ulFlags,
						    ::BRUTUS::IMsgStore_out lpMDB);

	virtual ::BRUTUS::BRESULT MailboxLogon(::BRUTUS::IMsgStore_ptr lpMDB,
					       const char *lpszMsgStoreDN,
					       const char *lpszMailboxDN,
					       ::BRUTUS::BDEFINE ulCreateFlags,
					       ::BRUTUS::BDEFINE ulOpenFlags,
					       ::BRUTUS::IMsgStore_out lppMailboxMDB);

	virtual ::BRUTUS::BRESULT GetOutlookFolderENTRYID(::BRUTUS::OUTLOOK_FOLDER Which,
							  ::BRUTUS::IMsgStore_ptr lpMDB,
							  ::BRUTUS::ENTRYID_out lppEntryID);

	virtual ::BRUTUS::BRESULT GetOutlookFolder(::BRUTUS::OUTLOOK_FOLDER Which,
						   ::BRUTUS::IMsgStore_ptr lpMDB,
						   ::BRUTUS::IMAPIFolder_out Folder);

	virtual ::BRUTUS::BRESULT DispatchNotifications(::CORBA::ULong Flags);

	virtual ::BRUTUS::BRESULT QueryProps(const ::BRUTUS::ENTRYID & Eid,
					     const ::BRUTUS::SPropTagArray & PropTagArray,
					     ::BRUTUS::seq_SPropValue_out PropValueArray);

	virtual ::BRUTUS::BRESULT QueryMessage(const ::BRUTUS::ENTRYID & Eid,
					       const ::BRUTUS::SPropTagArray & ObjPropTagArray,
					       const ::BRUTUS::SPropTagArray & AdditionalObjPropTagArray,
					       const ::BRUTUS::SPropTagArray & RecpPropTagArray,
					       ::BRUTUS::seq_SPropValue_out ObjPropValueArray,
					       ::BRUTUS::seq_SPropValue_out AdditionalObjPropValueArray,
					       ::BRUTUS::SRowSet_out RecpRows);

	virtual ::CORBA::Boolean GetServerTime(const char *server,
					       ::BRUTUS::TIME_OF_DAY_INFO_out tod);

	virtual ::CORBA::Boolean LookupAccountName(const char *lpSystemName,
						   const char *lpAccountName,
						   ::BRUTUS::seq_octet_out Sid,
						   ::CORBA::String_out ReferencedDomainName,
						   ::BRUTUS::SID_NAME_USE_out peUse);

	virtual ::BRUTUS::BRESULT InitMailbox(void);

	virtual ::CORBA::Boolean GetServerVersion(::BRUTUS::SERVER_VERSION_out Version);

	virtual void SetCodePage(::CORBA::ULong codepage);

	virtual ::BRUTUS::BRESULT QueryInterface(const char *iid,
						 ::BRUTUS::IUnknown_out ppvObject);

	virtual void Destroy(::CORBA::ULong InstanceID);

	void checkLifelines(void);

private:
	BRUTUS_IMAPISession_i(void)
	{ };

	virtual ~BRUTUS_IMAPISession_i(void)
	{
		try {
			// The POA guaranties that we only get here after all
			// threads have stopped accessing the object. There is
			// therefore no need to use any locking.
			if (mapi_session_) {
				for (unsigned long n = 0; n < advise_sinks_.length(); n++)
					mapi_session_->Unadvise(advise_sinks_[n]);
				advise_sinks_.Clear();

				mapi_session_->Logoff(0, MAPI_LOGOFF_SHARED, 0); // Investigate flag!!
				mapi_session_->Release();
				mapi_session_ = NULL;
			}
		}
		catch (...) {
		}
	};

	class LifeCheck {
	public:
		LifeCheck(::BRUTUS::BrutusCheck_ptr Checker,
			  ::CORBA::ULong CheckerID)
			: life_line(::BRUTUS::BrutusCheck::_duplicate(Checker)),
			  id(CheckerID)
			{
			};

		::BRUTUS::BrutusCheck_var life_line;
		::CORBA::ULong id;
	};

	void remove_life_check(const ::CORBA::ULong Id)
	{
		std::vector<LifeCheck*>::iterator lit = life_lines_.begin();

		unsigned long n = 0;
		for (n = 0; n < life_lines_.size(); n++, lit++) {
			if (Id == life_lines_.at(n)->id) {
				delete life_lines_.at(n);
				life_lines_.erase(lit);
				goto out;
			}
		}
		throw ::CORBA::BAD_PARAM();
	out:
		return;
	};

	::HRESULT get_outlook_folder_entryid(::BRUTUS::OUTLOOK_FOLDER Which,
					     LPMDB msg_store,
					     ULONG FAR *lpcbEntryID,
					     LPENTRYID FAR *lppEntryID);

	LPMAPISESSION mapi_session_;
	::PortableServer::POA_var poa_;
	unsigned long refcount_;
	::CORBA::ORB_var orb_;
	bool shutdown_orb_at_logout_;

	::CORBA::ULong life_line_counter_;
	std::vector<LifeCheck*> life_lines_;

	Sinks advise_sinks_;
	ACE_RW_Mutex mutex_;
	ACE_RW_Mutex life_check_mutex_;

	void (*shutdown_)(void);
};


#endif /* __IMAPISESSIONS_IMPL_H_  */
