/* CheckChoice.cpp 0.13              UTF-8                    dh:2007-01-19
 *
 *            VERIFY THE IodmNative COM INTERFACE IMPLEMENTATION
 *            **************************************************
 *
 * CheckChoice provides the regression confirmation of chooseDocument
 * functionality for the 0.50beta distribution.  This regression is also
 * used to confirm that refactoring of OdmNative100 Check05 into components
 * for CheckChoice, CheckNew, and CheckSave has not introduced regression
 * defects.
 *
 * This program confirms the choice of documents (ODMJNI counterpart of
 * chooseDocument) and the functioning of the Working Document that is
 * obtained in response (here via an IodmWorking100 interface).  The
 * launching of the chosen document's content for viewing and editing and
 * any commitChanges are demosntrated.
 *
 * This is a C++ program.  The COM interfaces that we use are defined as
 * only as C++ classes and they have not been modified to work with C
 * Language programs.
 *
 * For the most part, we are still writing "Clean C," but with C++ classes
 * as interface definitions and using the C++ flexibility around placement
 * of declarations.
 */

#include <stdio.h>
    /* For FILE, fputs, stdout, ... */

#include <stdlib.h>
    /* For System function to request Save As ... */

#include <windows.h>
    /* For standard Windows types, constants, and classes. */

#include "Odma32types100.h"
    /* ODMA types used with OdmNative100 interfaces */

#include "OdmNative100.hpp"
    /* For the basic IodmApplication100 interface and the factory function
       for starting OdmNative operation: OdmBindNative100.
       */

#include "OdmWorking100.hpp"
    /* For the basic IodmWorking interface that is used for working
       documents instantiated via OdmNative operations.
       */

#include "OdmPending100.hpp"
    /* For the basic IodmPending interface that is used as part of the
       Save As ... implementation setup with a new copy to operate with.
       */

    /* The OdmNative100.lib must also be included in the linking of this
       program.  See the BuildCheckChoice.bat file for location of the
       material needed at compile-time.
       */

#include "CheckUtils.hpp"
    /* The CheckUtils.lib or the modules must be included in the linking
       of this program.  See BuildCheckChoice.bat for how that is done.
       */


/* AUXILLIARY INPUT-OUTPUT FUNCTIONS
 * *********************************
 */

/*              CheckChoice> */
#define INDENT "             "
#define STARS  "        **** "

static const char indent[] = INDENT;
static const char stars[] =  STARS;



static void hello(FILE *out)
{   /* Tell them who's here. */

    fputs("\nCheckChoice> 0.13 Demonstrate OdmNative100 "
                         "0.50beta Chosen-Document Operations.\n",
          out);

    fputs(INDENT "Program " __FILE__ " dated " __TIMESTAMP__ "\n"
          INDENT
                 #if defined(_MSC_EXTENSIONS)
                     "MS-extended "
                 #endif

                 #if defined(__cplusplus)
                     "C++"
                 #elif defined(__STDC__)
                     "Standard C"
                 #elif defined(_MSC_VER)
                     "Visual C"
                 #else
                     "C Language"
                 #endif

                 #if defined(_DLL)
                     " DLL"
                 #endif
                 #if defined(_WIN32)
                     " (Win32)"
                 #endif

                 " compiled " __DATE__ " at " __TIME__
                 "\n\n",
          out);


    } /* hello */


static void reportIodmApp( FILE  *out,
                           BOOL  haveIodmApp
                           )
{   if (!haveIodmApp)
         {  /* Lament having no IodmApplication100 interface */
            fputs("\n" STARS "IodmApplication100 INTERFACE NOT ACQUIRED\n"
                       STARS "    No further operations are possible.\n",
                  out);
            }

    else {  /* Report operations able to begin. */
            fputs("\n" INDENT "> IodmApplication100 "
                              "interface acquired.\n",
                  out);
            }

    } /* reportIodmApp */


static void reportHasConMan( FILE  *out,
                       BOOL  haveConnectionManager
                           )
{   if (!haveConnectionManager)
         {  /* Lament having no Connection Manager */
            fputs("\n" STARS "CONNECTION MANAGER NOT AVAILABLE\n"
                       STARS "    No ODMA operations are possible and the\n"
                       STARS "    application must behave "
                                 "conventionally.\n",
                  out);
            }

    else {  /* Report operations able to begin. */
            fputs("\n" INDENT "> > An ODMA Connection Manager "
                              "is available to use.\n",
                  out);
            }

    } /* reportHasConMan */



static void reportUnexpectedDMS( FILE  *out,
                                 BOOL  unexpectedDMS
                                 )
{   if (unexpectedDMS)
         {  /* There's a problem here. */
            fputs("\n" STARS "DMS INCORRECTLY REPORTED AVAILABLE.\n"
                       STARS "    No DMS can be available if there is no\n"
                       STARS "    ODMA Connection Manager available.\n",
                  out );
            }

    else {  /* It is expected that there be no unexpected DMS ... */
            fputs(STARS "    As required in this case, there is also\n"
                  STARS "    no DefaultDMS.\n",
                  out );
            }

    } /* reportUnexpectedDMS */



static void reportHasDefaultDMS( FILE  *out,
                                 BOOL  hasDefaultDMS
                                 )
{   fputs("\n", out);

    if (!hasDefaultDMS)
         {  /* There's a limitation here. */
            fputs(INDENT "> > > NO DEFAULT DMS IS AVAILABLE.\n"
                  INDENT "      Operations that apply against a default\n"
                  INDENT "      are unavailable to this application.\n",
                  out);
            }

    else {  /* all ODMA 1.0 operations are available */
            fputs(INDENT "> > > There is a Default DMS for use.\n"
                  INDENT "      All ODMA 1.0 DMS-requiring operations\n"
                  INDENT "      are available to this application.\n",
                  out);
            }

    } /* reportDefaultDMS */



static void reportNoSelect(FILE* out, ODMSTATUS rc)
{   /* Show results of selectDocID operation that didn't return a
       Document ID.
       */

       fputs("\n" INDENT "> > > > No DocID returned from selectDocID.\n"
                  INDENT "        rc = ",
             out);

       fprintf(out, "%d, %s",
                    (int) rc, odmStatusName(rc) );

       switch (rc)
       {    /* Indicate if response is unexpected */
           case ODM_SUCCESS:
           case ODM_E_FAIL:
           case ODM_E_CANCEL:
           case ODM_E_NODMS:
           case ODM_E_APPSELECT:
                fputs("\n", out);
                return;
           }

       fputs(" *** INVALID ***\n", out);

       } /* reportNoSelect */


static void reportNoOpen(FILE* out, ODMSTATUS rc)
{   /* Show results of openDoc operation that didn't return a
       Document Location.
       */

       fputs("\n" INDENT "> > > > No docLocation returned from openDoc.\n"
                  INDENT "        rc = ",
             out);

       fprintf(out, "%d, %s",
                    (int) rc, odmStatusName(rc) );

       switch (rc)
       {    /* Indicate if response is unexpected */
           case ODM_SUCCESS:
           case ODM_E_FAIL:
           case ODM_E_ACCESS:
           case ODM_E_INUSE:
           case ODM_E_DOCID:
                fputs("\n", out);
                return;
           }

       fputs(" *** INVALID ***\n", out);

       } /* reportNoOpen */


static void reportNoNewDoc(FILE* out, ODMSTATUS rc)
{   /* Show results of deriveNewDoc operation that didn't return a
       Document ID.
       */

       fputs("\n" INDENT "> > > > No DocID from deriveNewDoc.\n"
                  INDENT "        rc = ",
             out);

       fprintf(out, "%d, %s",
                    (int) rc, odmStatusName(rc) );

       switch (rc)
       {    /* Indicate if response is unexpected */
           case ODM_SUCCESS:
           case ODM_E_FAIL:
           case ODM_E_CANCEL:
           case ODM_E_APPSELECT:
                fputs("\n", out);
                return;
           }

       fputs(" *** UNEXPECTED ***\n", out);

       } /* reportNoNewDoc */



static void reportGoesIodmWrk(FILE * out)
{   /* Explain what happens when IodmWorking100 is released */

    fputs("\n" INDENT "> > > > No further use of the document interface\n"
               INDENT "        will be made.  The resources will be\n"
               INDENT "        released, and any document closed, when\n"
               INDENT "        the last reference to the interface is\n"
               INDENT "        released.\n",
          out);

    } /* reportGoesIodmWrk */


static void reportGoesDMS(FILE* out)
{   /* Explain how the DMS is released. */

    fputs("\n" INDENT "> > > There is no further need for the Default\n"
               INDENT "      DMS.  It will be released automatically\n"
               INDENT "      when all IodmApplication100 references\n"
               INDENT "      are released.\n",
          out);

    } /* reportGoesDMS */



static void reportGoesConMan(FILE* out)
{   /* Explain how the Connection Manager is released. */

    fputs("\n" INDENT "> > There is no further need for the ODMA\n"
               INDENT "    Connection Manager.  It will be released\n"
               INDENT "    automatically when all IodmApplication100\n"
               INDENT "    interface references are released.\n",
          out );

    } /* reportGoesConMan */



static void reportRelease(FILE *out, const char ifaceName[])
{   /* Just build a bit more statement. */

    fputs(INDENT "> ", out);
    fputs(ifaceName, out);
    fputs(" interface released.\n", out);

    }


static void goodbye(FILE *out)
{   /* Tell them who's leaving. */

    fputs("\nCheckChoice> exit\n\n", out);
    }



/* APPLICATION-IMPORTANT CONSTANTS
 * *******************************
 */

static const char pszTestAppId[ ] = "OdmNativeTest\0";
    /* The name of the application for all of our tests. Guard with
       an extra null byte.  This is a valid Application ID.
       */

static const IID IID_IodmUnknown = IID_IodmUnknown_ ;
    /* Standard IUnknown IID uniquelly named to avoid conflicts
       with definitions in other headers and external data. */

static const IID IID_IodmApplication100 = IID_IodmApplication100_ ;
    /* The IodmApplication100 IID of the current Odmnative release. */

static const IID IID_IodmWorking100 = IID_IodmWorking100_ ;
    /* The IodmWorking100 IID of the current OdmNative release. */

static const IID IID_IodmPending100 = IID_IodmPending100_ ;
    /* The IodmPending100 IID of the current OdmNative release. */

static const IID IID_IFoo = /* A never assigned IID used for fun. */
        { 0x12751e61, 0x9ef6, 0x451e,
            { 0x9a, 0xbd, 0xf9, 0x75, 0xad, 0x1d, 0x79, 0x91 } };



/* MAIN CHECKCHOICE FUNCTION DEFINITION
 * ************************************
 */

 int main( int argc, char *argv[])

 {  /* Use simple console application with no command-line parameters.
       */

    /* GUARDED REFERENCE POINTERS AND STATE.  This data is managed
       is as part of an always stable state for the application.
       Values reflect the availability conditions of ODMA to the
       application.
       */

    IUnknown *pIFooUnknown = NULL;
        /* Not expected to produce a valid result.
           This is used solely as a negative confirmation.
           */

    IUnknown *pIodmAppUnk = NULL;
        /* The IUnknown interface of the OdmApplication Object, it
           would seem.  If we acquire this, we use it to Query Interface
           for the next one.
           */

    IodmApplication100 *pIodmApp = NULL;
        /* The Interface that allows us to do a little work.  This is
           the helper level for odmjni100 implementation of the
           info.odma.practical100.OdmConnection interface.
           */

    IodmWorking100 *pIodmWrk = NULL;
        /* The interface to the working document, if we manage to get
           that far. */

    IodmPending100 *pIodmPend = NULL;
        /* The interface to the pending document if we manage to get
           that far through Save As ... after working the Working document.
           */

    HRESULT rc = S_OK;
        /* Result code for HRESULT-producing functions and methods. */

    BOOL proceedOK = FALSE;
        /* Boolean result for BOOL-returning interface methods. */

    int releaseCount = 0;
        /* Count of releases done at the end. */

    /* IDENTIFY APPLICATION.  This just shows which test is running.
       */

    hello(stdout);


    /* ACQUIRE THE APPLICATION-LEVEL INTERFACE.  Demonstrate that
       we are (or are not) obtaining the necessary interfaces.
       */

    rc = OdmBindNative100(pszTestAppId, IID_IFoo,
                                        (void**) &pIFooUnknown);
    reportIface(stdout, INDENT, "IFoo", rc, pIFooUnknown);

    rc = OdmBindNative100(pszTestAppId, IID_IodmUnknown,
                                       (void**) &pIodmAppUnk);
    reportIface(stdout, INDENT, "IUnknown", rc, pIodmAppUnk);

    if (SUCCEEDED(rc))
         rc = pIodmAppUnk -> QueryInterface( IID_IodmApplication100,
                                             (void**) &pIodmApp );

    else rc = OdmBindNative100(pszTestAppId, IID_IodmApplication100,
                                             (void**) &pIodmApp );

    reportIface(stdout, INDENT, "IodmApplication100", rc, pIodmApp);


    /* REPORT WHETHER HAVE INTERFACE AND CAN PROCEED
    */

    proceedOK = (pIodmApp != NULL);

    reportIodmApp(stdout, proceedOK);
        /* Reporting whether it is possible to proceed.
           TESTME: There are more cases of proper QueryInterface
                   and reference counting.  We need a test fixture for
                   that, and also on the defensive measures in the
                   IUnknown implementations.
           */

    if (proceedOK)
         reportIfaceVersion(  stdout, INDENT "  ",
                              pIodmApp -> interfaceImplementation()
                              );

         /* FIXME: This information needs to be presented in a way
                   that fits the indentation that we are using.
                   */


    /* DETERMINE AVAILABILITY OF CONNECTION MANAGER
       */

    if (proceedOK)
         {  /* Determine whether we have the Connection Manager
               and can continue.
               */

            proceedOK = ( pIodmApp -> hasConMan() );

            reportHasConMan(stdout, proceedOK);

            if (!proceedOK)
                reportUnexpectedDMS(stdout, pIodmApp -> hasDefaultDMS() );

            }

    /* DETERMINE AVAILABILITY OF A DEFAULT DMS FOR THE APPLICATION
       */

    if (proceedOK)
         {  /* Determine if we have a default DMS so we can continue
               with operations.
               */

            proceedOK = ( pIodmApp -> hasDefaultDMS() );

            reportHasDefaultDMS(stdout, proceedOK);

            }


    /* HAVE THE DEFAULT DMS CHOOSE A DOCUMENT ID IN COOPERATION WITH
       THE USER.

       FIXME: When OdmNative is reabstracted, all of this code should
              reduce to a single call that gets an HRESULT to report
              and a Document Interface when successful.  That will
              go much smoother than this version.
       */

    char theDocId[ODM_DOCID_MAX+2] = {'\0'};
            /* Buffer for any returned Document ID, with a little
               extra padding.
               */

    BOOL viewMode = FALSE;
            /* Set to indicate whether the document should be opened in
               view mode rather than modify mode. */

    ODMSTATUS odmResult = ODM_SUCCESS;

    if (proceedOK)
         {  /* Use the Default DMS to perform a selectDoc and start
               using the result.
               */


            odmResult = pIodmApp -> selectDocID(theDocId, &viewMode);
            /* OK, let's see what we have. */

            if (odmResult != ODM_SUCCESS)
                 reportNoSelect(stdout, odmResult);
                    /* We have no Document ID so there is no further
                       we can go. */

            proceedOK = (odmResult == ODM_SUCCESS);

            }

#define docSLIDER INDENT "> > > > "
#define docINDENT INDENT "        "

    if (proceedOK)

         {  fputs("\n"  docSLIDER "A Document ID is selected for ",
                  stdout);

            if (viewMode)
                 fputs("viewing", stdout);
            else fputs("modification", stdout);

            fputs(".\n" docINDENT "Doc ID ", stdout);

            fputs(theDocId, stdout);

            fputs("\n", stdout);

            }

    char theDocLoc[ODM_FILENAME_MAX+20] = {'\0'};
        /* Where we store our version of the Document Location */

    if (proceedOK)

         {  odmResult = pIodmApp -> openDoc(theDocId, theDocLoc, viewMode);
                /* The ID was from a select, so open should be possible */

            if (odmResult != ODM_SUCCESS)
                     reportNoOpen(stdout, odmResult);
                        /* And if not, say why. */

            proceedOK = (odmResult == ODM_SUCCESS);

            }

    if (proceedOK)

         {  /* Report the Document Location for the opened Document */

            fputs("\n"  docSLIDER "File-system location of content:\n"
                        docINDENT,
                  stdout);

            fputs(theDocLoc, stdout);

            fputs("\n", stdout);

            }


    if (proceedOK)

         {  /* Make a Working Document that we can then use to perform
               all further operations on the specific document and its
               delivered content.
               */

               rc = pIodmApp -> makeWorkingDocument
                                    (  theDocId, theDocLoc, viewMode,
                                       IID_IodmWorking100,
                                       (void **) &pIodmWrk
                                       );

               reportIface(  stdout, "\n" docSLIDER,
                             "IodmWorking100", rc, pIodmWrk
                             );

               proceedOK = SUCCEEDED(rc);

               }

    workWork(stdout, docINDENT,
             theDocId, theDocLoc,
             pIodmWrk);

    char response[10] = {'\0'};

    if (proceedOK)

         {  /* We have a good working document.  Now lets see whether
               we should do a save as. */

            fputs("\n\n", stdout);
            fputs(docINDENT "Document ", stdout);
            fputs(theDocId, stdout);
            fputs("\n" docINDENT "SAVE AS ... ? (y/N): ", stdout);
            fflush(stdout);
            fgets(response, sizeof(response), stdin);
            if (response[0] == 'N' || response[0] == 'n')
                 proceedOK = FALSE;
            }

    if (proceedOK)

        {   /* Let's try the SaveAs operation. */

            odmResult = pIodmWrk -> deriveNewDoc(".txt", theDocId);

            if (odmResult != ODM_SUCCESS)
                 reportNoNewDoc(stdout, odmResult);
                    /* We have no Document ID so there is no further
                       we can go. */

            proceedOK = (odmResult == ODM_SUCCESS);

            }

    if (proceedOK)

         {  fputs("\n" docSLIDER "New DMS document derived for "
                        "saving content.\n",
                  stdout);

            fputs(docINDENT "Doc ID ", stdout);

            fputs(theDocId, stdout);

            fputs("\n", stdout);

            rc = pIodmWrk -> openPendingDoc
                                ( theDocId,
                                  IID_IodmPending100,
                                  (void**) &pIodmPend );

                /* This should always  work*/

            reportIface(  stdout, "\n" docSLIDER,
                          "IodmPending100", rc, pIodmPend
                          );

            proceedOK = SUCCEEDED(rc);

            }

    if (proceedOK)
         proceedOK = workPending(  stdout, docINDENT,
                                   theDocLoc,
                                   pIodmPend
                                   );
            /* We should be using a different DocLoc, which is fine */

    IUnknown *pIfromPending = NULL;

    if (proceedOK)
         {  /* We can go ahead and commit content. */

            rc = pIodmPend -> commitContent(&pIfromPending);

            proceedOK = SUCCEEDED(rc);
            reportIface(stdout, "\n" docINDENT,
                                "IUnknown commitContent",
                                rc, pIfromPending
                                );
            if (!proceedOK)
                 {  /* The CommitContent operation failed */
                    fputs(   "n" docSLIDER
                                 "COMMIT CONTENT FAILED",
                             stdout);
                    }
            }

    if (proceedOK)

         {  /* Setup the Working Document for further operation.
               */

            pIodmWrk -> Release();
                /* In this SaveAs scenario, we are done with the first
                   Working Document and move to a new one.
                   */

            rc = pIfromPending -> QueryInterface
                                    (  IID_IodmWorking100,
                                       (void **) &pIodmWrk
                                       );

            pIfromPending -> Release();

            reportIface(  stdout, "\n" docSLIDER,
                          "IodmWorking100", rc, pIodmWrk
                          );

            proceedOK = SUCCEEDED(rc);

            }

    workWork(stdout, docINDENT,
             theDocId, theDocLoc,
             pIodmWrk);



    /* ACCOUNT FOR THE RELEASE OF RESOURCES THAT ARE BEING HELD. */

    if ( pIodmWrk != NULL )
         {  /* Report the ended-use of the working document.
               We actually release it last so that we confirm the
               proper sequence of releases and the ODMA operations that
               go with them.
               */

               reportGoesIodmWrk(stdout);
               }

    if ( pIodmApp != NULL )
         {  /* Report how the resources will be unwound. */

            if ( pIodmApp -> hasConMan() )

                 {   /* Only cover the legitimate DMS case.  We
                        have complained about any infraction
                        already. */

                     if ( pIodmApp -> hasDefaultDMS() )

                          reportGoesDMS(stdout);

                     reportGoesConMan(stdout);

                     }
            }


    /* GUARDED RELEASE PROCESS.  All interface references are released
       if they are (still) held at the conclusion of the program.

       If any resources have been acquired in the course of
       IodmApplication100 usage, they will be released automatically
       when the last reference to the interface is released.
       */

    fputs("\n", stdout);

    if (pIodmApp != NULL)
        { pIodmApp -> Release(); pIodmApp = NULL;
          reportRelease(stdout, "IodmApplication100");
          ++releaseCount;
          }

    if (pIodmAppUnk != NULL)
        { pIodmAppUnk -> Release(); pIodmAppUnk = NULL;
          reportRelease(stdout, "IUnknown");
          ++releaseCount;
          }

    if (pIFooUnknown != NULL)
        { pIFooUnknown -> Release(); pIFooUnknown = NULL;
          reportRelease(stdout, "IFoo (??)");
          ++releaseCount;
          }

    if (pIodmWrk != NULL)
        { pIodmWrk -> Release(); pIodmWrk = NULL;
          reportRelease(stdout, "IodmWorking100");
          ++releaseCount;
          }

    if (pIodmPend != NULL)
        { pIodmPend -> Release(); pIodmPend = NULL;
          reportRelease(stdout, "IodmPending100");
          ++releaseCount;
          }


    if (releaseCount > 0)
        fputs("\n", stdout);

    goodbye(stdout);

    return 0;

    } /* main */


/* 0.13 2007-01-19-20:45 Integrate a "Save As ..." option.

   0.12 2007-01-17-21:57 Correct message in never-tested reportNoOpen
        routine.

   0.11 2007-01-17-19:19 Factor out the Working Document Exercise for
        shared use, since all interesting functions end up with Working
        Documents.

   0.10 2007-01-17-18:31 Factor out some CheckUtils in preparation for
        factoring more functions.  Do regression confirmation.

   0.09 2007-01-17-17:10 Convert directly from Check05 0.08 for regression
        before making any further modifications.

   0.08 2007-01-12-19:30 Add and confirm the code for iterating through
        commitChanges.

   0.07 2007-01-02-20:46 Report the document properties.

   0.06 2007-01-02-20:08 Check the docId and docLocation results for
        having the same values used in creating the document object.

   0.05 2007-01-02-18:25 Show the interfaceImplementation and the
        viewOnly status of the IodmWorking100 interface of a successfully-
        established working document.

   0.04 2007-01-02-17:39 Instantiate the IodmWorking100 interface for a
        successfully-opened DMS Document, reporting the result.  Demonstrate
        that the IodmWorking100 interface releases properly and with an
        ODMCloseDoc operation, and that it holds an IodmApplication100i
        interface properly too.

   0.03 2007-01-02-17:17 Report the result of successful OpenDoc.

   0.02 2007-01-02-16:33 Add the OpenDoc case and report the details.
        See what happens with the Odma32.log with an unbalanced open.

   0.01 2007-01-02-14:38 Make initial changes to prepare to open a
        selected document IodmApplication100.selectDoc() has a document
        result: Expand reportIface() to handle different indentations, and
        refactor traceSelectDoc back into Main and confirm that the
        regression case still runs.

   0.00 2006-12-20-16:40 Start by cloning Setup04 0.20 as Check05 and
        producing a clean regression check before we do anything else.

   $Header: /ODMdev/info/odma/OdmNative100/test/Check06/CheckChoice.cpp 36    07-01-19 23:04 Orcmid $
   */

/*                      *** end of CheckChoice.cpp ***                  */

