/*-----------------------------------------------------------------------------------------
    Copyright (c)2000 TogetherSoft Corporation. Patents pending. All rights reserved.
-----------------------------------------------------------------------------------------*/

package ProblemDomain;

import java.util.Date;
import java.util.Vector;
import java.util.Enumeration;
import java.math.BigDecimal;
import java.text.NumberFormat;
import DataManagement.SaleDM;

/**
 * @stereotype moment-interval
 * @author TogetherSoft
 */
public class CashSale {

    /** Poor way to do taxes (but it's easy <g>). */
    public final static double TAX_RATE = 0.06;

    /**
     * This is the list of sale details (line items)
     * @link aggregation
     * @associates <b>ProblemDomain.CashSaleDetail</b>
     * @supplierCardinality 1..*
     */
    private Vector detailList;

    /** Timestamp of this sale */
    private Date time;

    /**
     * Subtotal of all line items. NOTE: this could be calculated on the fly by iterating through the details, but once we
     * are all done with the transaction, we have decided to purposefully store the result.
     * This is a trade-off based on the fact that this transaction, once completed, is never
     * edited again. By redundantly storing data, we are allowing a quick answer to support
     * the various analysis "runs" made against the data.
     */
    private BigDecimal subtotal;

    /** This represents a discount at the entire sale level. */
    private BigDecimal discountAmount;

    /** This is the total tax for this sale. */
    private BigDecimal tax;

    /**
     * This allows us to "plug in" different cash sale sequences to support other ways to take in the payment. Examples
     * include: Cash, Credit, Debit, Check.
     * @supplierCardinality 1
     */
    private IMakeCashSale anICashSaleSequencerPlugInPoint;

    /**
     * This is the payment. Based on this value and the total due, we can compute the change due.
     * @supplierCardinality 0..*
     * @clientCardinality 1
     */
    private BigDecimal payment;
    private final static int SALE_NEW = 0;
    private final static int SALE_OPEN = 1;
    private final static int SALE_CLOSED = 2;

    /** Indicates if sale is in process or completed. */
    private int status = SALE_NEW;

    /**
     * Access to the Data Management Layer. NOTE: shown here, but could really
     * just be transiently created in methods that require DM layer...
     */
    private SaleDM mySaleDM;

/* ========================================
 * Constructors
 * ======================================== */

    public CashSale() {
        time = new Date();
        detailList = new Vector();
        discountAmount = new BigDecimal(0.0);
        payment = new BigDecimal(0.0);
        status = SALE_NEW;
        subtotal = new BigDecimal(0.0);
        tax = new BigDecimal(0.0);
    }

/* ========================================
 * Business Methods
 * ======================================== */

    public BigDecimal makeCashSale(BigDecimal paymentAmt) throws InsuffPaymentException {
        payment = paymentAmt;
        // compute change due
        BigDecimal total = calcTotal();
        BigDecimal change = paymentAmt.subtract(total);
        // If change is negative, insufficient payment made!
        if (change.compareTo(new BigDecimal(0.0)) < 0) {
            NumberFormat cf = NumberFormat.getCurrencyInstance();
            String msg = new StringBuffer("Payment of ").append(cf.format(paymentAmt)).append(" less than Total: ")
                .append(cf.format(total)).toString();
            throw new InsuffPaymentException(msg);
        }
        // indicate sale is complete
        completed();
        return change;
    } // END makeCashSale()

    public BigDecimal calcSubtotal() {
        Enumeration iter = getDetails();
        CashSaleDetail detail = null;
        BigDecimal subtotal = new BigDecimal(0.0);
        while (iter.hasMoreElements()) {
            detail = (CashSaleDetail)iter.nextElement();
            subtotal = subtotal.add(detail.calcTotal());
        } // ENDWHILE
        return subtotal;
    }

    public BigDecimal calcDiscountAmount() {
        return discountAmount;
    }

    /** BAD IMPLEMENTATION of hard-coded fixed tax rate */
    public BigDecimal calcTax() {
        BigDecimal subtotal = calcSubtotal();
        BigDecimal tax = subtotal.multiply(new BigDecimal(TAX_RATE));
        return tax;
    }

    /** Sum up the subtotal and the tax. */
    public BigDecimal calcTotal() {
        BigDecimal subtotal = calcSubtotal();
        BigDecimal tax = calcTax();
        BigDecimal total = new BigDecimal(0.0);
        total = total.add(subtotal);
        total = total.add(tax);
        return total;
    }

    public BigDecimal recalcTotal() {
        return calcTotal();
    }

    public boolean verifyAvailability() {
        return false;
    }

    public boolean isCompleted() {
        if (status == SALE_CLOSED) {
            return true;
        }
        else {
            return false;
        }
    }

    public boolean isPending() {
        if (status == SALE_OPEN) {
            return true;
        }
        else {
            return false;
        }
    }

    public void completed() {
        status = SALE_CLOSED;
    }

/* ========================================
 * Persistence Methods
 * ======================================== */

    public void save() {
        mySaleDM.save(this);
    } // END save()

/* ========================================
 * Class-Level Business Methods
 * ======================================== */

    public static Vector listCashSales() {
        return new Vector();
    }

    public static BigDecimal calcTotalCashSales() {
        return new BigDecimal(0);
    }

    public static int calcTotalCashSalesQty() {
        return 0;
    }

    public static BigDecimal calcAvgTotalCashSale() {
        return new BigDecimal(0);
    }

    public static int calcAvgTotalCashSaleQty() {
        return 0;
    }

    public static int calcCashSalesRate() {
        return 0;
    }

    public static int calcCashSalesQtyRate() {
        return 0;
    }

/* ========================================
 * Accessor/Mutator Methods
 * ======================================== */

    public void addSaleDetail(CashSaleDetail aCashSaleDetail) {
        // Change status on first detail addition
        if (status == SALE_NEW) {
            status = SALE_OPEN;
        }
        // Do a simple check to see if same item has been scanned
        // before. If so, then increment qty.
        CashSaleDetail detail = null;
        boolean foundIt = false;
        for (int i = 0; i < detailList.size(); ++i) {
            detail = (CashSaleDetail)detailList.elementAt(i);
            if (detail.getProductDesc().getItemNumber() == aCashSaleDetail.getProductDesc().getItemNumber()) {
                foundIt = true;
                detail.setQty(detail.getQty() + 1); // Change the real thing
                break; // Out of for loop
            } // ENDIF (match)
        } // ENDFOR (each element)
        // Add it if not already there
        if (!foundIt) {
            detailList.addElement(aCashSaleDetail);
        }
    } // END addSaleDetail()

    public Enumeration getDetails() {
        return detailList.elements();
    }

    public Vector getDetailList() {
        // May want to return a clone instead of the real things
        return detailList;
    }

    public Date getTime() {
        return new Date(time.getTime());
    }

    public void setCompleted(boolean bool) {
        if (bool) {
            status = SALE_CLOSED;
        }
        else {
            status = SALE_OPEN;
        }
    }
} // END class CashSale

