/***
 * 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.vehicle;

import javax.persistence.Column;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.DiscriminatorType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Version;

import org.hibernate.annotations.Any;
import org.hibernate.annotations.AnyMetaDef;
import org.hibernate.annotations.MetaValue;
import org.hibernate.annotations.Type;
import org.hibernate.annotations.TypeDef;
import org.hibernate.annotations.TypeDefs;
import org.nakedobjects.applib.AbstractDomainObject;
import org.nakedobjects.applib.annotation.Disabled;
import org.nakedobjects.applib.annotation.Hidden;
import org.nakedobjects.applib.annotation.MemberOrder;
import org.nakedobjects.applib.annotation.Optional;
import org.nakedobjects.applib.annotation.When;
import org.nakedobjects.applib.util.TitleBuffer;

import com.pragprog.dhnako.carserv.dom.customer.Customer;


@NamedQueries({
	@NamedQuery(
			name="findVehicleByRegistrationNumber", 
			query="from Vehicle where registrationNumber=:registrationNumber")
})
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="vehicle_type", discriminatorType=DiscriminatorType.STRING, length=3)
@Entity
@TypeDefs({
	@TypeDef(name="regnum", typeClass=RegistrationNumberType.class)
	})
public abstract class Vehicle extends AbstractDomainObject {

	// {{ Identification
	public String title() {
		final TitleBuffer buf = new TitleBuffer();
		buf.append(getRegistrationNumber());
		return buf.toString();
	}
	// }}


    // {{ Id
    private Long id;
    @Hidden
	@Optional
    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
	public Long getId() {
		return id;
	}
	public void setId(Long id) {
		this.id = id;
	}
    // }}
    

    // {{ Version
    private Long version;
    @Hidden
    @Optional
    @Version
	public Long getVersion() {
		return version;
	}
	public void setVersion(Long version) {
		this.version = version;
	}
    // }}
    


	// {{ RegistrationNumber
	private RegistrationNumber registrationNumber;
	@MemberOrder(sequence = "1.1")
	@Disabled(When.ONCE_PERSISTED)
    @Type(type="regnum")
	public RegistrationNumber getRegistrationNumber() {
		return registrationNumber;
	}

	public void setRegistrationNumber(final RegistrationNumber registrationNumber) {
		this.registrationNumber = registrationNumber;
	}
	// }}


	// {{ Owner
	private VehicleOwner owner;

	@MemberOrder(sequence = "1.2")
	@Disabled(When.ONCE_PERSISTED)
    @Any(metaColumn = @Column(name="owner_type", length=3), fetch=FetchType.LAZY)
    @AnyMetaDef(idType="long", metaType="string", 
            metaValues={
                @MetaValue(targetEntity=Customer.class, value="CUS")
            })
    @JoinColumn(name="owner_id")
	public VehicleOwner getOwner() {
		return owner;
	}

	public void setOwner(final VehicleOwner owner) {
		this.owner = owner;
	}

	public void modifyOwner(final VehicleOwner owner) {
		VehicleOwner currentOwner = getOwner();
		// check for no-op
		if (owner == null || owner.equals(currentOwner) ) {
			return;
		}
		// delegate to parent to associate
		owner.addToVehicles(this);
		// additional business logic
		onModifyOwner(currentOwner, owner);
	}

	public void clearOwner() {
		VehicleOwner currentOwner = getOwner();
		// check for no-op
		if (currentOwner == null) {
			return;
		}
		// delegate to parent to dissociate
		currentOwner.removeFromVehicles(this);
		// additional business logic
		onClearOwner(currentOwner);
	}

	protected void onModifyOwner(final VehicleOwner oldOwner, final VehicleOwner newOwner) {
	}

	protected void onClearOwner(final VehicleOwner oldOwner) {
	}

	public String validateOwner(final VehicleOwner owner) {
		if (owner != null) {
			return owner.validateAddToVehicles(this);
		} else {
			// clearing association
			if (getOwner() == null) {
				return null;
			}
			return getOwner().validateRemoveFromVehicles(this);
		}
	}
	// }}


	
	// {{ Model
	private Model model;
	@MemberOrder(sequence = "1.3")
	@Disabled(When.ONCE_PERSISTED)
	@ManyToOne(fetch=FetchType.EAGER)
	public Model getModel() {
		return model;
	}

	public void setModel(final Model model) {
		this.model = model;
	}
	// }}


	// {{ delete
	@MemberOrder(sequence = "1.2")
	@Disabled(When.UNTIL_PERSISTED)
	public void delete() {
		clearOwner();
		getContainer().remove(this);
	}
	// }}

	
	// {{ lifecycle methods
	public void persisting() {
		getOwner().addToVehicles(this);
	}
	// }}

}