/* DateTimeFunctionImpl.java

	Purpose:
		
	Description:
		
	History:
		Oct 25, 2010 4:23:06 PM, Created by ashish

Copyright (C) 2010 Potix Corporation. All Rights Reserved.

*/
package org.zkoss.zssex.formula.fn;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;

import org.zkoss.poi.ss.formula.eval.ErrorEval;
import org.zkoss.poi.ss.formula.eval.EvaluationException;
import org.zkoss.poi.ss.formula.eval.NumberEval;
import org.zkoss.poi.ss.formula.eval.OperandResolver;
import org.zkoss.poi.ss.formula.eval.ValueEval;
import org.zkoss.poi.ss.formula.functions.Function;
import org.zkoss.poi.ss.formula.functions.NumericFunction;
import org.zkoss.poi.ss.formula.functions.TextFunction;
import org.zkoss.poi.ss.formula.functions.TextFunctionHelper;
import org.zkoss.poi.ss.formula.functions.UtilFns;
import org.zkoss.poi.ss.usermodel.DateUtil;
import org.zkoss.poi.ss.usermodel.ZssContext;
import org.zkoss.xel.fn.CommonFns;
import org.zkoss.zss.model.impl.sys.DateInputMask;


/**
 * @author ashish
 *
 */
public class DateTimeFunctionImpl {
	
	
	public static final Function DATEVALUE = new TextFunction() {

		@Override
		protected ValueEval evaluateFunc(ValueEval[] args, int srcCellRow,
				int srcCellCol) throws EvaluationException {
			String txtDate = TextFunctionHelper.evaluateStringArg(args[0], srcCellRow, srcCellCol);
			Object[] objs = FunctionHelper.parseToDate(txtDate);
			return new NumberEval(DateUtil.getExcelDate((Date)objs[0], false));
		}
	};
	
	public static final Function HOUR = new NumericFunction() {

		@Override
		protected double eval(ValueEval[] args, int srcCellRow, int srcCellCol)
				throws EvaluationException {
			
			String txt = TextFunctionHelper.evaluateStringArg(args[0], srcCellRow, srcCellCol);
			Object[] result = FunctionHelper.editTextToValue(txt, (String)null);
			
			double time;
			// parsed result may be the double value or date value
			if(result[1] instanceof Date) {
				time = DateUtil.getExcelDate((Date)result[1]);
			} else {
				time = (Double) result[1];
			}
			return argToCalendar(DateUtil.getJavaDate(time, false)).get(Calendar.HOUR_OF_DAY);
		}
	};
	
	public static final Function MINUTE = new NumericFunction() {

		@Override
		protected double eval(ValueEval[] args, int srcCellRow, int srcCellCol)
				throws EvaluationException {

			String txt = TextFunctionHelper.evaluateStringArg(args[0], srcCellRow, srcCellCol);
			Object[] result = FunctionHelper.editTextToValue(txt, (String)null);
			double time;
			if(result[1] instanceof Date) {
				time = DateUtil.getExcelDate((Date)result[1]);	
			} else {
				time = (Double) result[1];
			}
			return argToCalendar(DateUtil.getJavaDate(time, false)).get(Calendar.MINUTE);
		}
	};
	public static final Function SECOND = new NumericFunction() {

		@Override
		protected double eval(ValueEval[] args, int srcCellRow, int srcCellCol)
				throws EvaluationException {

			String txt = TextFunctionHelper.evaluateStringArg(args[0], srcCellRow, srcCellCol);
			Object[] result = FunctionHelper.editTextToValue(txt, (String)null);
			double time;
			if(result[1] instanceof Date) {
				time = DateUtil.getExcelDate((Date)result[1]);	
			} else {
				time = (Double) result[1];
			}
			return argToCalendar(DateUtil.getJavaDate(time, false)).get(Calendar.SECOND);
		}
	};

	public static final Function WEEKDAY = new NumericFunction() {

		@Override
		protected double eval(ValueEval[] args, int srcCellRow, int srcCellCol)
				throws EvaluationException {
			double time = NumericFunction.singleOperandEvaluate(args[0], srcCellRow, srcCellCol);
			Calendar cal = argToCalendar(DateUtil.getJavaDate(time, false));
			int type = 1;
			if(args.length == 2) {
				type = (int) NumericFunction.singleOperandEvaluate(args[1], srcCellRow, srcCellCol);
			}
			switch(type){
			case 1:
				return new Integer(cal.get(Calendar.DAY_OF_WEEK));
			case 2:
				return new Integer(weekdayFromMonday(cal.get(Calendar.DAY_OF_WEEK)));
			case 3:
				return new Integer(weekdayFromMonday(cal.get(Calendar.DAY_OF_WEEK))-1);
			default:
				throw new EvaluationException(ErrorEval.NUM_ERROR);
			}

		}
	};
	private static int weekdayFromMonday(int day){
		if(day == 1)
			return 7;
		else
			return (day-1);
	}

	/**
	 * Converts a serial number to a year
	 * @param arg input data
	 * @return a year
	 */
	public static Calendar argToCalendar(Object arg){
		Calendar calendar = Calendar.getInstance();
		if(arg instanceof Date){
			calendar.setTime((Date)arg);
//			Set TimeZone from default to GMT due to EXCEL property
			//calendar.setTimeZone(UtilFns.getTZ_GMT());
			return calendar;
		}
		else{
			calendar.setTime(UtilFns.daysToDate(CommonFns.toNumber(arg).intValue()));
			return calendar;
			//throw new SSErrorXelException(SSError.VALUE);
		}
	}
	
	//ZSS-1014
	public static final Function EOMONTH = new NumericFunction() {

		@Override
		protected double eval(ValueEval[] args, int srcCellRow, int srcCellCol)
				throws EvaluationException {
			double time = NumericFunction.singleOperandEvaluate(args[0], srcCellRow, srcCellCol);
			int moffset = (int) NumericFunction.singleOperandEvaluate(args[1], srcCellRow, srcCellCol);

			Calendar cal = argToCalendar(DateUtil.getJavaDate(time, false));
			int year = cal.get(Calendar.YEAR);
			int month = cal.get(Calendar.MONTH); //0 based
			
			int month0 = month + moffset;
			if (month0 >= 0) {
				year += month0 / 12;
				month = month0 % 12;
			} else {
				final int yearOff = -(month0 + 1) / 12 + 1; 
				year -= yearOff;
				month = (month0 + yearOff * 12) % 12;
			}
			
			Calendar newcal = Calendar.getInstance();
			newcal.set(Calendar.YEAR, year);
			newcal.set(Calendar.MONTH, month);
			newcal.set(Calendar.DAY_OF_MONTH, 1);
			int lastday = newcal.getActualMaximum(Calendar.DAY_OF_MONTH);
			newcal.set(Calendar.DAY_OF_MONTH, lastday);
			newcal.set(Calendar.HOUR_OF_DAY, 0);
			newcal.set(Calendar.MINUTE, 0);
			newcal.set(Calendar.SECOND, 0);
			newcal.set(Calendar.MILLISECOND, 0);
			
			return DateUtil.getExcelDate(newcal, false);
		}
	};
}
