/* 
 * The contents of this file are subject to the Mozilla Public
 * License Version 1.1 (the "License"); you may not use this file
 * except in compliance with the License. You may obtain a copy of
 * the License at http://www.mozilla.org/MPL/
 * 
 * Software distributed under the License is distributed on an "AS
 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * rights and limitations under the License.
 * 
 * The Original Code is the Sablotron XSLT Processor.
 * 
 * The Initial Developer of the Original Code is Ginger Alliance Ltd.
 * Portions created by Ginger Alliance are Copyright (C) 2000 Ginger
 * Alliance Ltd. All Rights Reserved.
 * 
 * Contributor(s):
 * 
 * Alternatively, the contents of this file may be used under the
 * terms of the GNU General Public License Version 2 or later (the
 * "GPL"), in which case the provisions of the GPL are applicable 
 * instead of those above.  If you wish to allow use of your 
 * version of this file only under the terms of the GPL and not to
 * allow others to use your version of this file under the MPL,
 * indicate your decision by deleting the provisions above and
 * replace them with the notice and other provisions required by
 * the GPL.  If you do not delete the provisions above, a recipient
 * may use your version of this file under either the MPL or the
 * GPL.
 */

/****************************************
*                                       *
i n c l u d e s
*                                       *
****************************************/

#include<iostream.h>
#include <stdarg.h>

#define SablotAsExport
#include "sablot.h"

#include "base.h"
#include "verts.h"
#include "tree.h"
#include "expr.h"
#include "proc.h"
#include "situa.h"

//
//  defines
//

#define P( PTR ) ((Processor *)(PTR))
#define EE(statement) {int code___=(statement); if (code___) return code___;}

/****************************************
g l o b a l s
****************************************/
#define checkErr(situation) {if (situation->isError()) \
    {Warn(situation, W_PENDING_ERROR); return NOT_OK;}}


/****************************************
d e f i n i t i o n s
****************************************/

void doStart(SituationObj* situation)
{
    Log1(situation, L_START, situation->timeStr());
};

void doEnd(SituationObj* situation)
{
    Log1(situation, L_STOP, situation->timeStr());
}

#define ErrQ(situation, e) {\
    if (situation) { \
        situation->error(e,theEmptyString,theEmptyString); \
        doEnd(situation); \
        return e; \
    } \
}


//
//
//      SablotCreateProcessor
//
//

int SablotCreateProcessor(void **processorPtr)
{
    // to debug memory leaks in MSVC, uncomment the call to checkLeak()
    // and set _crtBreakAlloc to the # of block (proc=51)
#if defined(__linux__) && defined(CHECK_LEAKS)
    mtrace();
#endif
    *processorPtr = new Processor;
    if (!*processorPtr)
    {
        SituationObj* situation = NULL;
        ErrQ( situation, E_MEMORY );
    }

    checkErr( P(*processorPtr)->situation );
    doStart ( P(*processorPtr)->situation );
    return OK;
}


//
//
//      SablotRunProcessor
//
//
int SablotRunProcessor(void *processor_,
                       char *sheetURI, 
                       char *inputURI, 
                       char *resultURI,
                       char **params, 
                       char **arguments)
{
    Bool problem = FALSE;

#ifndef __BORLANDC__
    try
#endif
    {
        // remove the existing 'arg' buffers
        E( SablotFreeResultArgs (processor_));
        P(processor_) -> prepareForRun();
        char **p;
        if (arguments)
        {
            for (p = arguments; *p && !problem; p += 2)
                problem = P(processor_) -> useArg(p[0], p[1]);
        }
        if (params)
        {
            for (p = params; *p && !problem; p += 2)
                problem = P(processor_) -> useGlobalParam(p[0], p[1]);
        }
        if (problem || P(processor_) -> open(sheetURI,inputURI,resultURI))
        {
            int code = P(processor_) -> situation -> getError();
            P(processor_) -> cleanupAfterRun();
            P(processor_) -> freeResultArgs();
//            cdelete(proc);
            P(processor_) -> situation->clear();
            return code;
        }
    }
#ifndef __BORLANDC__
    catch (MsgCode e)
    {
        ErrQ(P(processor_)->situation, e);
    };
#endif
    P(processor_) -> cleanupAfterRun();
    return OK;
}



//
//
//      SablotDestroyProcessor
//
//


int SablotDestroyProcessor(void *processor_)
{
    Processor *proc = P( processor_ );
    proc ->situation->clear();
    doEnd(proc -> situation);
    EE( SablotFreeResultArgs (processor_));
    cdelete( proc );
#if defined(WIN32) && defined(CHECK_LEAKS)
    memStats();
    checkLeak();
#endif
    return OK;
};



//
//
//      SablotSetBase
//
//

int SablotSetBase(void* processor_, 
                  const char *theBase)
{
    P(processor_) -> setHardBaseURI(theBase);
    return OK;
}


//
//
//      SablotSetBaseForScheme
//
//

int SablotSetBaseForScheme(void* processor_, 
                  const char *scheme, const char *base)
{
    P(processor_) -> addBaseURIMapping(scheme, base);
    return OK;
}




//
//
//      SablotGetResultArg
//
//

int SablotGetResultArg(void *processor_,
                       char *argURI,
                       char **argValue)
{
    char *newCopy;
    // we even obtain the index of the output buffer but won't use it
    int resultNdx_;
    if (argValue)
    {
        P(processor_) -> copyArg(argURI, &resultNdx_, newCopy);
        // if output does not go to a buffer, newCopy is NULL
        *argValue = newCopy;
    }
    return OK;
}


//
//
//      SablotSetLog
//
//

int SablotSetLog(void *processor_,
    const char *logFilename, int logLevel)
{
    (P(processor_) -> situation)->msgOutputFile("stderr",(char*) logFilename);
    // logLevel ignored so far
    return OK;
}


/*****************************************************************
SablotProcess

  the main function published by Sablotron. Feeds given XML
  input to a stylesheet. Both of these as well as the location for
  output are identified by a URI. One can also pass top-level
  stylesheet parameters and named buffers ('args').
ARGS
  sheetURI      URI of the XSLT stylesheet
  inputURI      URI of the XML document
  resultURI     URI of the output document
  params        a NULL-terminated array of char*'s defining the top-level
                parameters to be passed, interpreted as a
                sequence of (name, value) pairs. 
  arguments     a NULL-terminated array of char*'s defining the named
                buffers to be passed, interpreted as a
                sequence of (name, value) pairs. Both params and arguments
                may be NULL. 
RETURNS
  .             nonzero iff error
  resultArg     in case output goes to a named buffer, *resultArg is set to                     
                point to it (otherwise to NULL). Free it using SablotFree().
*****************************************************************/


int SablotProcess(char *sheetURI, char *inputURI, char *resultURI,
    char **params, char **arguments, char **resultArg)
{
    void *theproc;
    EE( SablotCreateProcessor (&theproc) );
    EE( SablotRunProcessor(theproc,
        sheetURI, inputURI, resultURI,
        params, arguments) );
    EE( SablotGetResultArg(theproc, resultURI, resultArg) );
    EE( SablotDestroyProcessor(theproc) );
    return OK;
};


/*****************************************************************
SablotProcessFiles

  calls SablotProcess to process files identified by their
  file names. No named buffers or params are passed. Included
  for backward compatibility.
ARGUMENTS
  styleSheetName, inputName, resultName
            file names of the stylesheet, XML input and output,
            respectively.
RETURNS
  .         error flag
*****************************************************************/


int SablotProcessFiles(char *styleSheetName,
                       char *inputName,
                       char *resultName)
{
    return SablotProcess(
        styleSheetName, inputName, resultName, 
        NULL, NULL, NULL);
};


/*****************************************************************
SablotProcessStrings

  calls SablotProcess to process documents in memory, passing
  pointers to the documents. No named buffers or params are passed.
  Included for backward compatibility.
ARGUMENTS
  styleSheetStr, inputStr
                text of the stylesheet and the XML input
RETURNS
  .             error flag
  *resultStr    pointer to the newly allocated block containing
                the result 
*****************************************************************/
int SablotProcessStrings(char *styleSheetStr,
                         char *inputStr,
                         char **resultStr)
{
  char *argums[] =
  {
    "/_stylesheet", styleSheetStr,
    "/_xmlinput", inputStr,
    "/_output", NULL, //was: *resultStr,
    NULL
  };
  return SablotProcess("arg:/_stylesheet","arg:/_xmlinput","arg:/_output",
                    NULL, argums, resultStr);
};


/*****************************************************************
SablotProcessStringsWithBase

    Like SablotProcessStrings but lets you pass an URI that replaces
    the stylesheet's URI in relative address resolution.

ARGUMENTS
  styleSheetStr, inputStr
                text of the stylesheet and the XML input
  theHardBase   the "hard base URI" replacing the stylesheet's URI
                in all relevant situations
RETURNS
  .             error flag
  *resultStr    pointer to the newly allocated block containing
                the result 
*****************************************************************/

int SablotProcessStringsWithBase(char *styleSheetStr,
                         char *inputStr,
                         char **resultStr,
                         char *theHardBase)
{
    char *argums[] =
    {
        "/_stylesheet", styleSheetStr,
        "/_xmlinput", inputStr,
        "/_output", NULL, //was: *resultStr,
        NULL
    };
    void *theproc;
    EE( SablotCreateProcessor(&theproc));
    EE( SablotSetBase(theproc, theHardBase));
    EE( SablotRunProcessor(theproc,
        "arg:/_stylesheet", "arg:/_xmlinput", "arg:/_output",
        NULL, argums));
    EE( SablotGetResultArg(theproc,
        "arg:/_output", resultStr));
    EE( SablotDestroyProcessor(theproc));
    return OK;
};


/*****************************************************************
SablotFree

  Frees the Sablotron-allocated buffer.
*****************************************************************/

int SablotFree(char *resultStr)
{
    if (resultStr)
        free(resultStr);
    return OK;
}


//
//
//      SablotFreeResultArgs
//      frees the result arg buffers after the have been obtained
//      via SablotGetResultArg()
//
//

int SablotFreeResultArgs(void *processor_)
{
    EE( P(processor_) -> freeResultArgs() );
    return OK;
}


//
//
//      SablotRegHandler
//
//

int SablotRegHandler(void *processor_, 
                     /* HLR_MESSAGE, HLR_SCHEME, HLR_SAX, HLR_MISC */
                     HandlerType type,   
                     void *handler, 
                     void *userData)
{
    EE( P(processor_) -> setHandler(type, handler, userData) );
    if (type == HLR_MESSAGE)
        EE( P(processor_) -> situation -> closeFiles() );
    return OK;
}



//
//
//      SablotUnregHandler
//
//

int SablotUnregHandler(void *processor_, 
                       HandlerType type,   /* HLR_MESSAGE, HLR_SCHEME, HLR_SAX */
                       void *handler,
                       void *userData)
{
    EE( P(processor_) -> setHandler(type, NULL, NULL) );
    if (type == HLR_MESSAGE)
        EE( P(processor_) -> situation -> openDefaultFiles() );
    return OK;
}


//
//
//      SablotGetMsgText
//
//

const char *SablotGetMsgText(int code)
{
    return GetMessage((MsgCode) code) -> text;
}

//
//
//      SablotClearError
//
//

int SablotClearError(void *processor_)
{
    P(processor_) -> situation -> clear();
    return OK;
}

//
//
//      SablotSetInstanceData
//
//

void SablotSetInstanceData(void *processor_, void *idata)
{
    P(processor_) -> setInstanceData(idata);
}

//
//
//      SablotGetInstanceData
//
//

void *SablotGetInstanceData(void *processor_)
{
    return P(processor_) -> getInstanceData();
}
