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

import java.sql.Date;
import java.util.ArrayList;
import java.util.List;

import org.nakedobjects.applib.AbstractDomainObject;
import org.nakedobjects.applib.annotation.Disabled;
import org.nakedobjects.applib.annotation.Hidden;
import org.nakedobjects.applib.annotation.MaxLength;
import org.nakedobjects.applib.annotation.MemberOrder;
import org.nakedobjects.applib.annotation.MultiLine;
import org.nakedobjects.applib.annotation.Optional;
import org.nakedobjects.applib.annotation.When;
import org.nakedobjects.applib.clock.Clock;

import com.pragprog.dhnako.carserv.dom.domainservices.EmailService;
import com.pragprog.dhnako.carserv.dom.vehicle.Vehicle;
import com.pragprog.dhnako.carserv.dom.vehicle.VehicleType;

public class Customer extends AbstractDomainObject {

	// {{ Identification
	public String title() {
		return getName() != null? getName().title(): null;
	}

	public String iconName() {
		return getName() != null? getName().getTitle().getIconName(): null;
	}
	// }}

	// {{ Lifecycle methods
	public void created() {
		name = newTransientInstance(Name.class);
	}
	// }}

	
	// {{ Name
	private Name name;
	@Disabled
	@MemberOrder(sequence = "1")
	public Name getName() {
		return name;
	}

	public void setName(final Name name) {
		this.name = name;
	}
	// }}


	// {{ Notes
	private String notes;

    @MultiLine(numberOfLines = 10, preventWrapping = false)
	@Optional
	@MaxLength(255)
	@MemberOrder(sequence = "1.5")
	public String getNotes() {
		return notes;
	}

	public void setNotes(final String notes) {
		this.notes = notes;
	}

	public void modifyNotes(final String notes) {
		String currentNotes = getNotes();
		// check for no-op
		if (notes == null || notes.equals(currentNotes)) {
			return;
		}
		// associate new
		setNotes(notes);
		// additional business logic
		onModifyNotes(currentNotes, notes);
	}

	public void clearNotes() {
		String currentNotes = getNotes();
		// check for no-op
		if (currentNotes == null) {
			return;
		}
		// dissociate existing
		setNotes(null);
		// additional business logic
		onClearNotes(currentNotes);
	}

	protected void onModifyNotes(final String oldNotes, final String newNotes) {
		setNotedBy(getUser().getName());
	}

	protected void onClearNotes(final String oldNotes) {
	}

	public String disableNotes() {
		return doesntOwnAnyVehicles()?
				"Can only add notes for customers with vehicles": null;
	}
	// }}

	
	// {{ NotedBy
	private String notedBy;

	@MemberOrder(sequence = "1.5.5")
	@Optional
	@MaxLength(16)
	public String getNotedBy() {
		return notedBy;
	}

	public void setNotedBy(final String notedBy) {
		this.notedBy = notedBy;
	}
	// }}



	// {{ Feedback
	private String feedback;

	@MemberOrder(sequence = "1.6")
	@MultiLine(numberOfLines=5, preventWrapping=false)
	@Optional
	public String getFeedback() {
		return feedback;
	}

	public void setFeedback(final String feedback) {
		this.feedback = feedback;
	}

	public boolean hideFeedback() {
		return !isValuableCustomer();
	}
	// }}


	// {{ Vehicles
	private List<Vehicle> vehicles = new ArrayList<Vehicle>();

	@MemberOrder(sequence = "1.4")
	@Disabled
	public List<Vehicle> getVehicles() {
		return vehicles;
	}

	public void setVehicles(final List<Vehicle> vehicles) {
		this.vehicles = vehicles;
	}

	public void addToVehicles(final Vehicle vehicle) {
		// check for no-op
		if (vehicle == null || getVehicles().contains(vehicle)) {
			return;
		}
		// dissociate arg from its current parent (if any).
		vehicle.clearOwningCustomer();
		// associate arg
		vehicle.setOwningCustomer(this);
		getVehicles().add(vehicle);
		// additional business logic
		onAddToVehicles(vehicle);
	}

	public void removeFromVehicles(final Vehicle vehicle) {
		// check for no-op
		if (vehicle == null || !getVehicles().contains(vehicle)) {
			return;
		}
		// dissociate arg
		vehicle.setOwningCustomer(null);
		getVehicles().remove(vehicle);
		// additional business logic
		onRemoveFromVehicles(vehicle);
	}

	protected void onAddToVehicles(final Vehicle vehicle) {
	}

	protected void onRemoveFromVehicles(final Vehicle vehicle) {
	}
	
	public String validateAddToVehicles(final Vehicle vehicle) {
		return getVehicles().size() >= 3?
				"No more than 3 vehicles per customer": null;
	}

	public String validateRemoveFromVehicles(final Vehicle vehicle) {
		return null;
	}
	// }}

	
	
	// {{ Since
	private Date since;

	@MemberOrder(sequence = "1.6")
	public Date getSince() {
		return since;
	}

	public void setSince(final Date since) {
		this.since = since;
	}

	public Date defaultSince() {
		return new java.sql.Date(Clock.getTime());
	}
	// }}


	// {{ EmailAddress
	private String emailAddress;

	@MemberOrder(sequence = "1.3.5")
	@Optional
	public String getEmailAddress() {
		return emailAddress;
	}

	public void setEmailAddress(final String emailAddress) {
		this.emailAddress = emailAddress;
	}
	// }}



	// {{ newVehicle
	@MemberOrder(sequence = "1.1")
	@Disabled(When.UNTIL_PERSISTED)
	public Vehicle newVehicle(final VehicleType vehicleType) {
	    Vehicle vehicle = vehicleType.create();
	    vehicle.setOwningCustomer(this);
	    return vehicle;
	}
	// }}

	
	
	// {{ deleteVehicle
	@MemberOrder(sequence = "1.2")
	@Disabled(When.UNTIL_PERSISTED)
	public void deleteVehicle(final Vehicle vehicle) {
		vehicle.delete();
	}
	public String validateDeleteVehicle(final Vehicle vehicle) {
		return getVehicles().contains(vehicle)?
				null: "Customer does not own this vehicle";
	}
	public List<Vehicle> choices0DeleteVehicle() {
		return getVehicles();
	}
	public Vehicle default0DeleteVehicle() {
		return getVehicles().size() == 1? getVehicles().get(0): null;
	}
	public String disableDeleteVehicle() {
	    return doesntOwnAnyVehicles()? "No vehicles to delete": null;
	}
	// }}
	
	

	// {{ remind
	@MemberOrder(sequence = "1.3")
	@Disabled(When.UNTIL_PERSISTED)
	public void remind() {
	    emailService.sendEmail(
	        "customer-service@carserv.com", 
	        getEmailAddress(), 
	        "Vehicle Servicing Reminder", 
	        "Your vehicle is due for servicing soon");
	}
	public String disableRemind() {
	    return getEmailAddress() == null || getEmailAddress().length() == 0?
	        "No email address":null;
	}
	// }}
	
	
	// {{ Helpers
	@Hidden
	public boolean matches(final String firstName, final String lastName) {
		return getName() != null ? getName().matches(firstName, lastName) : false;
	}
	private boolean doesntOwnAnyVehicles() {
		return getVehicles().size() == 0;
	}
	private boolean isValuableCustomer() {
		return getVehicles().size() >= 2;
	}
	// }}


	// {{ Injected dependencies
	// {{ injected: EmailService
	private EmailService emailService;

	public void setEmailService(final EmailService emailService) {
		this.emailService = emailService;
	}
	// }}
	// }}


}
