package gov.va.med.cds.ars.persistence;

import java.io.Serializable;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.GregorianCalendar;

import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.XMLGregorianCalendar;

import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.usertype.UserType;

import javax.xml.datatype.DatatypeConstants;

public class XmlDateUserType implements UserType {
	private int[] types = { Types.DATE };

	// DatatypeFactory creates new javax.xml.datatype Objects that map XML
	// to/from Java Objects.
	private static DatatypeFactory datatypeFactory = null;
	private static SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss Z");

	static {
		try {
			datatypeFactory = DatatypeFactory.newInstance();
		} catch (DatatypeConfigurationException e) {
			throw new IllegalStateException("Error while trying to obtain a new instance of DatatypeFactory", e);
		}
	}

	public int[] sqlTypes() {
		return types;
	}

	@SuppressWarnings("rawtypes")
	public Class returnedClass() {
		return XMLGregorianCalendar.class;
	}

	public boolean equals(Object a, Object b) throws HibernateException {
		return (a == b) || ((a != null) && (b != null) && (a.equals(b)));
	}

	public int hashCode(Object o) throws HibernateException {
		return o.hashCode();
	}

	public Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor session, Object owner)
			throws HibernateException, SQLException {
		String sDate = rs.getString(names[0]);
		java.util.Date date = null;

		XMLGregorianCalendar returnDate = null;
		Date databaseDate;
		if (sDate.length() > 10) {
			try {
				date = formatter.parse(sDate);
			} catch (ParseException e) {
				throw new IllegalStateException("XmlDateUserType nullSafeGet sdf.parse failure strDate: " + sDate);
			}
			databaseDate = new Date(date.getTime());
			returnDate = asXMLGregorianCalendar(databaseDate);
		} else {
			databaseDate = rs.getDate(names[0]);
			returnDate = asXMLGregorianCalendar(databaseDate);
			returnDate.setTimezone(DatatypeConstants.FIELD_UNDEFINED);
			returnDate.setTime(DatatypeConstants.FIELD_UNDEFINED, DatatypeConstants.FIELD_UNDEFINED,
					DatatypeConstants.FIELD_UNDEFINED);
		}
		return returnDate;
	}

	public void nullSafeSet(PreparedStatement ps, Object value, int index, SessionImplementor sessionImplementor)
			throws HibernateException, SQLException {
		throw new IllegalStateException("nullSafeSet not implemented for usertype XmlDateUserType.");

		// if ( value != null && value instanceof XMLGregorianCalendar )
		// {
		// XMLGregorianCalendar lastSurveyDate = ( XMLGregorianCalendar )value;
		// Date databaseDate = lastSurveyDate.
		// Hibernate.DATE.nullSafeSet( ps, databaseDate, index );
		// }
		// else
		// {
		// ps.setNull( index, Hibernate.DATE.sqlType() );
		// }
	}

	public Object deepCopy(Object o) throws HibernateException {
		Date returnDate = null;

		// if ( o == null )
		// return null;

		if (o instanceof Date) {
			returnDate = new Date(((Date) o).getTime());
		}
		// History origHistory = ( History )o;
		// History newHistory = new History();

		// newHistory.setInitialCapacity( origHistory.getInitialCapacity() );
		// newHistory.setEstablishmentDate( origHistory.getEstablishmentDate()
		// );
		// return newHistory;
		return returnDate;
	}

	public boolean isMutable() {
		return true;
	}

	public Serializable disassemble(Object value) throws HibernateException {
		return (Serializable) value;
	}

	public Object assemble(Serializable cached, Object owner) throws HibernateException {
		return cached;
	}

	public Object replace(Object original, Object target, Object owner) throws HibernateException {
		return original;
	}

	/**
	 * Converts a java.util.Date into an instance of XMLGregorianCalendar
	 * 
	 * @param date
	 *            database date
	 * @return xml object of date
	 */
	private XMLGregorianCalendar asXMLGregorianCalendar(Date date) {
		if (date == null) {
			return null;
		} else {
			GregorianCalendar gc = new GregorianCalendar();
			gc.setTimeInMillis(date.getTime());
			return datatypeFactory.newXMLGregorianCalendar(gc);
		}
	}

	public static void main(String[] args) {
		XmlDateUserType ut = new XmlDateUserType();
		@SuppressWarnings("deprecation")
		Date d = new Date(110, 5, 10);

		XMLGregorianCalendar dgc = ut.asXMLGregorianCalendar(d);

		System.out.println(dgc);
	}

}
