////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// 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
//
////////////////////////////////////////////////////////////////////////////////////////////////////////////

#include "MAPIExPCH.h"
#include "MAPIEx.h"

/////////////////////////////////////////////////////////////
// CMAPIFolder

CMAPIFolder::CMAPIFolder()
{
	Init();
}

CMAPIFolder::CMAPIFolder(CMAPIEx* pMAPI, LPMAPIFOLDER pFolder, LPCTSTR szName)
{
	Init();

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

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

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

void CMAPIFolder::Close()
{
	ClearBuffer();
	RELEASE(m_pHierarchy);
	RELEASE(m_pContents);
	CMAPIObject::Close();
}

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

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

void CMAPIFolder::Attach(CMAPIEx* pMAPI, LPMAPIFOLDER pFolder, LPCTSTR 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);
	} 
	else 
	{
		SetEntryID(NULL);
	}
}

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

LPCTSTR CMAPIFolder::GetName()
{
	return m_strName;
}

LPMAPITABLE CMAPIFolder::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 
// (use instead of manually calling GetHierarchy and GetNextSubFolder)
CMAPIFolder* CMAPIFolder::OpenSubFolder(LPCTSTR szSubFolder)
{
	if(!GetHierarchy()) return NULL;

	CString strFolder;
	CMAPIFolder folder;
	CMAPIFolder* pSubFolder=NULL;
	while(GetNextSubFolder(folder, strFolder)) 
	{
		if(!strFolder.CompareNoCase(szSubFolder)) 
		{
			pSubFolder=new CMAPIFolder(m_pMAPI,folder.Detach());
		} 
		else 
		{
			pSubFolder=folder.OpenSubFolder(szSubFolder);
		}
		if(pSubFolder) break;
	}
	return pSubFolder;
} 

// Creates a sub-folder under pFolder, opens the folder if it already exists
CMAPIFolder* CMAPIFolder::CreateSubFolder(LPCTSTR szSubFolder)
{
	LPMAPIFOLDER pSubFolder=NULL;
	ULONG ulFolderType=FOLDER_GENERIC;
	ULONG ulFlags=OPEN_IF_EXISTS | CMAPIEx::cm_nMAPICode;

	Folder()->CreateFolder(ulFolderType, (LPTSTR)szSubFolder, NULL, NULL,ulFlags, &pSubFolder);
	return pSubFolder ? new CMAPIFolder(m_pMAPI, pSubFolder) : NULL;
}

// Deletes a sub-folder and ALL sub folders/messages
BOOL CMAPIFolder::DeleteSubFolder(LPCTSTR szSubFolder)
{
	if(GetHierarchy()) 
	{
		CString strFolder;
		CMAPIFolder folder;
		while(GetNextSubFolder(folder, strFolder)) 
		{
			if(!strFolder.CompareNoCase(szSubFolder)) 
			{
				return DeleteSubFolder(&folder);
			}
		}
	}
	return FALSE;
}

// Deletes a sub-folder and ALL sub folders/messages
BOOL CMAPIFolder::DeleteSubFolder(CMAPIFolder* pSubFolder)
{
	if(!pSubFolder) return FALSE;

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

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

LPMAPITABLE CMAPIFolder::GetContents()
{
	RELEASE(m_pContents);
	if(Folder()->GetContentsTable(CMAPIEx::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 CMAPIFolder::GetRowCount()
{
	ULONG ulCount;
	if(!m_pContents || m_pContents->GetRowCount(0, &ulCount)!=S_OK) return -1;
	return ulCount;
}

BOOL CMAPIFolder::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 CMAPIFolder::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 CMAPIFolder::SetRestriction(SRestriction* pRestriction)
{
	return (m_pContents && m_pContents->Restrict(pRestriction, 0)==S_OK);
}

BOOL CMAPIFolder::QueryRows()
{
	ClearBuffer();
	if(m_pContents)
	{
		if(m_pContents->QueryRows(m_nMaxRowsSize, 0, &m_pRows)==S_OK)
		{
			return TRUE;
		}
		m_pRows=NULL;
	}
	return FALSE;
}

SRow* CMAPIFolder::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 CMAPIFolder::GetNextMessage(CMAPIMessage& message)
{
	SRow* pRow=GetNextRow();
	return pRow ? message.Open(m_pMAPI, pRow->lpProps[PROP_ENTRYID].Value.bin) : FALSE;
}

BOOL CMAPIFolder::GetNextContact(CMAPIContact& 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 CMAPIFolder::GetNextAppointment(CMAPIAppointment& 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 CMAPIFolder::GetNextSubFolder(CMAPIFolder& folder, CString& 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(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)==S_OK)
			{
				strFolder=CMAPIEx::GetValidString(pRows->aRow[0].lpProps[0]);
				folder.Attach(m_pMAPI, pSubFolder, strFolder);
			}
		}
		FreeProws(pRows);
	}
	return (pSubFolder!=NULL);
}

BOOL CMAPIFolder::DeleteMessage(CMAPIMessage& message)
{
	return DeleteObject(message);
}

BOOL CMAPIFolder::CopyMessage(CMAPIMessage& message, CMAPIFolder* pFolderDest)
{
	if(!pFolderDest) return FALSE;

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

BOOL CMAPIFolder::MoveMessage(CMAPIMessage& message, CMAPIFolder* pFolderDest)
{
	if(!pFolderDest) return FALSE;

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

BOOL CMAPIFolder::DeleteContact(CMAPIContact& contact)
{
	return DeleteObject(contact);
}

BOOL CMAPIFolder::DeleteAppointment(CMAPIAppointment& appointment)
{
	return DeleteObject(appointment);
}

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