/*
 * irecinit.c - CReconcileInitiator implementation.
 */


/* Headers
 **********/

#include "project.h"
#pragma hdrstop

#include "oleutil.h"
#include "irecinit.h"


/* Types
 ********/

/* ReconcileInitiator class */

typedef struct _creconcileinitiator
{
   /* IReconcileInitiator */

   IReconcileInitiator irecinit;

   /* IBriefcaseInitiator */

   IBriefcaseInitiator ibcinit;

   /* reference count */

   ULONG ulcRef;

   /* handle to parent briefcase */

   HBRFCASE hbr;

   /* status callback function */

   RECSTATUSPROC rsp;

   /* status callback function data */

   LPARAM lpCallbackData;

   /* IUnknown to release to abort reconciliation. */

   PIUnknown piunkForAbort;
}
CReconcileInitiator;
DECLARE_STANDARD_TYPES(CReconcileInitiator);


/* Module Prototypes
 ********************/

PRIVATE_CODE HRESULT ReconcileInitiator_QueryInterface(PCReconcileInitiator, REFIID, PVOID *);
PRIVATE_CODE ULONG ReconcileInitiator_AddRef(PCReconcileInitiator);
PRIVATE_CODE ULONG ReconcileInitiator_Release(PCReconcileInitiator);
PRIVATE_CODE HRESULT ReconcileInitiator_SetAbortCallback(PCReconcileInitiator, PIUnknown);
PRIVATE_CODE HRESULT ReconcileInitiator_SetProgressFeedback(PCReconcileInitiator, ULONG, ULONG);

PRIVATE_CODE HRESULT AbortReconciliation(PCReconcileInitiator);

PRIVATE_CODE HRESULT RI_IReconcileInitiator_QueryInterface(PIReconcileInitiator, REFIID, PVOID *);
PRIVATE_CODE ULONG RI_IReconcileInitiator_AddRef(PIReconcileInitiator);
PRIVATE_CODE ULONG RI_IReconcileInitiator_Release(PIReconcileInitiator);
PRIVATE_CODE HRESULT RI_IReconcileInitiator_SetAbortCallback(PIReconcileInitiator, PIUnknown);
PRIVATE_CODE HRESULT RI_IReconcileInitiator_SetProgressFeedback( PIReconcileInitiator, ULONG, ULONG);

PRIVATE_CODE HRESULT RI_IBriefcaseInitiator_QueryInterface(PIBriefcaseInitiator, REFIID, PVOID *);
PRIVATE_CODE ULONG RI_IBriefcaseInitiator_AddRef(PIBriefcaseInitiator);
PRIVATE_CODE ULONG RI_IBriefcaseInitiator_Release(PIBriefcaseInitiator);
PRIVATE_CODE HRESULT RI_IBriefcaseInitiator_IsMonikerInBriefcase(PIBriefcaseInitiator, PIMoniker);

#ifdef DEBUG

PRIVATE_CODE BOOL IsValidPCCReconcileInitiator(PCCReconcileInitiator);
PRIVATE_CODE BOOL IsValidPCIBriefcaseInitiator(PCIBriefcaseInitiator);

#endif


/* Module Variables
 *******************/

#pragma data_seg(DATA_SEG_READ_ONLY)

/* IReconcileInitiator vtable */

PRIVATE_DATA IReconcileInitiatorVtbl Mcirecinitvtbl =
{
   &RI_IReconcileInitiator_QueryInterface,
   &RI_IReconcileInitiator_AddRef,
   &RI_IReconcileInitiator_Release,
   &RI_IReconcileInitiator_SetAbortCallback,
   &RI_IReconcileInitiator_SetProgressFeedback
};

/* IBriefcaseInitiator vtable */

PRIVATE_DATA IBriefcaseInitiatorVtbl Mcibcinitvtbl =
{
   &RI_IBriefcaseInitiator_QueryInterface,
   &RI_IBriefcaseInitiator_AddRef,
   &RI_IBriefcaseInitiator_Release,
   &RI_IBriefcaseInitiator_IsMonikerInBriefcase
};

#pragma data_seg()


/***************************** Private Functions *****************************/


/*
** ReconcileInitiator_QueryInterface()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects:  none
*/
PRIVATE_CODE HRESULT ReconcileInitiator_QueryInterface(
                                                PCReconcileInitiator precinit,
                                                REFIID riid, PVOID *ppvObject)
{
   HRESULT hr;

   ASSERT(IS_VALID_STRUCT_PTR(precinit, CCReconcileInitiator));
   //ASSERT(IsValidREFIID(riid));
   ASSERT(IS_VALID_WRITE_PTR(ppvObject, PVOID));

   if (IsEqualIID(riid, &IID_IUnknown) ||
       IsEqualIID(riid, &IID_IReconcileInitiator))
   {
      *ppvObject = &(precinit->irecinit);
      precinit->irecinit.lpVtbl->AddRef(&(precinit->irecinit));
      hr = S_OK;
   }
   else if (IsEqualIID(riid, &IID_IBriefcaseInitiator))
   {
      *ppvObject = &(precinit->ibcinit);
      precinit->ibcinit.lpVtbl->AddRef(&(precinit->ibcinit));
      hr = S_OK;
   }
   else
      hr = E_NOINTERFACE;

   return(hr);
}


/*
** ReconcileInitiator_AddRef()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects:  none
*/
PRIVATE_CODE ULONG ReconcileInitiator_AddRef(PCReconcileInitiator precinit)
{
   ASSERT(IS_VALID_STRUCT_PTR(precinit, CCReconcileInitiator));

   ASSERT(precinit->ulcRef < ULONG_MAX);
   return(++(precinit->ulcRef));
}


/*
** ReconcileInitiator_Release()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects:  none
*/
PRIVATE_CODE ULONG ReconcileInitiator_Release(PCReconcileInitiator precinit)
{
   ULONG ulcRef;

   ASSERT(IS_VALID_STRUCT_PTR(precinit, CCReconcileInitiator));

   if (EVAL(precinit->ulcRef > 0))
      precinit->ulcRef--;

   ulcRef = precinit->ulcRef;

   if (! precinit->ulcRef)
      FreeMemory(precinit);

   return(ulcRef);
}


/*
** ReconcileInitiator_SetAbortCallback()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects:  none
*/
PRIVATE_CODE HRESULT ReconcileInitiator_SetAbortCallback(
                                                PCReconcileInitiator precinit,
                                                PIUnknown piunkForAbort)
{
   ASSERT(IS_VALID_STRUCT_PTR(precinit, CCReconcileInitiator));
   /* piunkForAbort can be legally NULL */
   ASSERT(NULL == piunkForAbort || IS_VALID_STRUCT_PTR(piunkForAbort, CIUnknown));

   precinit->piunkForAbort = piunkForAbort;

   return(S_OK);
}


/*
** ReconcileInitiator_SetProgressFeedback()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects:  none
*/
PRIVATE_CODE HRESULT ReconcileInitiator_SetProgressFeedback(
                                                PCReconcileInitiator precinit,
                                                ULONG ulProgress,
                                                ULONG ulProgressMax)
{
   RECSTATUSUPDATE rsu;

   /* ulProgress may be any value. */
   /* ulProgressMax may be any value. */

   ASSERT(IS_VALID_STRUCT_PTR(precinit, CCReconcileInitiator));

   rsu.ulScale = ulProgressMax;
   rsu.ulProgress = ulProgress;

   if (! NotifyReconciliationStatus(precinit->rsp, RS_DELTA_MERGE,
                                    (LPARAM)&rsu, precinit->lpCallbackData))
      AbortReconciliation(precinit);

   return(S_OK);
}


/*
** ReconcileInitiator_IsMonikerInBriefcase()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects:  none
*/
PRIVATE_CODE HRESULT ReconcileInitiator_IsMonikerInBriefcase(
                                          PCReconcileInitiator precinit,
                                          PIMoniker pimk)
{
   HRESULT hr;
   PIMoniker pimkBriefcase;

   ASSERT(IS_VALID_STRUCT_PTR(precinit, CCReconcileInitiator));
   ASSERT(IS_VALID_STRUCT_PTR(pimk, CIMoniker));

   hr = GetBriefcaseRootMoniker(precinit->hbr, &pimkBriefcase);

   if (SUCCEEDED(hr))
   {
      PIMoniker pimkCommonPrefix;

      hr = pimk->lpVtbl->CommonPrefixWith(pimk, pimkBriefcase,
                                          &pimkCommonPrefix);

      if (SUCCEEDED(hr))
      {
         switch (hr)
         {
            case MK_S_US:
               WARNING_OUT(((TEXT("ReconcileInitiator_IsMonikerInBriefcase(): Called on briefcase root."))));
               /* Fall through... */
            case MK_S_HIM:
               hr = S_OK;
               break;

            default:
               ASSERT(hr == S_OK ||
                      hr == MK_S_ME);
               hr = S_FALSE;
               break;
         }

#ifdef DEBUG

         {
            PIBindCtx pibindctx;
            BOOL bGotMoniker = FALSE;
            BOOL bGotBriefcase = FALSE;
            PWSTR pwszMoniker;
            PWSTR pwszBriefcase;
            PIMalloc pimalloc;

            if (SUCCEEDED(CreateBindCtx(0, &pibindctx)))
            {
               bGotMoniker = SUCCEEDED(pimk->lpVtbl->GetDisplayName(
                                                               pimk, pibindctx,
                                                               NULL,
                                                               &pwszMoniker));

               bGotBriefcase = SUCCEEDED(pimkBriefcase->lpVtbl->GetDisplayName(
                                                            pimkBriefcase,
                                                            pibindctx, NULL,
                                                            &pwszBriefcase));

               pibindctx->lpVtbl->Release(pibindctx);
            }

            if (! bGotMoniker)
               pwszMoniker = (PWSTR)L"UNAVAILABLE DISPLAY NAME";

            if (! bGotBriefcase)
               pwszBriefcase = (PWSTR)L"UNAVAILABLE DISPLAY NAME";

            TRACE_OUT(((TEXT("ReconcileInitiator_IsMonikerInBriefcase(): Moniker %ls is %s briefcase %ls.")),
                       pwszMoniker,
                       (hr == S_OK) ? "in" : "not in",
                       pwszBriefcase));

            if (EVAL(GetIMalloc(&pimalloc)))
            {
               if (bGotMoniker)
                  pimalloc->lpVtbl->Free(pimalloc, pwszMoniker);

               if (bGotBriefcase)
                  pimalloc->lpVtbl->Free(pimalloc, pwszBriefcase);

               /* Do not release pimalloc. */
            }
         }

#endif

         /* Do not release pimkBriefcase. */
      }
   }

   return(hr);
}


/*
** AbortReconciliation()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects:  none
*/
PRIVATE_CODE HRESULT AbortReconciliation(PCReconcileInitiator precinit)
{
   ASSERT(IS_VALID_STRUCT_PTR(precinit, CCReconcileInitiator));

   if (precinit->piunkForAbort)
      precinit->piunkForAbort->lpVtbl->Release(precinit->piunkForAbort);

   return(S_OK);
}


/*
** RI_IReconcileInitiator_QueryInterface()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects:  none
*/
PRIVATE_CODE HRESULT RI_IReconcileInitiator_QueryInterface(
                                                PIReconcileInitiator pirecinit,
                                                REFIID riid, PVOID *ppvObject)
{
   ASSERT(IS_VALID_STRUCT_PTR(pirecinit, CIReconcileInitiator));
   //ASSERT(IsValidREFIID(riid));
   ASSERT(IS_VALID_WRITE_PTR(ppvObject, PVOID));

   return(ReconcileInitiator_QueryInterface(
            ClassFromIface(CReconcileInitiator, irecinit, pirecinit),
            riid, ppvObject));
}


/*
** RI_IReconcileInitiator_AddRef()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects:  none
*/
PRIVATE_CODE ULONG RI_IReconcileInitiator_AddRef(
                                                PIReconcileInitiator pirecinit)
{
   ASSERT(IS_VALID_STRUCT_PTR(pirecinit, CIReconcileInitiator));

   return(ReconcileInitiator_AddRef(
            ClassFromIface(CReconcileInitiator, irecinit, pirecinit)));
}


/*
** RI_IReconcileInitiator_Release()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects:  none
*/
PRIVATE_CODE ULONG RI_IReconcileInitiator_Release(
                                                PIReconcileInitiator pirecinit)
{
   ASSERT(IS_VALID_STRUCT_PTR(pirecinit, CIReconcileInitiator));

   return(ReconcileInitiator_Release(
            ClassFromIface(CReconcileInitiator, irecinit, pirecinit)));
}


/*
** RI_IReconcileInitiator_SetAbortCallback()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects:  none
*/
PRIVATE_CODE HRESULT RI_IReconcileInitiator_SetAbortCallback(
                                                PIReconcileInitiator pirecinit,
                                                PIUnknown piunkForAbort)
{
   ASSERT(IS_VALID_STRUCT_PTR(pirecinit, CIReconcileInitiator));
   /* piunkForAbort can be legally NULL */
   ASSERT(NULL == piunkForAbort || IS_VALID_STRUCT_PTR(piunkForAbort, CIUnknown));

   return(ReconcileInitiator_SetAbortCallback(
            ClassFromIface(CReconcileInitiator, irecinit, pirecinit),
            piunkForAbort));
}


/*
** RI_IReconcileInitiator_SetProgressFeedback()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects:  none
*/
PRIVATE_CODE HRESULT RI_IReconcileInitiator_SetProgressFeedback(
                                                PIReconcileInitiator pirecinit,
                                                ULONG ulProgress,
                                                ULONG ulProgressMax)
{
   /* ulProgress may be any value. */
   /* ulProgressMax may be any value. */

   ASSERT(IS_VALID_STRUCT_PTR(pirecinit, CIReconcileInitiator));

   return(ReconcileInitiator_SetProgressFeedback(
            ClassFromIface(CReconcileInitiator, irecinit, pirecinit),
            ulProgress, ulProgressMax));
}


/*
** RI_IBriefcaseInitiator_QueryInterface()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects:  none
*/
PRIVATE_CODE HRESULT RI_IBriefcaseInitiator_QueryInterface(
                                                PIBriefcaseInitiator pibcinit,
                                                REFIID riid, PVOID *ppvObject)
{
   ASSERT(IS_VALID_STRUCT_PTR(pibcinit, CIBriefcaseInitiator));
   //ASSERT(IsValidREFIID(riid));
   ASSERT(IS_VALID_WRITE_PTR(ppvObject, PVOID));

   return(ReconcileInitiator_QueryInterface(
            ClassFromIface(CReconcileInitiator, ibcinit, pibcinit),
            riid, ppvObject));
}


/*
** RI_IBriefcaseInitiator_AddRef()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects:  none
*/
PRIVATE_CODE ULONG RI_IBriefcaseInitiator_AddRef(PIBriefcaseInitiator pibcinit)
{
   ASSERT(IS_VALID_STRUCT_PTR(pibcinit, CIBriefcaseInitiator));

   return(ReconcileInitiator_AddRef(
            ClassFromIface(CReconcileInitiator, ibcinit, pibcinit)));
}


/*
** RI_IBriefcaseInitiator_Release()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects:  none
*/
PRIVATE_CODE ULONG RI_IBriefcaseInitiator_Release(
                                                PIBriefcaseInitiator pibcinit)
{
   ASSERT(IS_VALID_STRUCT_PTR(pibcinit, CIBriefcaseInitiator));

   return(ReconcileInitiator_Release(
            ClassFromIface(CReconcileInitiator, ibcinit, pibcinit)));
}


/*
** RI_IBriefcaseInitiator_IsMonikerInBriefcase()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects:  none
*/
PRIVATE_CODE HRESULT RI_IBriefcaseInitiator_IsMonikerInBriefcase(
                                                PIBriefcaseInitiator pibcinit,
                                                PIMoniker pmk)
{
   ASSERT(IS_VALID_STRUCT_PTR(pibcinit, CIBriefcaseInitiator));
   ASSERT(IS_VALID_STRUCT_PTR(pmk, CIMoniker));

   return(ReconcileInitiator_IsMonikerInBriefcase(
            ClassFromIface(CReconcileInitiator, ibcinit, pibcinit),
            pmk));
}


#ifdef DEBUG

/*
** IsValidPCCReconcileInitiator()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects:  none
*/
PRIVATE_CODE BOOL IsValidPCCReconcileInitiator(PCCReconcileInitiator pcrecinit)
{
   /* ulcRef may be any value. */
   /* lpCallbackData may be any value. */

   return(IS_VALID_READ_PTR(pcrecinit, CCReconcileInitiator) &&
          IS_VALID_STRUCT_PTR(&(pcrecinit->irecinit), CIReconcileInitiator) &&
          IS_VALID_STRUCT_PTR(&(pcrecinit->ibcinit), CIBriefcaseInitiator) &&
          IS_VALID_HANDLE(pcrecinit->hbr, BRFCASE) &&
          (! pcrecinit->rsp ||
           IS_VALID_CODE_PTR(pcrecinit->rsp, RECSTATUSPROC)) &&
          (! pcrecinit->piunkForAbort ||
           IS_VALID_STRUCT_PTR(pcrecinit->piunkForAbort, CIUnknown)));
}


/*
** IsValidPCIBriefcaseInitiator()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects:  none
*/
PRIVATE_CODE BOOL IsValidPCIBriefcaseInitiator(PCIBriefcaseInitiator pcibcinit)
{
   return(IS_VALID_READ_PTR(pcibcinit, CIBriefcaseInitiator) &&
          IS_VALID_READ_PTR(pcibcinit->lpVtbl, sizeof(*(pcibcinit->lpVtbl))) &&
          IS_VALID_STRUCT_PTR((PCIUnknown)pcibcinit, CIUnknown) &&
          IS_VALID_CODE_PTR(pcibcinit->lpVtbl->IsMonikerInBriefcase, IsMonikerInBriefcase));
}

#endif


/****************************** Public Functions *****************************/


/*
** IReconcileInitiator_Constructor()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects:  none
*/
PUBLIC_CODE HRESULT IReconcileInitiator_Constructor(
                                             HBRFCASE hbr, RECSTATUSPROC rsp,
                                             LPARAM lpCallbackData,
                                             PIReconcileInitiator *ppirecinit)
{
   HRESULT hr;
   PCReconcileInitiator precinit;

   /* lpCallbackData may be any value. */

   ASSERT(IS_VALID_HANDLE(hbr, BRFCASE));
   ASSERT(! rsp ||
          IS_VALID_CODE_PTR(rsp, RECSTATUSPROC));
   ASSERT(IS_VALID_WRITE_PTR(ppirecinit, PIReconcileInitiator));

   if (AllocateMemory(sizeof(*precinit), &precinit))
   {
      precinit->irecinit.lpVtbl = &Mcirecinitvtbl;
      precinit->ibcinit.lpVtbl = &Mcibcinitvtbl;
      precinit->ulcRef = 0;
      precinit->hbr = hbr;
      precinit->rsp = rsp;
      precinit->lpCallbackData = lpCallbackData;
      precinit->piunkForAbort = NULL;

      ASSERT(IS_VALID_STRUCT_PTR(precinit, CCReconcileInitiator));

      hr = precinit->irecinit.lpVtbl->QueryInterface(
               &(precinit->irecinit), &IID_IReconcileInitiator, ppirecinit);

      ASSERT(hr == S_OK);
   }
   else
      hr = E_OUTOFMEMORY;

   ASSERT(FAILED(hr) ||
          IS_VALID_STRUCT_PTR(*ppirecinit, CIReconcileInitiator));

   return(hr);
}