// Copyright (C) 2007-2012 Bristle Software, Inc.
// 
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 1, 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 General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc.

/******************************************************************************
* com.bristle.jslib.NotesUtl.js
*******************************************************************************
* Purpose:
*       This file contains utility routines for use with Lotus Notes.
* Usage:
*       - The typical scenario for using this file from an HTML file is:
*         <script language='JavaScript' src='com.bristle.jslib.NotesUtl.js'></script>
*         Call the various functions that reside here.
* Assumptions:
*       - The file "com.bristle.jslib.Util.js" has already been loaded.
*       - The following ActiveX objects are installed.
*         - MSXML.DLL  - XML and XSL support
*         - Lotus Notes v5.0.2b or higher.
* Effects:
* Anticipated Changes:
* Notes:
* Implementation Notes:
* Portability Issues:
* Revision History:
*   $Log$
******************************************************************************/

// Create the "namespace" to hold the functions in this file.
com.bristle.jslib.NotesUtl = {};

/******************************************************************************
* throwError
*******************************************************************************
* Encode error details into a string and throw it.
*
* Anticipated Changes:
* - Re-write this to dynamically create a JavaScript object named 
*   something like Error with Number, Description, etc.
******************************************************************************/
com.bristle.jslib.NotesUtl.strERROR_PREFIX = "runNotesAgent: ERROR ";
com.bristle.jslib.NotesUtl.throwError =
function(intNumber, strDescription)
{
    throw com.bristle.jslib.NotesUtl.strERROR_PREFIX 
          + intNumber + ": " + strDescription;
}

/******************************************************************************
* getErrorNumber
*******************************************************************************
* Extract error number from error thrown by 
* com.bristle.jslib.NotesUtl.throwError.
*
* Anticipated Changes:
* - Once com.bristle.jslib.NotesUtl.throwError is re-written to throw a 
*   JavaScript object, change this to return the Number property of the 
*   object.
******************************************************************************/
com.bristle.jslib.NotesUtl.getErrorNumber =
function(strError)
{
    // Skip past the prefix, then parse the rest of the string as a 
    // base 10 number, stopping at the first non-digit.
    return parseInt
                (strError.substr
                        (com.bristle.jslib.NotesUtl.strERROR_PREFIX.length), 
                 10);
}

/******************************************************************************
* Error constants.
******************************************************************************/
com.bristle.jslib.NotesUtl.intERROR_CREATING_NOTES_SESSION =
function() { return 1; }
com.bristle.jslib.NotesUtl.intERROR_INITIALIZING_NOTES_SESSION =
function() { return 2; }
com.bristle.jslib.NotesUtl.intERROR_objNotes_MISSING =
function() { return 3; }
com.bristle.jslib.NotesUtl.intERROR_strServer_MISSING =
function() { return 4; }
com.bristle.jslib.NotesUtl.intERROR_strDBFile_MISSING =
function() { return 5; }
com.bristle.jslib.NotesUtl.intERROR_strAgent_MISSING =
function() { return 6; }
com.bristle.jslib.NotesUtl.intERROR_strArg_MISSING =
function() { return 7; }
com.bristle.jslib.NotesUtl.intERROR_OPENING_DATABASE =
function() { return 8; }
com.bristle.jslib.NotesUtl.intERROR_CREATING_AGENT_DOCUMENT =
function() { return 9; }
com.bristle.jslib.NotesUtl.intERROR_ADDING_ITEMS_TO_AGENT_DOCUMENT =
function() { return 10; }
com.bristle.jslib.NotesUtl.intERROR_SAVING_AGENT_DOCUMENT =
function() { return 11; }
com.bristle.jslib.NotesUtl.intERROR_ACCESSING_AGENT =
function() { return 12; }
com.bristle.jslib.NotesUtl.intERROR_RUNNING_AGENT =
function() { return 13; }
com.bristle.jslib.NotesUtl.intERROR_RETRIEVING_RETURN_TEXT =
function() { return 14; }


/******************************************************************************
* Initialize Notes.
*
* Note:  It is useful to have this as a separate function, not called 
*        automatically by the other functions in this file, because it
*        causes the user to be prompted for a Notes password, and we
*        don't want to prompt the user more often than necessary for 
*        the same password.
*
*@throws com.bristle.jslib.NotesUtl.intERROR_CREATING_NOTES_SESSION()
*@throws com.bristle.jslib.NotesUtl.intERROR_INITIALIZING_NOTES_SESSION()
******************************************************************************/
com.bristle.jslib.NotesUtl.initNotes =
function()
{
    //
    // Create and initialize Notes session.
    //
    var objNotes;
    try 
    {
        objNotes = new ActiveXObject("Lotus.NotesSession");
    }
    catch(exception)
    {
        com.bristle.jslib.NotesUtl.throwError
                  (com.bristle.jslib.NotesUtl.intERROR_CREATING_NOTES_SESSION(), 
                   "Error creating Notes session object.<br />\n"
                   + "Perhaps your computer is not configured to allow "
                   + "the Web browser to invoke Lotus Notes?<br />\n"
                   + "Error reported as:<br />\n" 
                   + com.bristle.jslib.Util.formatScriptError(exception));
    }
    try 
    {
        objNotes.Initialize();
    }
    catch(exception) 
    {
        com.bristle.jslib.NotesUtl.throwError
                 (com.bristle.jslib.NotesUtl.intERROR_INITIALIZING_NOTES_SESSION(), 
                  "Error initializing Notes session<br />\n" 
                  + com.bristle.jslib.Util.formatScriptError(exception));
    }
    return objNotes;
}

/******************************************************************************
* Run an agent in a Notes database. 
*
*@param strServer Notes server on which the database resides 
*                 (blank for local db)
*@param strDBFile Path to database file 
*@param strAgent  Name of agent to run
*@param strArg    String to pass to agent
*@return          String passed back by agent
*@throws          com.bristle.jslib.NotesUtl.intERROR_objNotes_MISSING()
*@throws          com.bristle.jslib.NotesUtl.intERROR_strServer_MISSING()
*@throws          com.bristle.jslib.NotesUtl.intERROR_strDBFile_MISSING()
*@throws          com.bristle.jslib.NotesUtl.intERROR_strAgent_MISSING()
*@throws          com.bristle.jslib.NotesUtl.intERROR_strArg_MISSING()
*@throws          com.bristle.jslib.NotesUtl.intERROR_OPENING_DATABASE()
*@throws          com.bristle.jslib.NotesUtl.intERROR_CREATING_AGENT_DOCUMENT()
*@throws          com.bristle.jslib.NotesUtl.intERROR_ADDING_ITEMS_TO_AGENT_DOCUMENT()
*@throws          com.bristle.jslib.NotesUtl.intERROR_SAVING_AGENT_DOCUMENT()
*@throws          com.bristle.jslib.NotesUtl.intERROR_ACCESSING_AGENT()
*@throws          com.bristle.jslib.NotesUtl.intERROR_RUNNING_AGENT()
*@throws          com.bristle.jslib.NotesUtl.intERROR_RETRIEVING_RETURN_TEXT()
******************************************************************************/
com.bristle.jslib.NotesUtl.runNotesAgent =
function(objNotes, strServer, strDBFile, strAgent, strArg)
{
    //
    // Validate params.
    //
    if (objNotes==null)
    {
        com.bristle.jslib.NotesUtl.throwError
                  (com.bristle.jslib.NotesUtl.intERROR_objNotes_MISSING(), 
                   "Error objNotes must be specified");
    }
    if (strServer==null)
    {
        com.bristle.jslib.NotesUtl.throwError
                  (com.bristle.jslib.NotesUtl.intERROR_strServer_MISSING(), 
                   "Error strServer must be specified or blank");
    }
    if (strDBFile==null || strDBFile=="") 
    {
        com.bristle.jslib.NotesUtl.throwError
                  (com.bristle.jslib.NotesUtl.intERROR_strDBFile_MISSING(), 
                   "Error strDBFile must be specified");
    }
    if (strAgent==null || strAgent=="") 
    {
        com.bristle.jslib.NotesUtl.throwError
                  (com.bristle.jslib.NotesUtl.intERROR_strAgent_MISSING(), 
                   "Error strAgent must be specified");
    }
    if (strArg==null) 
    {
        com.bristle.jslib.NotesUtl.throwError
                  (com.bristle.jslib.NotesUtl.intERROR_strArg_MISSING(), 
                   "Error strArg must be specified or blank");
    } 

    //
    // Get specified Notes DB.
    //
    var objNotesDB;
    try 
    {
        // Note:  Does not report error if nonexistent server!
        //        ?? What to do about that?
        var objNotesDBDir = objNotes.GetDBDirectory(strServer);
        objNotesDB = objNotesDBDir.OpenDatabase(strDBFile);
    }
    catch(exception)
    {
        com.bristle.jslib.NotesUtl.throwError
                  (com.bristle.jslib.NotesUtl.intERROR_OPENING_DATABASE(), 
                   "Error opening database " + strServer 
                    + " " + strDBFile + "<br />\n" 
                    + com.bristle.jslib.Util.formatScriptError(exception));
    }

    //
    // Create Notes document containing inputs to the agent, and 
    // retain its NoteId for use later.
    //
    var objNotesDoc;
    try
    {
        objNotesDoc = objNotesDB.CreateDocument();
    }
    catch(exception)
    {
        com.bristle.jslib.NotesUtl.throwError
                  (com.bristle.jslib.NotesUtl.intERROR_CREATING_AGENT_DOCUMENT(), 
                   "Error creating agent document<br />\n" 
                   + com.bristle.jslib.Util.formatScriptError(exception));
    }
    try 
    {
        objNotesDoc.AppendItemValue("ArgumentText",strArg);
        objNotesDoc.AppendItemValue("TargetAgent",strAgent);

        var objNotesItem =
           objNotesDoc.AppendItemValue("User",objNotes.UserName);
        objNotesItem.IsAuthors = true;

        objNotesDoc.AppendItemValue("Form","Agent Document");
    }
    catch(exception) 
    {
        com.bristle.jslib.NotesUtl.throwError
                  (com.bristle.jslib.NotesUtl.intERROR_ADDING_ITEMS_TO_AGENT_DOCUMENT(), 
                   "Error adding items to agent document<br />\n" 
                   + com.bristle.jslib.Util.formatScriptError(exception));
    }
    var intNoteID;
    try
    {
        objNotesDoc.Save(true,false);
        intNoteID = objNotesDoc.NoteID;
    }
    catch(exception) 
    {
        com.bristle.jslib.NotesUtl.throwError
                  (com.bristle.jslib.NotesUtl.intERROR_SAVING_AGENT_DOCUMENT(), 
                   "Error saving agent document<br />\n" 
                   + com.bristle.jslib.Util.formatScriptError(exception));
    }

    //
    // Run Notes agent.
    //
    var objNotesAgent;
    try
    {
        objNotesAgent = objNotesDB.GetAgent(strAgent);
    }
    catch(exception) 
    {
        com.bristle.jslib.NotesUtl.throwError
                  (com.bristle.jslib.NotesUtl.intERROR_ACCESSING_AGENT(), 
                   "Error accessing agent " + strAgent + "<br />\n" 
                   + com.bristle.jslib.Util.formatScriptError(exception));
    }
    // Note:  Have to test explicitly for null because it sometimes
    //        happens, even though GetAgent throws no exception.
    //        Why?
    if (objNotesAgent == null)
    {
        com.bristle.jslib.NotesUtl.throwError
                  (com.bristle.jslib.NotesUtl.intERROR_ACCESSING_AGENT(), 
                   "Error accessing agent " + strAgent + "<br />\n" 
                   + "GetAgent returned null.");
    }
    try 
    {
        objNotesAgent.Run(intNoteID);
    }
    catch(exception)
    {
        com.bristle.jslib.NotesUtl.throwError
                  (com.bristle.jslib.NotesUtl.intERROR_RUNNING_AGENT(), 
                   "Error running agent " + strAgent + "<br />\n" 
                   + com.bristle.jslib.Util.formatScriptError(exception));
    }
    //
    // Reopen the database to get a fresh copy of the input doc, which 
    // has now been updated by the agent.
    //
    var strOutput;
    try 
    {
        var objNotesDBDir = objNotes.GetDBDirectory(strServer);
        var objNotesDB = objNotesDBDir.OpenDatabase(strDBFile); 
        var objNotesDoc = objNotesDB.GetDocumentByID(intNoteID);
        var objNotesItem = objNotesDoc.GetFirstItem("ReturnText");
//??alert("Successfully ran agent and got first item: " + objNotesItem + "\n"
//??    + "About to get text from it.\n"
//??    + "\n"
//??    + "Warning!  It sometimes crashes here.  Why?");
        strOutput = objNotesItem.Text;
//??alert("Successfully got text");
    }
    catch(exception) 
    {
        com.bristle.jslib.NotesUtl.throwError
                  (com.bristle.jslib.NotesUtl.intERROR_RETRIEVING_RETURN_TEXT(), 
                   "Error retrieving return text<br />\n" 
                   + com.bristle.jslib.Util.formatScriptError(exception));
    }

    return strOutput;
}

