////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// File: MAPIFolder.cpp
// Description: MAPI Folder class wrapper
//
// Copyright (C) 2005-2011, Noel Dillabough
//
// This source code is free to use and modify provided this notice remains intact and that any enhancements
// or bug fixes are posted to the CodeProject page hosting this class for the community to benefit.
//
// Usage: see the CodeProject article at http://www.codeproject.com/internet/CMapiEx.asp
//
////////////////////////////////////////////////////////////////////////////////////////////////////////////

// Ported to U++ Framework by Koldo. See License.txt file

#include <MapiUtil.h>
#include "MAPIEx.h"

/////////////////////////////////////////////////////////////
// MAPIFolder

MAPIFolder::MAPIFolder() {
	Init();
}

MAPIFolder::MAPIFolder(MAPIEx* pMAPI, LPMAPIFOLDER pFolder, String szName) {
	Init();

	m_entryID.cb=0;
	Attach(pMAPI, pFolder, szName);
}

MAPIFolder::~MAPIFolder() {
	Close();
}

void MAPIFolder::Init() {
	m_pHierarchy = NULL;
	m_pContents = NULL;
	m_nMaxRowsSize = DEFAULT_FOLDER_BUFFER_SIZE;
	m_pRows = NULL;
	ClearBuffer();
}

void MAPIFolder::Close() {
	ClearBuffer();
	RELEASE(m_pHierarchy);
	RELEASE(m_pContents);
	MAPIObject::Close();
}

void MAPIFolder::SetBufferSize(int nSize) {
	m_nMaxRowsSize = max(1, nSize);
}

void MAPIFolder::ClearBuffer() {
	if(m_pRows) {
		FreeProws(m_pRows);
		m_pRows = NULL;
	}
	m_nRowsIndex = 0;
}

bool MAPIFolder::Attach(MAPIEx* pMAPI, LPMAPIFOLDER pFolder, String szName) {
	Close();
	m_pMAPI = pMAPI;
	m_pItem = pFolder;
	m_strName = szName;
	LPSPropValue pProp;
	if(GetProperty(PR_ENTRYID, pProp) == S_OK) {
		SetEntryID(&pProp->Value.bin);
		MAPIFreeBuffer(pProp);
		return true;
	} else {
		SetEntryID(NULL);
		return false;
	}
}

LPMAPIFOLDER MAPIFolder::Detach() {
	LPMAPIFOLDER pFolder = Folder();
	m_pItem = NULL;
	return pFolder;
}

String MAPIFolder::GetName() {
	return m_strName;
}

LPMAPITABLE MAPIFolder::GetHierarchy() {
	RELEASE(m_pHierarchy);
	if(Folder()->GetHierarchyTable(0, &m_pHierarchy) != S_OK) 
		return NULL;

	const int nProperties = 2;
	SizedSPropTagArray(nProperties, Columns)={nProperties,{PR_DISPLAY_NAME, PR_ENTRYID}};
	if(m_pHierarchy->SetColumns((LPSPropTagArray)&Columns, 0) != S_OK) {
		RELEASE(m_pHierarchy);
		return NULL;
	}
	return m_pHierarchy;
}

// High Level function to open a sub folder by iterating recursively (DFS) over all folders 
bool MAPIFolder::OpenSubFolder(const String &subFolderName, MAPIFolder &subFolder, 
																bool fullPath, String path) {
	if(!GetHierarchy()) 
		return NULL;

	String strFolder;
	MAPIFolder folder;
	MAPIFolder* pSubFolder = NULL;
	while(GetNextSubFolder(folder, strFolder)) {
		bool retSubFolder;
		if (fullPath)
			strFolder = path + "/" + strFolder;
		if(!CompareNoCase(strFolder, subFolderName)) 
			retSubFolder = subFolder.Attach(m_pMAPI, folder.Detach());
		else 
			retSubFolder = folder.OpenSubFolder(subFolderName, subFolder, fullPath, strFolder);
		if(retSubFolder) 
			return true;
	}
	return false;
} 

// Creates a sub-folder under pFolder, opens the folder if it already exists
bool MAPIFolder::CreateSubFolder(const String &subFolderName, MAPIFolder &subFolder) {
	LPMAPIFOLDER pSubFolder = NULL;
	ULONG ulFolderType=FOLDER_GENERIC;
	ULONG ulFlags=OPEN_IF_EXISTS | MAPIEx::cm_nMAPICode;

	Folder()->CreateFolder(ulFolderType, (LPTSTR)(subFolderName.Begin()), NULL, NULL, ulFlags, 
																				  &pSubFolder);
	subFolder.Attach(m_pMAPI, pSubFolder);
	return pSubFolder; 
}

// Deletes a sub-folder and ALL sub folders/messages
bool MAPIFolder::DeleteSubFolder(const String &subFolderName) {
	if(GetHierarchy()) {
		String strFolder;
		MAPIFolder folder;
		while(GetNextSubFolder(folder, strFolder)) {
			if(!CompareNoCase(strFolder, subFolderName)) 
				return DeleteSubFolder(folder);
		}
	}
	return false;
}

// Deletes a sub-folder and ALL sub folders/messages
bool MAPIFolder::DeleteSubFolder(MAPIFolder &subFolder) {
	if(!subFolder.IsOpened()) 
		return false;

	LPSPropValue props = NULL;
	ULONG cValues=0;
	ULONG rgTags[]={ 1, PR_ENTRYID };

	if(subFolder.Folder()->GetProps((LPSPropTagArray) rgTags, MAPIEx::cm_nMAPICode, 
																&cValues, &props) == S_OK) {
		HRESULT hr = Folder()->DeleteFolder(props[0].Value.bin.cb, 
						(LPENTRYID)props[0].Value.bin.lpb, 0, NULL,DEL_FOLDERS|DEL_MESSAGES);
		MAPIFreeBuffer(props);
		return (hr == S_OK);
	}
	return false;
}

LPMAPITABLE MAPIFolder::GetContents() {
	RELEASE(m_pContents);
	if(Folder()->GetContentsTable(MAPIEx::cm_nMAPICode, &m_pContents)!=S_OK) 
		return NULL;

	const int nProperties = MESSAGE_COLS;
	SizedSPropTagArray(nProperties, Columns) = {nProperties,{PR_MESSAGE_FLAGS, PR_ENTRYID }};
	if(m_pContents->SetColumns((LPSPropTagArray)&Columns, 0)!=S_OK) {
		RELEASE(m_pContents);
		return NULL;
	}
	return m_pContents;
}

int MAPIFolder::GetCount() {
	ULONG ulCount;
	if(!m_pContents || m_pContents->GetRowCount(0, &ulCount) != S_OK) 
		return -1;
	return ulCount;
}

bool MAPIFolder::SortContents(ULONG ulSortParam, ULONG ulSortField) {
	if(!m_pContents) 
		return false;

	SizedSSortOrderSet(1, SortColums) = {1, 0, 0, {{ulSortField, ulSortParam}}};
	return (m_pContents->SortTable((LPSSortOrderSet)&SortColums, 0) == S_OK);
}

bool MAPIFolder::SetUnreadOnly(bool bUnreadOnly) {
	if(bUnreadOnly) {
		SRestriction res;
		res.rt = RES_BITMASK;
		res.res.resBitMask.relBMR = BMR_EQZ;
		res.res.resBitMask.ulPropTag = PR_MESSAGE_FLAGS;
		res.res.resBitMask.ulMask = MSGFLAG_READ;
		return SetRestriction(&res);
	}
	return SetRestriction(NULL);
}

bool MAPIFolder::SetRestriction(SRestriction* pRestriction) {
	return (m_pContents && m_pContents->Restrict(pRestriction, 0)==S_OK);
}

bool MAPIFolder::QueryRows() {
	ClearBuffer();
	if(m_pContents) {
		if(m_pContents->QueryRows(m_nMaxRowsSize, 0, &m_pRows) == S_OK) {
			return true;
		}
		m_pRows = NULL;
	}
	return false;
}

SRow* MAPIFolder::GetNextRow() {
	if(m_pRows == NULL || m_nRowsIndex >= m_pRows->cRows) {
		if(!QueryRows()) 
			return NULL;
	}
	if(m_pRows->cRows==0) {
		ClearBuffer();
		return NULL;
	}
	return &m_pRows->aRow[m_nRowsIndex++];
}

bool MAPIFolder::GetNextMessage(MAPIMessage& message) {
	SRow* pRow = GetNextRow();
	return pRow ? message.Open(m_pMAPI, pRow->lpProps[PROP_ENTRYID].Value.bin) : false;
}

bool MAPIFolder::GetNextContact(MAPIContact& contact) {
#ifdef _WIN32_WCE
	return m_poom.GetNextContact(m_pMAPI, contact);
#else
	SRow* pRow = GetNextRow();
	return pRow ? contact.Open(m_pMAPI, pRow->lpProps[PROP_ENTRYID].Value.bin) : false;
#endif
}

bool MAPIFolder::GetNextAppointment(MAPIAppointment& appointment) {
#ifdef _WIN32_WCE
	return m_poom.GetNextAppointment(m_pMAPI,appointment);
#else
	SRow* pRow=GetNextRow();
	return pRow ? appointment.Open(m_pMAPI, pRow->lpProps[PROP_ENTRYID].Value.bin) : false;
#endif
}

bool MAPIFolder::GetNextSubFolder(MAPIFolder& folder, String& strFolder) {
	if(!m_pHierarchy) 
		return NULL;
	folder.Close();

	DWORD dwObjType;
	LPSRowSet pRows = NULL;

	LPMAPIFOLDER pSubFolder = NULL;
	if(m_pHierarchy->QueryRows(1, 0, &pRows) == S_OK) {
		if(pRows->cRows) {
			if(S_OK == Folder()->OpenEntry(pRows->aRow[0].lpProps[PROP_ENTRYID].Value.bin.cb, 
								(LPENTRYID)pRows->aRow[0].lpProps[PROP_ENTRYID].Value.bin.lpb, 	
									NULL, MAPI_MODIFY, &dwObjType, (LPUNKNOWN*)&pSubFolder)) {
				strFolder = MAPIEx::GetValidString(pRows->aRow[0].lpProps[0]);
				folder.Attach(m_pMAPI, pSubFolder, strFolder);
			}
		}
		FreeProws(pRows);
	}
	if (!pSubFolder)
		return false;
	folder.GetHierarchy();
	folder.GetContents();
	return true;
}

bool MAPIFolder::DeleteMessage(MAPIMessage& message) {
	return DeleteObject(message);
}

bool MAPIFolder::CopyMessage(MAPIMessage& message, MAPIFolder &folderDest) {
	ENTRYLIST entries = { 1, message.GetEntryID() };
	HRESULT hr = Folder()->CopyMessages(&entries, NULL, folderDest.Folder(), 0, NULL, 0);
	return (hr == S_OK);
}

bool MAPIFolder::MoveMessage(MAPIMessage& message, MAPIFolder &folderDest) {
	if(!folderDest.IsOpened()) 
		return false;

	ENTRYLIST entries = { 1, message.GetEntryID() };
	HRESULT hr = Folder()->CopyMessages(&entries, NULL, folderDest.Folder(), 0, NULL,MESSAGE_MOVE);
	return (hr == S_OK);
}

bool MAPIFolder::DeleteContact(MAPIContact& contact) {
	return DeleteObject(contact);
}

bool MAPIFolder::DeleteAppointment(MAPIAppointment& appointment) {
	return DeleteObject(appointment);
}

bool MAPIFolder::DeleteObject(MAPIObject& object) {
	ENTRYLIST entries={ 1, object.GetEntryID() };
	HRESULT hr = Folder()->DeleteMessages(&entries, 0, NULL, 0);
	return (hr==S_OK);
}
