/***
 * Excerpted from "Domain-Driven Design Using Naked Objects",
 * published by The Pragmatic Bookshelf.
 * Copyrights apply to this code. It may not be used to create training material, 
 * courses, books, articles, and the like. Contact us if you are in doubt.
 * We make no guarantees that this code is fit for any purpose. 
 * Visit http://www.pragmaticprogrammer.com/titles/dhnako for more book information.
***/
package com.pragprog.dhnako.carserv.dom.service;

import org.nakedobjects.applib.adapters.AbstractValueSemanticsProvider;
import org.nakedobjects.applib.adapters.DefaultsProvider;
import org.nakedobjects.applib.adapters.Parser;

import com.domainlanguage.time.CalendarDate;
import com.domainlanguage.time.CalendarInterval;
import com.domainlanguage.time.Duration;
import com.pragprog.dhnako.carserv.dom.domainservices.CalendarService;

public class CalendarIntervalValueSemanticsProvider extends
		AbstractValueSemanticsProvider<CalendarInterval> {

	private static final String PARSEABLE_PATTERN = "yyyyMMdd";
	private static final String TITLE_PATTERN = "MMM dd, yyyy";

	@Override
	public Parser<CalendarInterval> getParser() {
		return new Parser<CalendarInterval>() {
			public CalendarInterval parseTextEntry(
					final CalendarInterval context, final String entry) {

				// try to interpret "+1", "+2" etc
				if (context != null && entry.startsWith("+")) {
					int extendDurationBy = parseDays(entry.substring(1));
					if (extendDurationBy != 0) {
						return CalendarInterval.inclusive(
							    context.start(),
								context.end().plusDays(extendDurationBy));
					}
				}

				String[] split = entry.split(":");
				String dateEntry = split[0];
				String durationEntry = split.length > 1 ? split[1] : null;
				CalendarDate start = parseDate(dateEntry, PARSEABLE_PATTERN);
				int howMany = parseDays(durationEntry);
				return start != null? 
						CalendarInterval.startingFrom(start, Duration.days(howMany+1)):
						null;
			}
    	    private CalendarDate parseDate(
    	            final String dateEntry, final String candidatePattern) {
				try {
					return CalendarDate.from(dateEntry, candidatePattern);
    	        } catch(NullPointerException ex) {
					return null;
    	        } catch(IllegalArgumentException ex) {
					return null;
				}
			}
			private int parseDays(final String entry) {
				if (entry == null) {
					return 0;
				}
				try {
					return Integer.parseInt(entry);
    	        } catch(NumberFormatException ex) {
					return 0;
				}
			}
			public String displayTitleOf(CalendarInterval object) {
                return String.format("%s ~ %s", 
                    object.start().toString(TITLE_PATTERN), 
                    object.end().toString(TITLE_PATTERN));
			}

			public String displayTitleOf(CalendarInterval object, String usingMask) {
				return displayTitleOf(object);
			}

			public String parseableTitleOf(CalendarInterval object) {
                return String.format("%s:%d", 
                    object.start().toString(PARSEABLE_PATTERN), 
                    object.lengthInDaysInt());
			}

			public int typicalLength() {
				return 30;
			}
		};
	}

	public static final class CalendarIntervalDefaultsProvider implements
			DefaultsProvider<CalendarInterval> {
		public CalendarInterval getDefaultValue() {
			return CalendarInterval.startingFrom(
					calendarService.today().plusDays(1),
					Duration.days(1));
		}

		// {{ injected: CalendarService
		private CalendarService calendarService;

		public void setCalendarService(final CalendarService calendarService) {
			this.calendarService = calendarService;
		}
		// }}
	}

	@Override
	public DefaultsProvider<CalendarInterval> getDefaultsProvider() {
		return new CalendarIntervalDefaultsProvider();
	}
}
