/**
 * Copyright (C) 2016 Premium Minds.
 *
 * This file is part of pm-wicket-utils.
 *
 * pm-wicket-utils is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option) any
 * later version.
 *
 * pm-wicket-utils is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with pm-wicket-utils. If not, see <http://www.gnu.org/licenses/>.
 */
package com.premiumminds.webapp.wicket;

import java.lang.reflect.Method;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.temporal.ChronoField;
import java.time.temporal.Temporal;
import java.util.Locale;

import org.apache.wicket.markup.html.form.AbstractTextComponent.ITextFormatProvider;
import org.apache.wicket.markup.html.form.TextField;
import org.apache.wicket.model.IModel;
import org.apache.wicket.util.convert.ConversionException;
import org.apache.wicket.util.convert.IConverter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class TemporalTextField<T extends Temporal> extends TextField<T> implements ITextFormatProvider {
	/** Log for reporting. */
	private static final Logger log = LoggerFactory.getLogger(TemporalTextField.class);
	private static final long serialVersionUID = 1L;
	private String pattern;
	private IConverter<T> converter;

	private static final String DEFAULT_PATTERN = "dd/MM/yyyy";

	public TemporalTextField(String id, Class<T> type) {
		this(id, null, defaultDatePattern(), type);
	}

	public TemporalTextField(String id, IModel<T> model, Class<T> type) {
		this(id, model, defaultDatePattern(), type);
	}

	public TemporalTextField(String id, IModel<T> model,
			String pattern, final Class<T> type) {
		super(id, model, type);

		this.pattern = pattern;

		converter = new IConverter<T>() {
			private static final long serialVersionUID = 1L;

			public String convertToString(Temporal value, Locale locale) {
				if(null == locale) {
					return DateTimeFormatter.ofPattern(TemporalTextField.this.pattern).format(value);
				} else {
					return DateTimeFormatter.ofPattern(TemporalTextField.this.pattern).withLocale(locale).format(value);
				}
			}

			@SuppressWarnings("unchecked")
			public T convertToObject(String value, Locale locale) throws ConversionException {
				DateTimeFormatter formatter =
			            new DateTimeFormatterBuilder().appendPattern(TemporalTextField.this.pattern)
			            .parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
			            .parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0)
			            .parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0)
			            .parseDefaulting(ChronoField.DAY_OF_MONTH, 1)
			            .parseDefaulting(ChronoField.MONTH_OF_YEAR, 1)
			            .parseDefaulting(ChronoField.YEAR_OF_ERA, ZonedDateTime.now().getYear())
			            .toFormatter();
				if (null != locale) {
					formatter = formatter.withLocale(locale);
				}

				try {
					Method parser = type.getMethod("parse", CharSequence.class, DateTimeFormatter.class);
					return (T)parser.invoke(null, value, formatter);
				} catch (NoSuchMethodException | SecurityException e1) {
					log.error("Could not find parser for type " + type.getSimpleName());
				} catch(Exception e) {
					log.error("Could not convert [" + value +"] to a valid " + type.getSimpleName());
				}

				return null;
			}
		};
	}

	/**
	 * Returns the default converter if created without pattern; otherwise it returns a
	 * pattern-specific converter.
	 * 
	 * @param type
	 *            The type for which the convertor should work
	 * 
	 * @return A pattern-specific converter
	 * 
	 * @see org.apache.wicket.markup.html.form.TextField
	 */
	@SuppressWarnings("unchecked")
	@Override
	public <C> IConverter<C> getConverter(final Class<C> type)
	{
		if (Temporal.class.isAssignableFrom(type))
		{
			return (IConverter<C>)converter;
		}
		else
		{
			return super.getConverter(type);
		}
	}

	/**
	 * Returns the date pattern.
	 * 
	 * @see org.apache.wicket.markup.html.form.AbstractTextComponent.ITextFormatProvider#getTextFormat()
	 */
	@Override
	public String getTextFormat()
	{
		return pattern;
	}

	/**
	 * Try to get datePattern from user session locale. If it is not possible, it will return
	 * {@link #DEFAULT_PATTERN}
	 * 
	 * @return date pattern
	 */
	private static String defaultDatePattern()
	{
		return DEFAULT_PATTERN;
	}
}