/***
 * 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 org.nakedobjects.applib.AbstractDomainObject;
import org.nakedobjects.applib.annotation.Disabled;
import org.nakedobjects.applib.annotation.MemberOrder;
import org.nakedobjects.applib.annotation.Named;
import org.nakedobjects.applib.annotation.NotPersistable;
import org.nakedobjects.applib.annotation.Optional;
import org.nakedobjects.applib.annotation.RegEx;

import com.pragprog.dhnako.carserv.dom.payment.PaymentMethod;
import com.pragprog.dhnako.carserv.dom.payment.PaymentMethodType;
import com.pragprog.dhnako.carserv.dom.payment.PaymentMethodTypeRepository;
import com.pragprog.dhnako.carserv.dom.person.Person;
import com.pragprog.dhnako.carserv.dom.person.Title;
import com.pragprog.dhnako.carserv.dom.vehicle.Model;
import com.pragprog.dhnako.carserv.dom.vehicle.RegistrationNumber;
import com.pragprog.dhnako.carserv.dom.vehicle.Vehicle;

@NotPersistable
public class CustomerTakeOn extends AbstractDomainObject {

	// {{ Identification
	public String title() {
		return "Customer Take On";
	}
	// }}
	
	// {{ Title
	private Title title;

	@MemberOrder(sequence = "1.1")
	public Title getTitle() {
		return title;
	}

	public void setTitle(final Title title) {
		this.title = title;
	}

	public void modifyTitle(final Title title) {
		// check for no-op
		if (title == null || title.equals(getTitle())) {
			return;
		}
		// associate new
		setTitle(title);
		// additional business logic
		onModifyTitle(title);
	}

	public void clearTitle() {
		// check for no-op
		if (getTitle() == null) {
			return;
		}
		// dissociate existing
		setTitle(null);
		// additional business logic
		onClearTitle();
	}

	protected void onModifyTitle(final Title title) {
	}

	protected void onClearTitle() {
	}
	public String disableTitle() {
		return getTakenOn()?"Already taken on":null;
	}
	// }}
	
	// {{ FirstName
	private String firstName;

	@MemberOrder(sequence = "1.2")
	public String getFirstName() {
		return firstName;
	}

	public void setFirstName(final String firstName) {
		this.firstName = firstName;
	}

	public void modifyFirstName(final String firstName) {
		// check for no-op
		if (firstName == null || firstName.equals(getFirstName())) {
			return;
		}
		// associate new
		setFirstName(firstName);
		// additional business logic
		onModifyFirstName(firstName);
	}

	public void clearFirstName() {
		// check for no-op
		if (getFirstName() == null) {
			return;
		}
		// dissociate existing
		setFirstName(null);
		// additional business logic
		onClearFirstName();
	}

	protected void onModifyFirstName(final String firstName) {
	}

	protected void onClearFirstName() {
	}
	public String disableFirstName() {
		return getTakenOn()?"Already taken on":null;
	}
	// }}


	
	// {{ LastName
	private String lastName;

	@MemberOrder(sequence = "1.3")
	public String getLastName() {
		return lastName;
	}

	public void setLastName(final String lastName) {
		this.lastName = lastName;
	}

	public void modifyLastName(final String lastName) {
		// check for no-op
		if (lastName == null || lastName.equals(getLastName())) {
			return;
		}
		// associate new
		setLastName(lastName);
		// additional business logic
		onModifyLastName(lastName);
	}

	public void clearLastName() {
		// check for no-op
		if (getLastName() == null) {
			return;
		}
		// dissociate existing
		setLastName(null);
		// additional business logic
		onClearLastName();
	}

	protected void onModifyLastName(final String lastName) {
	}

	protected void onClearLastName() {
	}
	public String disableLastName() {
		return getTakenOn()?"Already taken on":null;
	}
	// }}

	// {{ Vehicle1Model
	private Model vehicle1Model;

	@Optional
	@MemberOrder(sequence = "2.1")
	public Model getVehicle1Model() {
		return vehicle1Model;
	}

	public void setVehicle1Model(final Model vehicle1Model) {
		this.vehicle1Model = vehicle1Model;
	}

	public void modifyVehicle1Model(final Model vehicle1Model) {
		// check for no-op
		if (vehicle1Model == null || vehicle1Model.equals(getVehicle1Model())) {
			return;
		}
		// associate new
		setVehicle1Model(vehicle1Model);
		// additional business logic
		onModifyVehicle1Model(vehicle1Model);
	}

	public void clearVehicle1Model() {
		// check for no-op
		if (getVehicle1Model() == null) {
			return;
		}
		// dissociate existing
		setVehicle1Model(null);
		// additional business logic
		onClearVehicle1Model();
	}

	protected void onModifyVehicle1Model(final Model vehicle1Model) {
	}

	protected void onClearVehicle1Model() {
	}
	public String disableVehicle1Model() {
		return getTakenOn()?"Already taken on":null;
	}
	// }}


	// {{ Vehicle1RegistrationNumber
	private RegistrationNumber vehicle1RegistrationNumber;

	@Optional
	@MemberOrder(sequence = "2.2")
	public RegistrationNumber getVehicle1RegistrationNumber() {
		return vehicle1RegistrationNumber;
	}

	public void setVehicle1RegistrationNumber(final RegistrationNumber vehicle1RegistrationNumber) {
		this.vehicle1RegistrationNumber = vehicle1RegistrationNumber;
	}

	public void modifyVehicle1RegistrationNumber(final RegistrationNumber vehicle1RegistrationNumber) {
		// check for no-op
		if (vehicle1RegistrationNumber == null || vehicle1RegistrationNumber.equals(getVehicle1RegistrationNumber())) {
			return;
		}
		// associate new
		setVehicle1RegistrationNumber(vehicle1RegistrationNumber);
		// additional business logic
		onModifyVehicle1RegistrationNumber(vehicle1RegistrationNumber);
	}

	public void clearVehicle1RegistrationNumber() {
		// check for no-op
		if (getVehicle1RegistrationNumber() == null) {
			return;
		}
		// dissociate existing
		setVehicle1RegistrationNumber(null);
		// additional business logic
		onClearVehicle1RegistrationNumber();
	}

	protected void onModifyVehicle1RegistrationNumber(final RegistrationNumber vehicle1RegistrationNumber) {
	}

	protected void onClearVehicle1RegistrationNumber() {
	}
	public String disableVehicle1RegistrationNumber() {
		return getTakenOn()?"Already taken on":null;
	}
	// }}


	
	// {{ Vehicle2Model
	private Model vehicle2Model;

	@Optional
	@MemberOrder(sequence = "3.1")
	public Model getVehicle2Model() {
		return vehicle2Model;
	}

	public void setVehicle2Model(final Model vehicle2Model) {
		this.vehicle2Model = vehicle2Model;
	}

	public void modifyVehicle2Model(final Model vehicle2Model) {
		// check for no-op
		if (vehicle2Model == null || vehicle2Model.equals(getVehicle2Model())) {
			return;
		}
		// associate new
		setVehicle2Model(vehicle2Model);
		// additional business logic
		onModifyVehicle2Model(vehicle2Model);
	}

	public void clearVehicle2Model() {
		// check for no-op
		if (getVehicle2Model() == null) {
			return;
		}
		// dissociate existing
		setVehicle2Model(null);
		// additional business logic
		onClearVehicle2Model();
	}

	protected void onModifyVehicle2Model(final Model vehicle2Model) {
	}

	protected void onClearVehicle2Model() {
	}
	public String disableVehicle2Model() {
		return getTakenOn()?"Already taken on":null;
	}
	// }}


	
	// {{ Vehicle2RegistrationNumber
	private RegistrationNumber vehicle2RegistrationNumber;

	@Optional
	@MemberOrder(sequence = "3.2")
	public RegistrationNumber getVehicle2RegistrationNumber() {
		return vehicle2RegistrationNumber;
	}

	public void setVehicle2RegistrationNumber(final RegistrationNumber vehicle2RegistrationNumber) {
		this.vehicle2RegistrationNumber = vehicle2RegistrationNumber;
	}

	public void modifyVehicle2RegistrationNumber(final RegistrationNumber vehicle2RegistrationNumber) {
		// check for no-op
		if (vehicle2RegistrationNumber == null || vehicle2RegistrationNumber.equals(getVehicle2RegistrationNumber())) {
			return;
		}
		// associate new
		setVehicle2RegistrationNumber(vehicle2RegistrationNumber);
		// additional business logic
		onModifyVehicle2RegistrationNumber(vehicle2RegistrationNumber);
	}

	public void clearVehicle2RegistrationNumber() {
		// check for no-op
		if (getVehicle2RegistrationNumber() == null) {
			return;
		}
		// dissociate existing
		setVehicle2RegistrationNumber(null);
		// additional business logic
		onClearVehicle2RegistrationNumber();
	}

	protected void onModifyVehicle2RegistrationNumber(final RegistrationNumber vehicle2RegistrationNumber) {
	}

	protected void onClearVehicle2RegistrationNumber() {
	}
	public String disableVehicle2RegistrationNumber() {
		return getTakenOn()?"Already taken on":null;
	}
	// }}


	// {{ CreditCardNumber
	private String creditCardNumber;

	@Optional
	@MemberOrder(sequence = "4.1")
	public String getCreditCardNumber() {
		return creditCardNumber;
	}

	public void setCreditCardNumber(final String creditCardNumber) {
		this.creditCardNumber = creditCardNumber;
	}

	public void modifyCreditCardNumber(final String creditCardNumber) {
		// check for no-op
		if (creditCardNumber == null || creditCardNumber.equals(getCreditCardNumber())) {
			return;
		}
		// associate new
		setCreditCardNumber(creditCardNumber);
		// additional business logic
		onModifyCreditCardNumber(creditCardNumber);
	}

	public void clearCreditCardNumber() {
		// check for no-op
		if (getCreditCardNumber() == null) {
			return;
		}
		// dissociate existing
		setCreditCardNumber(null);
		// additional business logic
		onClearCreditCardNumber();
	}

	protected void onModifyCreditCardNumber(final String creditCardNumber) {
	}

	protected void onClearCreditCardNumber() {
	}
	public String disableCreditCardNumber() {
		return getTakenOn()?"Already taken on":null;
	}
	// }}

	
	// {{ CreditCardExpiryDate
	private String creditCardExpiryDate;

	@Optional
	@RegEx(validation="[0-9]{2}/[0-9]{4}")
	@MemberOrder(sequence = "4.2")
	public String getCreditCardExpiryDate() {
		return creditCardExpiryDate;
	}

	public void setCreditCardExpiryDate(final String creditCardExpiryDate) {
		this.creditCardExpiryDate = creditCardExpiryDate;
	}

	public void modifyCreditCardExpiryDate(final String creditCardExpiryDate) {
		// check for no-op
		if (creditCardExpiryDate == null || creditCardExpiryDate.equals(getCreditCardExpiryDate())) {
			return;
		}
		// associate new
		setCreditCardExpiryDate(creditCardExpiryDate);
		// additional business logic
		onModifyCreditCardExpiryDate(creditCardExpiryDate);
	}

	public void clearCreditCardExpiryDate() {
		// check for no-op
		if (getCreditCardExpiryDate() == null) {
			return;
		}
		// dissociate existing
		setCreditCardExpiryDate(null);
		// additional business logic
		onClearCreditCardExpiryDate();
	}

	protected void onModifyCreditCardExpiryDate(final String creditCardExpiryDate) {
	}

	protected void onClearCreditCardExpiryDate() {
	}
	public String disableCreditCardExpiryDate() {
		return getTakenOn()?"Already taken on":null;
	}
	// }}
	
	
	// {{ AccountLimit
	private Integer accountLimit;

	@Optional
	@MemberOrder(sequence = "5.1")
	public Integer getAccountLimit() {
		return accountLimit;
	}

	public void setAccountLimit(final Integer accountLimit) {
		this.accountLimit = accountLimit;
	}

	public void modifyAccountLimit(final Integer accountLimit) {
		// check for no-op
		if (accountLimit == null || accountLimit.equals(getAccountLimit())) {
			return;
		}
		// associate new
		setAccountLimit(accountLimit);
		// additional business logic
		onModifyAccountLimit(accountLimit);
	}

	public void clearAccountLimit() {
		// check for no-op
		if (getAccountLimit() == null) {
			return;
		}
		// dissociate existing
		setAccountLimit(null);
		// additional business logic
		onClearAccountLimit();
	}

	protected void onModifyAccountLimit(final Integer accountLimit) {
	}

	protected void onClearAccountLimit() {
	}
	public String disableAccountLimit() {
		return getTakenOn()?"Already taken on":null;
	}
	// }}


	
	// {{ SetupCash
	private Boolean setupCash;

	@Optional
	@MemberOrder(sequence = "6.1")
	public Boolean getSetupCash() {
		return setupCash;
	}

	public void setSetupCash(final Boolean setupCash) {
		this.setupCash = setupCash;
	}

	public void modifySetupCash(final Boolean setupCash) {
		// check for no-op
		if (setupCash == null || setupCash.equals(getSetupCash())) {
			return;
		}
		// associate new
		setSetupCash(setupCash);
		// additional business logic
		onModifySetupCash(setupCash);
	}

	public void clearSetupCash() {
		// check for no-op
		if (getSetupCash() == null) {
			return;
		}
		// dissociate existing
		setSetupCash(null);
		// additional business logic
		onClearSetupCash();
	}

	protected void onModifySetupCash(final Boolean setupCash) {
	}

	protected void onClearSetupCash() {
	}
	public String disableSetupCash() {
		return getTakenOn()?"Already taken on":null;
	}
	// }}


	
	// {{ TakenOn
	private Boolean takenOn;

	@Disabled
	@MemberOrder(sequence = "9")
	public Boolean getTakenOn() {
		return takenOn;
	}

	public void setTakenOn(final Boolean takenOn) {
		this.takenOn = takenOn;
	}

	public void modifyTakenOn(final Boolean takenOn) {
		// check for no-op
		if (takenOn == null || takenOn.equals(getTakenOn())) {
			return;
		}
		// associate new
		setTakenOn(takenOn);
		// additional business logic
		onModifyTakenOn(takenOn);
	}

	public void clearTakenOn() {
		// check for no-op
		if (getTakenOn() == null) {
			return;
		}
		// dissociate existing
		setTakenOn(null);
		// additional business logic
		onClearTakenOn();
	}

	protected void onModifyTakenOn(final Boolean takenOn) {
	}

	protected void onClearTakenOn() {
	}
	public Boolean defaultTakenOn() {
		return false;
	}
	// }}



	// {{
	@Named("OK")
	public Person ok() {
	    Person person = 
	        customerRepository.newCustomer();
	    person.getName().setFirstName(getFirstName());
	    person.getName().setLastName(getLastName());
	    person.getName().setTitle(getTitle());
	    Customer customer = person.getRole(Customer.class);
	    if (enteredAllOf(getVehicle1Model(), getVehicle1RegistrationNumber())) {
	        Vehicle vehicle = customer.newVehicle(getVehicle1Model());
	        vehicle.setRegistrationNumber(getVehicle1RegistrationNumber());
	        customer.addToVehicles(vehicle);
	    }
	    if (enteredAllOf(getVehicle2Model(), getVehicle2RegistrationNumber())) {
	        Vehicle vehicle = customer.newVehicle(getVehicle2Model());
	        vehicle.setRegistrationNumber(getVehicle2RegistrationNumber());
	        customer.addToVehicles(vehicle);
	    }
	    if (enteredAllOf(getCreditCardNumber(), getCreditCardExpiryDate())) {
	    	CreditCard creditCard = (CreditCard) customer.newPaymentMethod(findPaymentMethodType(CreditCard.class));
	    	creditCard.setCardNumber(getCreditCardNumber());
	    	creditCard.setExpiryDate(getCreditCardExpiryDate());
	    	customer.addToPaymentMethods(creditCard);
	    }
	    if (enteredAllOf(getAccountLimit()) && getAccountLimit() > 0) {
	    	Account account = (Account) customer.newPaymentMethod(findPaymentMethodType(Account.class));
	    	account.setLimit(getAccountLimit());
	    	customer.addToPaymentMethods(account);
	    }
	    if (getSetupCash()) {
	    	Cash cash = (Cash) customer.newPaymentMethod(findPaymentMethodType(Cash.class));
	    	customer.addToPaymentMethods(cash);
	    }
	    persist(person);
	    modifyTakenOn(true);
	    return person;
	}
	public String disableOk() {
		String invalidReason = validate(this);
		if (invalidReason != null) {
			return invalidReason;
		}
		return getTakenOn()?"Already taken on":null;
	}
	// }}

	
	// {{ Helpers
	private PaymentMethodType findPaymentMethodType(final Class<? extends PaymentMethod> paymentMethodClass) {
		return paymentMethodTypeRepository.findByClassName(paymentMethodClass.getName());
	}
	private static boolean enteredAllOf(final Object... objects) {
		for(Object o: objects) {
			if (o == null) return false;
		}
		return true;
	}
	// }}


	// {{ injected dependencies
	// {{ injected: CustomerRepository
	private CustomerRepository customerRepository;

	public void setCustomerRepository(final CustomerRepository customerRepository) {
		this.customerRepository = customerRepository;
	}
	// }}

	// {{ injected: PaymentMethodTypeRepository
	private PaymentMethodTypeRepository paymentMethodTypeRepository;

	public void setPaymentMethodTypeRepository(final PaymentMethodTypeRepository paymentMethodTypeRepository) {
		this.paymentMethodTypeRepository = paymentMethodTypeRepository;
	}
	// }}

	// }}






}
