/* -*- 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.
 *
 */

/**
 * Numbering methods
**/

#include "Numbering.h"
#include "Names.h"
#include "txAtoms.h"

void Numbering::doNumbering(Element* xslNumber, String& dest, Node* context,
                            ProcessorState* ps)
{
    if (!xslNumber)
        return;

    int* counts = 0;
    int nbrOfCounts = 0;

    String valueAttr;
    xslNumber->getAttr(txXSLTAtoms::value, kNameSpaceID_None,
                       valueAttr);
    //-- check for expr
    if (!valueAttr.isEmpty()) {
        Expr* expr = ps->getExpr(xslNumber, ProcessorState::ValueAttr);
        if (!expr)
            return;
        nbrOfCounts = 1;
        counts = new int[1];
        ExprResult* result = expr->evaluate(context, ps);
        double dbl = result->numberValue();
        delete result;
        counts[0] = (int)dbl;
    }
    else if (context) {

        //-- create count expression

        String countAttr;
        xslNumber->getAttr(txXSLTAtoms::count, kNameSpaceID_None,
                           countAttr);

        Pattern* countPattern;
        MBool ownsPattern;
        
        if (!countAttr.isEmpty()) {
            countPattern = ps->getPattern(xslNumber,
                                          ProcessorState::CountAttr);
            ownsPattern = MB_FALSE;
        }
        else {
            // Actually, this code should probobly use NodeTests instead
            switch(context->getNodeType()) {
                case Node::ATTRIBUTE_NODE:
                    countAttr.append('@');
                    countAttr.append(context->getNodeName());
                    break;
                case Node::ELEMENT_NODE:
                    countAttr.append(context->getNodeName());
                    break;
                case Node::CDATA_SECTION_NODE :
                case Node::TEXT_NODE :
                    countAttr.append("text()");
                    break;
                case Node::COMMENT_NODE :
                    countAttr.append("comment()");
                    break;
                case Node::PROCESSING_INSTRUCTION_NODE :
                    countAttr.append("processing-instruction()");
                    break;
                default:
                    countAttr.append("node()[false()]"); //-- for now
                    break;
            }
            ExprParser parser;
            countPattern = parser.createPattern(countAttr);
            ownsPattern = MB_TRUE;
        }
        if (!countPattern) {
            delete counts;
            return;
        }

        NodeSet* nodes = 0;
        int cnum = 0;

        String level;
        xslNumber->getAttr(txXSLTAtoms::level, kNameSpaceID_None,
                           level);
        String fromAttr;
        xslNumber->getAttr(txXSLTAtoms::from, kNameSpaceID_None,
                           fromAttr);
        PatternExpr* from = 0;

        if (MULTIPLE_VALUE.isEqual(level))
            nodes = getAncestorsOrSelf(countPattern,
                                       from,
                                       context,
                                       ps,
                                       MB_FALSE);
        //else if (ANY_VALUE.isEqual(level))
        //    nodes = getAnyPreviousNodes(countExpr, context, ps);
        else
            nodes = getAncestorsOrSelf(countPattern,
                                       from,
                                       context,
                                       ps,
                                       MB_TRUE);

        nbrOfCounts = nodes->size();
        counts = new int[nbrOfCounts];
        cnum = 0;
        for (int i = nodes->size()-1; i >= 0; i--) {
            counts[cnum++] =
                countPreceedingSiblings(countPattern, nodes->get(i), ps);
        }
        delete nodes;
        if (ownsPattern)
            delete countPattern;
    }
    //-- format counts
    for ( int i = 0; i < nbrOfCounts; i++) {
        Double::toString(counts[i], dest);
    }
    delete counts;
} //-- doNumbering

int Numbering::countPreceedingSiblings
    (PatternExpr* patternExpr, Node* context, ProcessorState* ps)
{
    int count = 1;

    if (!context) return 0;

    Node* sibling = context;
    while ((sibling = sibling->getPreviousSibling())) {
        if (patternExpr->matches(sibling, sibling, ps))
            ++count;
    }
    return count;
} //-- countPreceedingSiblings

NodeSet* Numbering::getAncestorsOrSelf
    ( PatternExpr* countExpr,
      PatternExpr* from,
      Node* context,
      ProcessorState* ps,
      MBool findNearest)
{
    NodeSet* nodeSet = new NodeSet();
    Node* parent = context;
    while ((parent)  && (parent->getNodeType() == Node::ELEMENT_NODE))
    {
        if ((from) && from->matches(parent, parent->getParentNode(), ps)) break;

        if (countExpr->matches(parent, parent->getParentNode(), ps)) {
            nodeSet->append(parent);
            if (findNearest) break;
        }
        parent = parent->getParentNode();
    }
    return nodeSet;
} //-- fromAncestorsOrSelf
