package org.apache.slide.projector.processor.core;

import java.text.DateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.Map;

import org.apache.commons.contract.Context;
import org.apache.commons.contract.Processor;
import org.apache.commons.contract.Result;
import org.apache.commons.contract.constraints.DateConstraints;
import org.apache.commons.contract.constraints.LocaleConstraints;
import org.apache.commons.contract.constraints.StringConstraints;
import org.apache.commons.contract.descriptor.ParameterDescriptor;
import org.apache.commons.contract.descriptor.RequiredEnvironmentDescriptor;
import org.apache.commons.contract.descriptor.ResultDescriptor;
import org.apache.commons.contract.descriptor.ResultEntryDescriptor;
import org.apache.commons.contract.descriptor.StateDescriptor;
import org.apache.commons.contract.i18n.ParameterMessage;
import org.apache.commons.i18n.LocalizedMessage;
import org.apache.slide.projector.Projector;
import org.apache.slide.projector.processor.SimpleProcessor;

public class DateFormatter implements Processor {
	private final static String DATE_FORMAT = "dateFormat";
	private final static String TIME_FORMAT = "timeFormat";
    private final static String DATE = "date";
    private final static String LOCALE = "locale";
    private final static String HIDE = "hide";
    private final static String SHORT = "short";
    private final static String MEDIUM = "medium";
    private final static String LONG = "long";
    private final static String FULL = "full";
    private final static String[] STYLES = new String[] { HIDE, SHORT, MEDIUM, LONG, FULL }; 
    
    private final static ParameterDescriptor[] parameterDescriptors = new ParameterDescriptor[]{
    		new ParameterDescriptor(DATE, new ParameterMessage("dateFormatter/parameter/date"), new DateConstraints("yyyy-MM-dd'T'HH:mm:ss'Z'")),
    		new ParameterDescriptor(DATE_FORMAT, new ParameterMessage("dateFormatter/parameter/dateFormat"), new StringConstraints(STYLES), SHORT),
    		new ParameterDescriptor(TIME_FORMAT, new ParameterMessage("dateFormatter/parameter/timeFormat"), new StringConstraints(STYLES), SHORT),
    };

    private final static ResultDescriptor[] resultDescriptors = new ResultDescriptor[] { 
            new ResultDescriptor(
                    StateDescriptor.OK_DESCRIPTOR,
                    new ResultEntryDescriptor[]{new ResultEntryDescriptor(
                            SimpleProcessor.OUTPUT, new LocalizedMessage("dateFormatter/result/output"),
                            StringConstraints.UNCONSTRAINED)})
    };

    private final static RequiredEnvironmentDescriptor[] requiredEnvironmentDescriptors = new RequiredEnvironmentDescriptor[] {
    		new RequiredEnvironmentDescriptor(LOCALE, Projector.SESSION_STORE, new ParameterMessage("message/requiredEnvironment/locale"), new LocaleConstraints(), Locale.getDefault())
    };

	public Result process(Map parameters, Context context) throws Exception {
		Date date = (Date)parameters.get(DATE); 
		String dateFormat = parameters.get(DATE_FORMAT).toString();
		String timeFormat = parameters.get(TIME_FORMAT).toString();
		Locale locale = ((Locale)context.getStore(Projector.SESSION_STORE).get(LOCALE, context));
		String formattedDate = getDateFormatter(dateFormat, timeFormat, locale).format(date);
		return new Result(StateDescriptor.OK, SimpleProcessor.OUTPUT, new String(formattedDate));
	}

	public ParameterDescriptor[] getParameterDescriptors() {
		return parameterDescriptors; 
	}

	public ResultDescriptor[] getResultDescriptors() {
		return resultDescriptors;
	}
	
	private DateFormat getDateFormatter(String dateFormat, String timeFormat, Locale locale) {
		if ( dateFormat.equals(HIDE) ) {
			return DateFormat.getTimeInstance(getStyleByName(timeFormat), locale);
		} else if ( timeFormat.equals(HIDE) ) {
			return DateFormat.getDateInstance(getStyleByName(dateFormat), locale);
		} else {
			return DateFormat.getDateTimeInstance(getStyleByName(dateFormat), getStyleByName(timeFormat), locale);
		}
	}
	
	private int getStyleByName(String styleName) {
		if ( styleName.equals(SHORT) ) {
			return DateFormat.SHORT;
		} else if ( styleName.equals(MEDIUM) ) {
			return DateFormat.MEDIUM;
		} else if ( styleName.equals(LONG) ) {
			return DateFormat.LONG;
		}
		return DateFormat.FULL;
	}
}