/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- 
 * 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 TransforMiiX XSLT processor.
 * 
 * The Initial Developer of the Original Code is The MITRE Corporation.
 * Portions created by MITRE are Copyright (C) 1999 The MITRE Corporation.
 *
 * Portions created by Keith Visco as a Non MITRE employee,
 * (C) 1999 Keith Visco. All Rights Reserved.
 * 
 * Contributor(s): 
 * Keith Visco, kvisco@ziplink.net
 *    -- original author.
 *
 */



#ifndef TRANSFRMX_PROCESSORSTATE_H
#define TRANSFRMX_PROCESSORSTATE_H

#include "NodeSet.h"
#include "Stack.h"
#include "ErrorObserver.h"
#include "NamedMap.h"
#include "txPatternParser.h"
#include "Expr.h"
#include "StringList.h"
#include "txOutputFormat.h"
#include "Map.h"
#include "txIXPathContext.h"
#include "txExpandedNameMap.h"
#include "XSLTFunctions.h"
#include "txError.h"

class txVariableMap;
class txXSLKey;

/**
 * Class used for keeping the current state of the XSL Processor
 */
class ProcessorState : public txIMatchContext {

public:
    /**
     * Creates a new ProcessorState for the given XSL document
     * And result Document
     */
    ProcessorState(Document* aSourceDocument,
                   Document* aXslDocument,
                   Document* aResultDocument);

    /**
     * Destroys this ProcessorState
     */
    ~ProcessorState();

    /**
     * Contain information that is import precedence dependant.
     */
    class ImportFrame {
    public:
        ImportFrame(ImportFrame* aFirstNotImported);
        ~ImportFrame();
    
        // Map of named templates
        txExpandedNameMap mNamedTemplates;

        // Map of template modes, each item in the map is a list
        // of templates
        txExpandedNameMap mMatchableTemplates;

        // List of whitespace preserving and stripping nametests
        txList mWhiteNameTests;

        // Map of named attribute sets
        txExpandedNameMap mNamedAttributeSets;

        // Output format, as specified by the xsl:output elements
        txOutputFormat mOutputFormat;

        // ImportFrame which is the first one *not* imported by this frame
        ImportFrame* mFirstNotImported;

        // Map of top-level variables/parameters
        txExpandedNameMap mVariables;

        // The following stuff is missing here:

        // Namespace aliases (xsl:namespace-alias)
    };
    // To be able to do some cleaning up in destructor
    friend class ImportFrame;

    /**
     * Adds the given attribute set to the list of available named attribute
     * sets
     * @param aAttributeSet the Element to add as a named attribute set
     * @param aImportFrame  ImportFrame to add the attributeset to
     */
    void addAttributeSet(Element* aAttributeSet, ImportFrame* aImportFrame);

    /**
     * Registers the given ErrorObserver with this ProcessorState
     */
    void addErrorObserver(ErrorObserver& errorObserver);

    /**
     * Adds the given template to the list of templates to process
     * @param aXslTemplate  The Element to add as a template
     * @param aImportFrame  ImportFrame to add the template to
     */
    void addTemplate(Element* aXslTemplate, ImportFrame* aImportFrame);

    /**
     * Adds the given LRE Stylesheet to the list of templates to process
     * @param aStylesheet  The Stylesheet to add as a template
     * @param importFrame  ImportFrame to add the template to
     */
    void addLREStylesheet(Document* aStylesheet, ImportFrame* aImportFrame);

    /**
     * Returns the AttributeSet associated with the given name
     * or null if no AttributeSet is found
     */
    NodeSet* getAttributeSet(const txExpandedName& aName);

    /**
     * Returns the source node currently being processed
     */
    Node* getCurrentNode();

    /**
     * Returns the template associated with the given name, or
     * null if not template is found
     */
    Element* getNamedTemplate(const txExpandedName& aName);

    /**
     * Returns the OutputFormat which contains information on how
     * to serialize the output.
     */
    txOutputFormat* getOutputFormat();

    /**
     * Add a global variable
     */
    nsresult addGlobalVariable(Element* aVarElem,
                               ImportFrame* aImportFrame);

    /**
     * Returns map on top of the stack of local variable-bindings
     */
    txVariableMap* getLocalVariables();
    
    /**
     * Sets top map of the local variable-bindings stack
     */
    void setLocalVariables(txVariableMap* aMap);

    /**
     * Enums for the getExpr and getPattern functions
     */
    enum ExprAttr {
        SelectAttr = 0,
        TestAttr,
        ValueAttr
    };

    enum PatternAttr {
        CountAttr = 0,
        FromAttr
    };

    Expr* getExpr(Element* aElem, ExprAttr aAttr);
    txPattern* getPattern(Element* aElem, PatternAttr aAttr);

    /**
     * Returns a pointer to the result document
     */
    Document* getResultDocument();

    /**
     * Retrieve the document designated by the URI uri, using baseUri as base URI.
     * Parses it as an XML document, and returns it. If a fragment identifier is
     * supplied, the element with seleced id is returned.
     * The returned document is owned by the ProcessorState
     *
     * @param uri the URI of the document to retrieve
     * @param baseUri the base URI used to resolve the URI if uri is relative
     * @return loaded document or element pointed to by fragment identifier. If
     *         loading or parsing fails NULL will be returned.
     */
    Node* retrieveDocument(const String& uri, const String& baseUri);

    /**
     * Return stack of urls of currently entered stylesheets
     */
    Stack* getEnteredStylesheets();

    /**
     * Return list of import containers
     */
    List* getImportFrames();

    /**
     * Find template in specified mode matching the supplied node
     * @param aNode        node to find matching template for
     * @param aMode        mode of the template
     * @param aImportFrame out-param, is set to the ImportFrame containing
     *                     the found template
     * @return             root-node of found template, null if none is found
     */
    Node* findTemplate(Node* aNode,
                       const txExpandedName& aMode,
                       ImportFrame** aImportFrame)
    {
        return findTemplate(aNode, aMode, 0, aImportFrame);
    }

    /**
     * Find template in specified mode matching the supplied node. Only search
     * templates imported by a specific ImportFrame
     * @param aNode        node to find matching template for
     * @param aMode        mode of the template
     * @param aImportedBy  seach only templates imported by this ImportFrame,
     *                     or null to search all templates
     * @param aImportFrame out-param, is set to the ImportFrame containing
     *                     the found template
     * @return             root-node of found template, null if none is found
     */
    Node* findTemplate(Node* aNode,
                       const txExpandedName& aMode,
                       ImportFrame* aImportedBy,
                       ImportFrame** aImportFrame);

    /**
     * Struct holding information about a current template rule
     */
    struct TemplateRule {
        ImportFrame* mFrame;
        const txExpandedName* mMode;
        txVariableMap* mParams;
    };

    /**
     * Gets current template rule
     */
    TemplateRule* getCurrentTemplateRule();

    /**
     * Sets current template rule
     */
    void setCurrentTemplateRule(TemplateRule* aTemplateRule);

    /**
     * Adds the set of names to the Whitespace preserving element set
     */
    void preserveSpace(String& names);

    void processAttrValueTemplate(const String& aAttValue,
                                  Element* aContext,
                                  String& aResult);

    /**
     * Adds the set of names to the Whitespace stripping handling list.
     * xsl:strip-space calls this with MB_TRUE, xsl:preserve-space 
     * with MB_FALSE
     * aElement is used to resolve QNames
     */
    void shouldStripSpace(String& aNames, Element* aElement,
                          MBool aShouldStrip,
                          ImportFrame* aImportFrame);

    /**
     * Adds the supplied xsl:key to the set of keys
     */
    MBool addKey(Element* aKeyElem);

    /**
     * Returns the key with the supplied name
     * returns NULL if no such key exists
     */
    txXSLKey* getKey(txExpandedName& keyName);

    /**
     * Adds a decimal format. Returns false if the format already exists
     * but dosn't contain the exact same parametervalues
     */
    MBool addDecimalFormat(Element* element);

    /**
     * Returns a decimal format or NULL if no such format exists.
     */
    txDecimalFormat* getDecimalFormat(const txExpandedName& name);

    /**
     * Virtual methods from txIEvalContext
     */

    TX_DECL_MATCH_CONTEXT;

    /**
     * Set the current txIEvalContext and get the prior one
     */
    txIEvalContext* setEvalContext(txIEvalContext* aEContext)
    {
        txIEvalContext* tmp = mEvalContext;
        mEvalContext = aEContext;
        return tmp;
    }

    /**
     * Get the current txIEvalContext
     */
    txIEvalContext* getEvalContext()
    {
        return mEvalContext;
    }


    /**
     * More other functions
     */
    void receiveError(String& errorMessage)
    {
        receiveError(errorMessage, NS_ERROR_FAILURE);
    }

    /**
     * Returns a FunctionCall which has the given name.
     * @return the FunctionCall for the function with the given name.
     */
    nsresult resolveFunctionCall(txAtom* aName, PRInt32 aID,
                                 Element* aElem, FunctionCall*& aFunction);

private:

    class MatchableTemplate {
    public:
        MatchableTemplate(Node* aTemplate, txPattern* aPattern,
                          double aPriority)
            : mTemplate(aTemplate), mMatch(aPattern), mPriority(aPriority)
        {
        }
        Node* mTemplate;
        txPattern* mMatch;
        double mPriority;
    };
    
    class GlobalVariableValue : public TxObject {
    public:
        virtual ~GlobalVariableValue();
      
        ExprResult* mValue;
        MBool mEvaluating;
    };

    /**
     * The list of ErrorObservers registered with this ProcessorState
     */
    List errorObservers;

    /**
     * Stack of URIs for currently entered stylesheets
     */
    Stack enteredStylesheets;

    /**
     * List of import containers. Sorted by ascending import precedence
     */
    txList mImportFrames;

    /**
     * The output format used when serializing the result
     */
    txOutputFormat mOutputFormat;

    /**
     * The set of loaded documents. This includes both document() loaded
     * documents and xsl:include/xsl:import'ed documents.
     */
    NamedMap loadedDocuments;
    
    /**
     * The set of all available keys
     */
    txExpandedNameMap mXslKeys;

    /**
     * The set of all avalible decimalformats
     */
    txExpandedNameMap mDecimalFormats;
    
    /**
     * Default decimal-format
     */
    txDecimalFormat mDefaultDecimalFormat;

    /**
     * List of hashes with parsed expression. Every listitem holds the
     * expressions for an attribute name
     */
    Map            mExprHashes[3];

    /**
     * List of hashes with parsed patterns. Every listitem holds the
     * patterns for an attribute name
     */
    Map            mPatternHashes[2];

    /**
     * Current txIEvalContext*
     */
    txIEvalContext* mEvalContext;

    /**
     * Current template rule
     */
    TemplateRule*  mCurrentTemplateRule;

    /**
     * Top of stack of local variable-bindings
     */
    txVariableMap* mLocalVariables;
    
    /**
     * Map of values of global variables
     */
    txExpandedNameMap mGlobalVariableValues;

    Document*      mSourceDocument;
    Document*      xslDocument;
    Document*      resultDocument;
};

/**
 * txNameTestItem holds both an ElementExpr and a bool for use in
 * whitespace stripping.
 */
class txNameTestItem {
public:
    txNameTestItem(txAtom* aPrefix, txAtom* aLocalName, PRInt32 aNSID,
                   MBool stripSpace)
        : mNameTest(aPrefix, aLocalName, aNSID, Node::ELEMENT_NODE),
          mStrips(stripSpace)
    {
    }

    MBool matches(Node* aNode, txIMatchContext* aContext) {
        return mNameTest.matches(aNode, aContext);
    }

    MBool stripsSpace() {
        return mStrips;
    }

    double getDefaultPriority() {
        return mNameTest.getDefaultPriority();
    }

protected:
    txNameTest mNameTest;
    MBool mStrips;
};

/**
 * txPSParseContext
 * a txIParseContext forwarding all but resolveNamespacePrefix
 * to a ProcessorState
 */
class txPSParseContext : public txIParseContext {
public:
    txPSParseContext(ProcessorState* aPS, Element* aStyleNode)
        : mPS(aPS), mStyle(aStyleNode)
    {
    }

    ~txPSParseContext()
    {
    }

    nsresult resolveNamespacePrefix(txAtom* aPrefix, PRInt32& aID);
    nsresult resolveFunctionCall(txAtom* aName, PRInt32 aID,
                                 FunctionCall*& aFunction);
    void receiveError(const String& aMsg, nsresult aRes);

protected:
    ProcessorState* mPS;
    Element* mStyle;
};

#endif

