/***
 * 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 java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Calendar;

import org.hibernate.Hibernate;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.type.Type;
import org.starobjects.jpa.applib.usertypes.ImmutableCompositeType;

import com.domainlanguage.time.CalendarDate;
import com.domainlanguage.time.CalendarInterval;
import com.domainlanguage.time.Duration;


public class CalendarIntervalType extends ImmutableCompositeType {

    public Class<CalendarInterval> returnedClass() {
        return CalendarInterval.class;
    }

    public Object nullSafeGet(
            final ResultSet resultSet,
            final String[] names,
            final SessionImplementor session,
            final Object owner) throws SQLException {
        final Date startDate = resultSet.getDate(names[0]);
        if (resultSet.wasNull()) {
            return null;
        }
        final int interval = resultSet.getInt(names[1]);
        CalendarDate start = asDate(startDate);
		Duration length = Duration.days(interval);
		return CalendarInterval.startingFrom(start, length);
    }

    public void nullSafeSet(
            final PreparedStatement statement,
            final Object value,
            final int index,
            final SessionImplementor session) throws SQLException {
        if (value == null) {
            statement.setNull(index, Hibernate.DATE.sqlType());
            statement.setNull(index + 1, Hibernate.INTEGER.sqlType());
        } else {
            final CalendarInterval calendarInterval = (CalendarInterval) value;
            statement.setDate(index, startDateOf(calendarInterval));
            statement.setInt(index + 1, numDaysOf(calendarInterval));
        }
    }

	public String[] getPropertyNames() {
        return new String[] { "ci_date", "ci_num_days" };
    }

    public Type[] getPropertyTypes() {
        return new Type[] { Hibernate.DATE, Hibernate.INTEGER };
    }

    public Object getPropertyValue(final Object component, final int propertyIndex) {
        final CalendarInterval calendarInterval = (CalendarInterval)component;
        if (propertyIndex == 0) {
            return startDateOf(calendarInterval);
        } else {
            return numDaysOf(calendarInterval);
        }
    }

    public void setPropertyValue(final Object component, final int property, final Object value) {
        throw new UnsupportedOperationException("CalendarInterval is immutable");
    }


    ////////////////////////////////////////////////////////////////////
    // Helpers
    ////////////////////////////////////////////////////////////////////
    
	private Date startDateOf(CalendarInterval calendarInterval) {
		return asDate(calendarInterval.start());
	}

	private CalendarDate asDate(final Date sqlDate) {
		Calendar cal = Calendar.getInstance();
        cal.clear();
        cal.setTime(sqlDate);
        return CalendarDate.from(
        		cal.get(Calendar.YEAR), cal.get(Calendar.MONTH)+1, cal.get(Calendar.DAY_OF_MONTH));
	}

	private Date asDate(CalendarDate calDate) {
		int day = calDate.breachEncapsulationOf_day();
		int month = calDate.breachEncapsulationOf_month();
		int year = calDate.breachEncapsulationOf_year();

		Calendar cal = Calendar.getInstance();
		cal.clear();
		cal.set(Calendar.YEAR, year);
		cal.set(Calendar.MONTH, month-1);
		cal.set(Calendar.DAY_OF_MONTH, day);
		Date date = new Date(cal.getTime().getTime());
		return date;
	}

	private int numDaysOf(CalendarInterval calendarInterval) {
		return calendarInterval.lengthInDaysInt();
	}


}
// Copyright (c) Naked Objects Group Ltd.
