/*
 * 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):
 *
 * Olivier Gerardin, ogerardin@vo.lu
 *   -- original author.
 *
 * Nisheeth Ranjan, nisheeth@netscape.com
 *   -- implemented rint function, which was not available on Windows.
 *
 * $Id: NumberFunctionCall.cpp,v 1.12 2001/04/08 14:33:21 peterv%netscape.com Exp $
 */

/*
  NumberFunctionCall
  A representation of the XPath Number funtions
*/

#include "FunctionLib.h"
#include "XMLUtils.h"
#include "XMLDOMUtils.h"
#include <math.h>

/**
 * Creates a default NumberFunctionCall. The number() function
 * is the default
**/
NumberFunctionCall::NumberFunctionCall() : FunctionCall(XPathNames::NUMBER_FN) {
    type = NUMBER;
} //-- NumberFunctionCall

/**
 * Creates a NumberFunctionCall of the given type
**/
NumberFunctionCall::NumberFunctionCall(short type) : FunctionCall() {
    this->type = type;
    switch ( type ) {
    case ROUND :
        FunctionCall::setName(XPathNames::ROUND_FN);
        break;
    case CEILING :
        FunctionCall::setName(XPathNames::CEILING_FN);
        break;
    case FLOOR :
        FunctionCall::setName(XPathNames::FLOOR_FN);
        break;
    case SUM :
        FunctionCall::setName(XPathNames::SUM_FN);
        break;
    case NUMBER :
    default :
        FunctionCall::setName(XPathNames::NUMBER_FN);
        break;
    }
} //-- NumberFunctionCall

#if !defined(HAVE_RINT)
static double rint(double r)
{
  double integerPart = 0;
  double fraction = 0;
  fraction = modf(r, &integerPart);
  if (fraction >= 0.5)
      integerPart++;

  return integerPart;
}
#endif

/**
 * Evaluates this Expr based on the given context node and processor state
 * @param context the context node for evaluation of this Expr
 * @param ps the ContextState containing the stack information needed
 * for evaluation
 * @return the result of the evaluation
**/
ExprResult* NumberFunctionCall::evaluate(Node* context, ContextState* cs) {
    NumberResult* result = 0;
    ListIterator* iter = params.iterator();
    int argc = params.getLength();
    Expr* param = 0;
    String err;

    switch ( type ) {
    case CEILING :
        if ( requireParams(1, 1, cs) ) {
            double dbl = evaluateToNumber((Expr*)iter->next(), context, cs);
            result = new NumberResult(ceil(dbl));
        }
        else {
            result = new NumberResult(0.0);
        }
        break;

    case FLOOR :
        if ( requireParams(1, 1, cs) ) {
            double dbl = evaluateToNumber((Expr*)iter->next(), context, cs);
            result = new NumberResult(floor(dbl));
        }
        else {
            result = new NumberResult(0.0);
        }
        break;
      
    case ROUND :
        if ( requireParams(1, 1, cs) ) {
            double dbl = evaluateToNumber((Expr*)iter->next(), context, cs);
            double res = rint(dbl);
            if ((dbl>0.0) && (res == dbl-0.5)) {
                // fix for native round function from math library (rint()) which does not
                // match the XPath spec for positive half values
                result = new NumberResult(res+1.0);
            }
            else
                result = new NumberResult(res);
            break;
        }
        else result = new NumberResult(0.0);
        break;
      
    case SUM :
        double numResult;
        numResult = 0 ;
        if ( requireParams(1, 1, cs) ) {
            param = (Expr*)iter->next();
            ExprResult* exprResult = param->evaluate(context, cs);
            if ( exprResult->getResultType() == ExprResult::NODESET ) {
                NodeSet *lNList = (NodeSet *)exprResult;
                NodeSet tmp;
                for (int i=0; i<lNList->size(); i++){
                    tmp.add(0,lNList->get(i));
                    numResult += tmp.numberValue();
                };
            };
            delete exprResult;
            exprResult=0;
        };
        result = new NumberResult(numResult);
        break;
      
    case NUMBER :
    default : //-- number( object? )
        if ( requireParams(0, 1, cs) ) {
            if (iter->hasNext()) {
                param = (Expr*) iter->next();
                ExprResult* exprResult = param->evaluate(context, cs);
                result = new NumberResult(exprResult->numberValue());
                delete exprResult;
            }
            else {
                String resultStr;
                XMLDOMUtils::getNodeValue(context, &resultStr);
                if ( cs->isStripSpaceAllowed(context) &&
                     XMLUtils::shouldStripTextnode(resultStr)) {
                    result = new NumberResult(Double::NaN);
                }
                else {
                    Double dbl(resultStr);
                    result = new NumberResult(dbl.doubleValue());
                }
            }
        }
        else {
            result = new NumberResult(Double::NaN);
        }
        break;
    }
    delete iter;
    return result;
} //-- evaluate

