/***
 * 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 java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.TreeSet;

import org.nakedobjects.applib.AbstractDomainObject;
import org.nakedobjects.applib.annotation.Disabled;
import org.nakedobjects.applib.annotation.MemberOrder;
import org.nakedobjects.applib.annotation.When;
import org.nakedobjects.applib.util.TitleBuffer;


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

public abstract class Vehicle extends AbstractDomainObject {

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

	// {{ RegistrationNumber
	private RegistrationNumber registrationNumber;

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

	public void setRegistrationNumber(final RegistrationNumber registrationNumber) {
		this.registrationNumber = registrationNumber;
	}
	// }}
	
	
	// {{ OwningCustomer
	private Customer owningCustomer;

	@MemberOrder(sequence = "1.2")
	@Disabled(When.ONCE_PERSISTED)
	public Customer getOwningCustomer() {
		return owningCustomer;
	}

	public void setOwningCustomer(final Customer owningCustomer) {
		this.owningCustomer = owningCustomer;
	}

	public void modifyOwningCustomer(final Customer owningCustomer) {
		Customer currentOwningCustomer = getOwningCustomer();
		// check for no-op
		if (owningCustomer == null || owningCustomer.equals(currentOwningCustomer) ) {
			return;
		}
		// delegate to parent to associate
		owningCustomer.addToVehicles(this);
		// additional business logic
		onModifyOwningCustomer(currentOwningCustomer, owningCustomer);
	}

	public void clearOwningCustomer() {
		Customer currentOwningCustomer = getOwningCustomer();
		// check for no-op
		if (currentOwningCustomer == null) {
			return;
		}
		// delegate to parent to dissociate
		currentOwningCustomer.removeFromVehicles(this);
		// additional business logic
		onClearOwningCustomer(currentOwningCustomer);
	}

	protected void onModifyOwningCustomer(final Customer oldOwningCustomer, final Customer newOwningCustomer) {
	}

	protected void onClearOwningCustomer(final Customer oldOwningCustomer) {
	}
	
	public String validateOwningCustomer(final Customer owningCustomer) {
		if (owningCustomer != null) {
			return owningCustomer.validateAddToVehicles(this);
		} else {
			// clearing association
			if (getOwningCustomer() == null) {
				return null;
			}
			return getOwningCustomer().validateRemoveFromVehicles(this);
		}
	}
	// }}


	// {{ Services
	private List<Service> services = new ArrayList<Service>();

	@MemberOrder(sequence = "1.4")
	@Disabled
	public List<Service> getServices() {
		return services;
	}

	public void setServices(final List<Service> services) {
		this.services = services;
	}

	public void addToServices(final Service service) {
		// check for no-op
		if (service == null
				|| getServices().contains(service)) {
			return;
		}
		// dissociate arg from its current parent (if any).
		service.clearVehicle();
		// associate arg
		service.setVehicle(this);
		getServices().add(service);
		// additional business logic
		onAddToServices(service);
	}

	public void removeFromServices(
			final Service service) {
		// check for no-op
		if (service == null
				|| !getServices().contains(service)) {
			return;
		}
		// dissociate arg
		service.setVehicle(null);
		getServices().remove(service);
		// additional business logic
		onRemoveFromServices(service);
	}

	protected void onAddToServices(final Service service) {
	}

	protected void onRemoveFromServices(final Service service) {
	}
	// }}


	// {{ Model
	private Model model;

	@MemberOrder(sequence = "1.3")
	@Disabled(When.ONCE_PERSISTED)
	public Model getModel() {
		return model;
	}

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

	
	// {{ VehicleType
	private VehicleType type;

	@MemberOrder(sequence = "1.3.5")
	@Disabled
	public VehicleType getType() {
		return type;
	}

	public void setType(final VehicleType type) {
		this.type = type;
	}

	public void modifyType(final VehicleType type) {
		// check for no-op
		if (type == null || type.equals(getType())) {
			return;
		}
		// associate new
		setType(type);
		// additional business logic
		onModifyType(type);
	}

	public void clearType() {
		// check for no-op
		if (getType() == null) {
			return;
		}
		// dissociate existing
		setType(null);
		// additional business logic
		onClearType();
	}

	protected void onModifyType(final VehicleType type) {
	}

	protected void onClearType() {
	}
	// }}


	

	// {{ bookService
	@MemberOrder(sequence="1.1")
	@Disabled(When.UNTIL_PERSISTED)
	public Service bookService( ) {
	    Service service = newTransientInstance(Service.class);
	    service.setVehicle(this);
	    return service;
	}
	// }}
	
	
	// {{ delete
	@MemberOrder(sequence = "1.2")
	@Disabled(When.UNTIL_PERSISTED)
	public void delete() {
		clearOwningCustomer();
		getContainer().remove(this);
	}
	// }}

	
	// {{ mostRecentService
	@MemberOrder(sequence = "1.3")
	@Disabled(When.UNTIL_PERSISTED)
	public Service mostRecentService() {
	    TreeSet<Service> sortedServices = 
	        new TreeSet<Service>(new Comparator<Service>() {
	            public int compare(Service s, Service t) {
	                return s.getBookedInAndReady().compareTo(
	                       t.getBookedInAndReady());
	            }
	    });
	    sortedServices.addAll(getServices());
	    return sortedServices.size() > 0? sortedServices.last(): null;
	}
	// }}


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





}