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

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.Version;

import org.hibernate.annotations.Any;
import org.hibernate.annotations.AnyMetaDef;
import org.hibernate.annotations.MetaValue;
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;

@Entity
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="payment_method_type", discriminatorType=DiscriminatorType.STRING, length=3)
public abstract class PaymentMethod extends AbstractDomainObject {

	// {{ Identification
	public String title() {
		TitleBuffer buf = new TitleBuffer();
		buf.append(getOwner() != null? getOwner().title(): "");
		buf.append(getName());
		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;
	}
    // }}
    


	// {{ Type
	private PaymentMethodType type;

	@Disabled
	@MemberOrder(sequence = "1")
	@ManyToOne(fetch=FetchType.EAGER)
	public PaymentMethodType getType() {
		return type;
	}

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


	// {{ Owner
	private PaymentMethodOwner owner;

	@Disabled
	@MemberOrder(sequence = "2")
    @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 PaymentMethodOwner getOwner() {
		return owner;
	}

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

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

	public void clearOwner() {
		PaymentMethodOwner currentOwner = getOwner();
		// check for no-op
		if (currentOwner == null) {
			return;
		}
		// delegate to parent to dissociate
		currentOwner.removeFromPaymentMethods(this);
		// additional business logic
		onClearOwner(currentOwner);
	}
	
	protected void onModifyOwner(final PaymentMethodOwner oldOwner, final PaymentMethodOwner newOwner) {
	}

	protected void onClearOwner(final PaymentMethodOwner oldOwner) {
	}
	// }}


	// {{ Name
	private String name;

	@Disabled(When.ONCE_PERSISTED)
	@MemberOrder(sequence = "3")
	public String getName() {
		return name;
	}

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



	// {{ Current
	private Boolean current;
	@Column(name="[current]")
	@MemberOrder(sequence = "4")
	public Boolean getCurrent() {
		return current;
	}

	public void setCurrent(final Boolean current) {
		this.current = current;
	}
	// }}


	// {{ pay
	@Hidden
	public abstract void pay(Payable payable);
	public abstract String validatePay(Payable payable);
	// }}


	// {{ Lifecycle methods
	public void persisting() {
		getOwner().addToPaymentMethods(this);
	}
	// }}

}
