package gov.va.med.fw.model.lookup;

// Java Classes
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.util.Date;

import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.commons.lang.builder.ToStringBuilder;

import gov.va.med.fw.model.AbstractEntity;
import gov.va.med.fw.util.InvalidConfigurationException;

/**
 * @author DNS   LEV
 *
 */
public abstract class AbstractLookup extends AbstractEntity implements Lookup {

    private static final long serialVersionUID = 8255942734754635021L;
    
    /**
	 * The unique business description for this object.
	 * 
	 * @hibernate.property not-null="true" insert="false" update="false"
	 *                     column="DESCRIPTION" length="200" unique="true"
	 */
	private String description;

	/**
	 * The unique identifier for this object.
	 * 
	 * @hibernate.id type="java.math.BigDecimal" generator-class="assigned"
	 *               column="IDENTIFIER"
	 */
	private BigDecimal identifier = null;

	/**
	 * The unique business code for this object.
	 * 
	 * @hibernate.property not-null="true" insert="false" update="false"
	 *                     column="CODE" length="50" unique="true"
	 */
	private String code;

	/**
	 * The start date when this object becomes valid.
	 * 
	 * @hibernate.property insert="false" update="false" column="START_DATE"
	 */
	private Date startDate = null;

	/**
	 * The end date when this object becomes invalid.
	 * 
	 * @hibernate.property insert="false" update="false" column="END_DATE"
	 */
	private Date endDate = null;

	/**
	 * A value that describes the relative position of the object when in a
	 * sorted collection.
	 * 
	 * @hibernate.property insert="false" update="false" column="SORT_ORDER"
	 */
	private Integer sortOrder = null;

	/**
	 * Default constructor.
	 */
	protected AbstractLookup() {
		super();
	}

	/**
	 * Returns the unique identifier for this object.
	 * 
	 * @return The unique identifier for this object.
	 */
	public BigDecimal getIdentifier() {
		return this.identifier;
	}

	/**
	 * Sets the unique identifier for this object.
	 * 
	 * @param identifier
	 *           The unique identifier for this object.
	 */
	public void setIdentifier(BigDecimal identifier) {
		this.identifier = identifier;
	}

	/**
	 * Returns the unique business code for this object.
	 * 
	 * @return The unique business code for this object.
	 */
	public String getCode() {
		return this.code;
	}

	public AbstractCode getCodeObject() {
		String className = getClass().getName() + "$Code";
		try {
			Class clazz = Class.forName(className);
			Method m = clazz.getMethod("getByCode", new Class[] { String.class });
			return (AbstractCode) m.invoke(this, new Object[] { code });
		}
		catch (ClassNotFoundException e) {
			throw new InvalidConfigurationException(className
					+ " is not defined in the concrete lookup class");
		}
		catch (NoSuchMethodException e) {
			throw new InvalidConfigurationException(className
					+ " does not define getByCode(String) method");
		}
		catch (Exception e) {
			throw new InvalidConfigurationException(className
					+ " .getByCode(String) method can not be invoked");
		}
	}

	/**
	 * Sets the unique business code for this object.
	 * 
	 * @param code
	 *           The unique business code for this object.
	 */
	public void setCode(String code) {
		this.code = code;
	}

	/**
	 * Returns the unique business description for this object.
	 * 
	 * @return The unique business description for this object.
	 */
	public String getDescription() {
		return this.description;
	}

	/**
	 * Sets the unique business description for this object.
	 * 
	 * @param description
	 *           The unique business description for this object.
	 */
	public void setDescription(String description) {
		this.description = description;
	}

	/**
	 * Returns the start date when this object becomes valid.
	 * 
	 * @return The start date when this object becomes valid.
	 */
	public Date getStartDate() {
		return this.startDate;
	}

	/**
	 * Sets the start date when this object becomes valid.
	 * 
	 * @param startDate
	 *           The start date when this object becomes valid.
	 */
	public void setStartDate(Date startDate) {
		this.startDate = startDate;
	}

	/**
	 * Returns the end date when this object becomes invalid.
	 * 
	 * @return The end date when this object becomes invalid.
	 */
	public Date getEndDate() {
		return this.endDate;
	}

	/**
	 * Sets the end date when this object becomes invalid.
	 * 
	 * @param endDate
	 *           The end date when this object becomes invalid.
	 */
	public void setEndDate(Date endDate) {
		this.endDate = endDate;
	}

	/**
	 * Returns a value that describes the relative position of the object when in
	 * a sorted collection.
	 * 
	 * @return A value that describes the relative position of the object when in
	 *         a sorted collection.
	 */
	public Integer getSortOrder() {
		return this.sortOrder;
	}

	/**
	 * @see gov.va.med.fw.model.AbstractEntity#hashCode()
	 */
	public int hashCode() {
		return new HashCodeBuilder().append(this.identifier).toHashCode();
	}

	/**
	 * @see gov.va.med.fw.model.AbstractEntity#equals(java.lang.Object)
	 */
	public boolean equals(Object o) {
	   return ((o instanceof AbstractLookup) && this.equals( (AbstractLookup) o ));
	}


	/**
	 * @see gov.va.med.fw.model.AbstractEntity#buildToString(org.apache.commons.lang.builder.ToStringBuilder)
	 */
	protected void buildToString(ToStringBuilder builder) {
		builder.append("identifier", this.identifier).append("code", this.code)
				.append("startDate", this.startDate)
				.append("endDate", this.endDate)
				.append("sortOrder", this.sortOrder).append("description",
						this.description);
	}

	/**
	 * @see java.lang.Object#finalize()
	 */
	protected void finalize() throws Throwable {
		super.finalize();

		this.setIdentifier( null );
		this.setCode( null );
		this.setStartDate( null );
		this.setEndDate( null );
		this.setSortOrder( null );
		this.setDescription( null );
	}
	
	/**
	 * @param o
	 * @return
	 */
	private boolean equals(AbstractLookup o) {
	   return new EqualsBuilder().append( this.getClass(), o.getClass() )
	          					  .append( this.identifier, o.identifier )
	           					  .append( this.code, o.code )
	           					  .isEquals();
	}
	
	/**
	 * Sets a value that describes the relative position of the object when in a
	 * sorted collection.
	 * 
	 * @param sortOrder
	 *           A value that describes the relative position of the object when
	 *           in a sorted collection.
	 */
	public void setSortOrder(Integer sortOrder) {
		this.sortOrder = sortOrder;
	}
}