/*
 *  ====================================================================
 *    Licensed to the Apache Software Foundation (ASF) under one or more
 *    contributor license agreements.  See the NOTICE file distributed with
 *    this work for additional information regarding copyright ownership.
 *    The ASF licenses this file to You under the Apache License, Version 2.0
 *    (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.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 * ====================================================================
 */

package org.apache.poi.ss.formula.functions;

import java.util.Calendar;

import org.apache.poi.ss.formula.eval.BlankEval;
import org.apache.poi.ss.formula.eval.ErrorEval;
import org.apache.poi.ss.formula.eval.EvaluationException;
import org.apache.poi.ss.formula.eval.MissingArgEval;
import org.apache.poi.ss.formula.eval.NumberEval;
import org.apache.poi.ss.formula.eval.OperandResolver;
import org.apache.poi.ss.formula.eval.ValueEval;
import org.apache.poi.ss.usermodel.DateUtil;


/**
 * Implementation for the Excel function WEEKDAY
 */
public final class WeekdayFunc implements Function {
//or:  extends Var1or2ArgFunction {

    public static final Function instance = new WeekdayFunc();

    private WeekdayFunc() {
        // no fields to initialise
    }

    /* for Var1or2ArgFunction:
    @Override
    public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0) {
    }

    @Override
    public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1) {
    }
    */


    /**
     * Perform WEEKDAY(date, returnOption) function.
     * Note: Parameter texts are from German EXCEL-2010 help.
     * Parameters in args[]:
     *  args[0] serialDate
     * EXCEL-date value
     * Standardmaessig ist der 1. Januar 1900 die fortlaufende Zahl 1 und
     * der 1. Januar 2008 die fortlaufende Zahl 39.448, da dieser Tag nach 39.448 Tagen
     * auf den 01.01.1900 folgt.
     * @return Option (optional)
     * Bestimmt den Rueckgabewert:
        1   oder nicht angegeben Zahl 1 (Sonntag) bis 7 (Samstag). Verhaelt sich wie fruehere Microsoft Excel-Versionen.
        2   Zahl 1 (Montag) bis 7 (Sonntag).
        3   Zahl 0 (Montag) bis 6 (Sonntag).
        11  Die Zahlen 1 (Montag) bis 7 (Sonntag)
        12  Die Zahlen 1 (Dienstag) bis 7 (Montag)
        13  Die Zahlen 1 (Mittwoch) bis 7 (Dienstag)
        14  Die Zahlen 1 (Donnerstag) bis 7 (Mittwoch)
        15  Die Zahlen 1 (Freitag) bis 7 (Donnerstag)
        16  Die Zahlen 1 (Samstag) bis 7 (Freitag)
        17  Die Zahlen 1 (Sonntag) bis 7 (Samstag)
     */
    public ValueEval evaluate(ValueEval[] args, int srcRowIndex, int srcColumnIndex) {
        try {
            if (args.length < 1  ||  args.length > 2) {
                return ErrorEval.VALUE_INVALID;
            }

            // extract first parameter
            ValueEval serialDateVE = OperandResolver.getSingleValue(args[0], srcRowIndex, srcColumnIndex);
            double serialDate = OperandResolver.coerceValueToDouble(serialDateVE);
            if (!DateUtil.isValidExcelDate(serialDate)) {
                return ErrorEval.NUM_ERROR;                     // EXCEL uses this and no VALUE_ERROR
            }
            Calendar date = DateUtil.getJavaCalendar(serialDate, false);        // (XXX 1904-windowing not respected)
            int weekday = date.get(Calendar.DAY_OF_WEEK);       // => sunday = 1, monday = 2, ..., saturday = 7

            // extract second parameter
            int returnOption = 1;                   // default value
            if (args.length == 2) {
                ValueEval ve = OperandResolver.getSingleValue(args[1], srcRowIndex, srcColumnIndex);
                if (ve == MissingArgEval.instance  ||  ve == BlankEval.instance) {
                    return ErrorEval.NUM_ERROR;     // EXCEL uses this and no VALUE_ERROR
                }
                returnOption = OperandResolver.coerceValueToInt(ve);
                if (returnOption == 2) {
                    returnOption = 11;              // both mean the same
                }
            } // if

            // perform calculation
            double result;
            if (returnOption == 1) {
                result = weekday;
            // value 2 is handled above (as value 11)
            } else if (returnOption == 3) {
                result = (weekday + 6 - 1) % 7;
            } else if (returnOption >= 11  &&  returnOption <= 17) {
                // rotate in the value range 1 to 7
                result = (weekday + 6 - (returnOption - 10)) % 7 + 1.;
            } else {
                // EXCEL uses this and no VALUE_ERROR
                return ErrorEval.NUM_ERROR;
            }

            return new NumberEval(result);
        } catch (EvaluationException e) {
            return e.getErrorEval();
        }
    } // evaluate()

}
