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

package UserInterface;

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableModel;
import javax.swing.table.TableColumn;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import java.math.BigDecimal;
import java.text.DateFormat;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.Vector;
import ProblemDomain.CashSale; // PD Object
import ProblemDomain.CashSaleDetail; // PD Object
import ProblemDomain.ProductDesc; // PD Object
import ProblemDomain.InsuffPaymentException;

/**
 * This is a sample that was based on the JMCU CashSale component built into the Together tool. This presents a simple GUI and
 * shows how we can tie it into the PD classes.
 * @author TogetherSoft*/
public class POSFrame extends JFrame {
    // Problem Domain Object

    CashSale currentSale = new CashSale();
    // Dummy list of store items

    ProductDesc[] products;
    // Sale Detail Table Column Header

    private final String[] colNames = {"Item", "Name", "Unit", "Qty", "Price"};
    private final static int ADDED_DETAIL = 0;
    private final static int REMOVED_DETAIL = 1;
    // Dummy list of cashiers

    private final static String[] cashiers =
        { "Jim","Lucy","Steve","Sarah","Jon","Buddy","Bettie","Sue","John","Ted"};
    NumberFormat currencyFormat = NumberFormat.getCurrencyInstance();
    JMenuBar menuBar1 = new JMenuBar();
    JMenu menuFile = new JMenu();
    JMenuItem menuFileExit = new JMenuItem();
    JMenu menuHelp = new JMenu();
    JMenuItem menuHelpAbout = new JMenuItem();
    JToolBar toolBar = new JToolBar();
    ImageIcon imageHelp;
    JLabel statusBar = new JLabel();
    BorderLayout borderLayout1 = new BorderLayout();
    JScrollPane jScrollPaneDetails = new JScrollPane();
    JTable jTableSaleDetails = new JTable(new TableDataModel());
    JPanel jPanel1 = new JPanel();
    BorderLayout borderLayout2 = new BorderLayout();
    JPanel jPanelScan = new JPanel();
    JButton jButtonScan = new JButton();
    JTextField jTextFieldItem = new JTextField();
    JPanel jPanelDetails = new JPanel(new GridBagLayout());
    JButton jButtonNewSale = new JButton();
    JButton jButtonCancel = new JButton();
    JPanel jPanelCompleteSale = new JPanel();
    JButton jButtonPayment = new JButton();
    JButton jButtonTotal = new JButton();
    BorderLayout borderLayout3 = new BorderLayout();
    JTextField jTextFieldSubTotal = new JTextField();
    GridLayout gridLayout1 = new GridLayout();
    JLabel jLabel1 = new JLabel();
    JLabel jLabel2 = new JLabel();
    JTextField jTextFieldTax = new JTextField();
    JLabel jLabel3 = new JLabel();
    JTextField jTextFieldPayment = new JTextField();
    JTextField jTextFieldTotal = new JTextField();
    JTextField jTextFieldChange = new JTextField();
    JLabel jLabel4 = new JLabel();
    JLabel jLabelCashier = new JLabel();
    JLabel jLabel6 = new JLabel();
    JLabel jLabelTimeStamp = new JLabel();
    JLabel jLabel8 = new JLabel();

    //Construct the frame


    public POSFrame() {
        setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); //do not hide the frame, maybe the user will press "Cancel"
        addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent e) {
                fileExit_actionPerformed(null);
            }
        });
        try {
            setUpProducts();
            jbInit();
        }
        catch(Exception e) {
            e.printStackTrace();
        }
    }

    // Set up the product "database"


    private void setUpProducts() {
        products = new ProductDesc[10];
        products[0] = new ProductDesc("1", "Pepsi 24-pack", "Pepsi 24", new BigDecimal(3.99));
        products[1] = new ProductDesc("2", "Lays Ridges", "Lays", new BigDecimal(1.99));
        products[2] = new ProductDesc("3", "Vienna Sausages", "Vienna Sausages", new BigDecimal(2.99));
        products[3] = new ProductDesc("4", "White Popcorn", "White Popcorn", new BigDecimal(1.29));
        products[4] = new ProductDesc("5", "Soy Burgers", "Soy Burger", new BigDecimal(5.99));
        products[5] = new ProductDesc("6", "Cat Chow", "Cat Chow", new BigDecimal(9.99));
        products[6] = new ProductDesc("7", "Puppy Chow", "Puppy Chow", new BigDecimal(12.99));
        products[7] = new ProductDesc("8", "Finch Food", "Finch Food", new BigDecimal(1.59));
        products[8] = new ProductDesc("9", "Rice Krispies", "Rice Krispies", new BigDecimal(3.29));
        products[9] = new ProductDesc("10", "Fruit Loops", "Fruit Loops", new BigDecimal(3.49));
    }

    //Component initialization


    private void jbInit() throws Exception {
        this.getContentPane().setLayout(borderLayout1);
        this.setSize(new Dimension(459, 396));
        this.setTitle("Point Of Sale");
        statusBar.setText(" ");
        menuFile.setText("File");
        menuFileExit.setText("Exit");
        menuFileExit.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                fileExit_actionPerformed(e);
            }
        });
        menuHelp.setText("Help");
        menuHelpAbout.setText("About");
        menuHelpAbout.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                helpAbout_actionPerformed(e);
            }
        });
        jPanel1.setLayout(borderLayout2);
        jButtonScan.setToolTipText("Scan next item");
        jButtonScan.setMnemonic('S');
        jButtonScan.setText("Scan");
        jButtonScan.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(ActionEvent e) {
                jButtonScan_actionPerformed(e);
            }
        });
        jTextFieldItem.setPreferredSize(new Dimension(182, 21));
        jTextFieldItem.setText("<item>");
        jPanelScan.setBorder(BorderFactory.createRaisedBevelBorder());
        jTableSaleDetails.setBorder(BorderFactory.createLineBorder(Color.black));
        jButtonNewSale.setToolTipText("Start a new sale");
        jButtonNewSale.setMnemonic('N');
        jButtonNewSale.setText("New Sale");
        jButtonNewSale.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(ActionEvent e) {
                jButtonNewSale_actionPerformed(e);
            }
        });
        jButtonCancel.setToolTipText("Cancel current sale");
        jButtonCancel.setMnemonic('A');
        jButtonCancel.setText("Cancel");
        jButtonCancel.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(ActionEvent e) {
                jButtonCancel_actionPerformed(e);
            }
        });
        jButtonPayment.setFont(new java.awt.Font("Dialog", 1, 12));
        jButtonPayment.setToolTipText("Enter payment amount");
        jButtonPayment.setMnemonic('P');
        jButtonPayment.setText("Payment");
        jButtonPayment.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(ActionEvent e) {
                jButtonPayment_actionPerformed(e);
            }
        });
        jButtonTotal.setFont(new java.awt.Font("Dialog", 1, 12));
        jButtonTotal.setToolTipText("Calculate Total");
        jButtonTotal.setMnemonic('T');
        jButtonTotal.setText("Total");
        jButtonTotal.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(ActionEvent e) {
                jButtonTotal_actionPerformed(e);
            }
        });
        GridBagConstraints gridBagConstraints = new GridBagConstraints();
        gridBagConstraints.fill = GridBagConstraints.BOTH;
        gridBagConstraints.weightx = 1.0;
        gridBagConstraints.weighty = 1.0;
        jTextFieldSubTotal.setText("0.00");
        jTextFieldSubTotal.setHorizontalAlignment(SwingConstants.RIGHT);
        jPanelCompleteSale.setLayout(gridLayout1);
        gridLayout1.setColumns(2);
        gridLayout1.setHgap(3);
        gridLayout1.setRows(5);
        jLabel1.setFont(new java.awt.Font("Dialog", 1, 12));
        jLabel1.setHorizontalAlignment(SwingConstants.RIGHT);
        jLabel1.setText("SUBTOTAL:");
        jLabel2.setFont(new java.awt.Font("Dialog", 1, 12));
        jLabel2.setHorizontalAlignment(SwingConstants.RIGHT);
        jLabel2.setText("Tax:");
        jLabel3.setText("       ");
        jTextFieldTax.setText("0.00");
        jTextFieldTax.setHorizontalAlignment(SwingConstants.RIGHT);
        jTextFieldPayment.setText("0.00");
        jTextFieldPayment.setHorizontalAlignment(SwingConstants.RIGHT);
        jTextFieldTotal.setText("0.00");
        jTextFieldTotal.setHorizontalAlignment(SwingConstants.RIGHT);
        jTextFieldChange.setText("0.00");
        jTextFieldChange.setHorizontalAlignment(SwingConstants.RIGHT);
        jLabel4.setText("       ");
        jLabelCashier.setText("       ");
        jLabel6.setText("       ");
        jLabelTimeStamp.setText("       ");
        jLabel8.setText("Change:");
        jLabel8.setFont(new java.awt.Font("Dialog", 1, 12));
        jLabel8.setHorizontalAlignment(SwingConstants.RIGHT);
        jScrollPaneDetails.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
        jScrollPaneDetails.setBorder(BorderFactory.createLineBorder(Color.black));
        toolBar.add(jButtonNewSale);
        toolBar.add(jButtonCancel);
        menuFile.add(menuFileExit);
        menuHelp.add(menuHelpAbout);
        menuBar1.add(menuFile);
        menuBar1.add(menuHelp);
        this.setJMenuBar(menuBar1);
        this.getContentPane().add(toolBar, BorderLayout.NORTH);
        this.getContentPane().add(statusBar, BorderLayout.SOUTH);
        this.getContentPane().add(jPanel1, BorderLayout.CENTER);
        jPanel1.add(jPanelScan, BorderLayout.NORTH);
        jPanelScan.add(jButtonScan, null);
        jPanelScan.add(jTextFieldItem, null);
        jPanel1.add(jPanelDetails, BorderLayout.CENTER);
        jPanelDetails.add(jScrollPaneDetails, gridBagConstraints);
        jScrollPaneDetails.getViewport().add(jTableSaleDetails, null);
        jPanel1.add(jPanelCompleteSale, BorderLayout.SOUTH);
        jPanelCompleteSale.add(jLabel3, null);
        jPanelCompleteSale.add(jLabel1, null);
        jPanelCompleteSale.add(jTextFieldSubTotal, null);
        jPanelCompleteSale.add(jLabel4, null);
        jPanelCompleteSale.add(jLabel2, null);
        jPanelCompleteSale.add(jTextFieldTax, null);
        jPanelCompleteSale.add(jLabel6, null);
        jPanelCompleteSale.add(jButtonTotal, null);
        jPanelCompleteSale.add(jTextFieldTotal, null);
        jPanelCompleteSale.add(jLabelCashier, null);
        jPanelCompleteSale.add(jButtonPayment, null);
        jPanelCompleteSale.add(jTextFieldPayment, null);
        jPanelCompleteSale.add(jLabelTimeStamp, null);
        jPanelCompleteSale.add(jLabel8, null);
        jPanelCompleteSale.add(jTextFieldChange, null);
        // Initialize Detail Table
        initDetailTable();
        // Initialize widgets with data
        loadGUIWithPD();
        // Keep buttons properly grayed out at the start
        initButtons();
        this.toolBar.getRootPane().setDefaultButton(jButtonScan);
    }

    /** Keep buttons properly grayed out at the start. */
    private void initButtons() {
        jButtonNewSale.setEnabled(true);
        jButtonCancel.setEnabled(false);
        jButtonScan.setEnabled(true);
        jButtonPayment.setEnabled(false);
        jButtonTotal.setEnabled(false);
    } // END initButtons()


    private void loadGUIWithPD() {
        jTextFieldItem.setText("");
        jLabelTimeStamp.setText(DateFormat.getDateInstance().format(currentSale.getTime()));
        jLabelCashier.setText(cashiers[(int)(Math.random() * 10.0)]);
        jTextFieldSubTotal.setText("");
        jTextFieldTax.setText("");
        jTextFieldTotal.setText("");
        jTextFieldPayment.setText("");
        jTextFieldChange.setText("");
        jTextFieldChange.setForeground(Color.black);
    } // END loadGUIWithPD()


    private void initDetailTable() {

        jTableSaleDetails.setPreferredScrollableViewportSize(new Dimension(300,70));

        TableColumn column = null;
        // {"Item", "Name", "Unit", "Qty", "Price"};
        for (int i = 0; i < jTableSaleDetails.getColumnCount(); ++i) {
            column = jTableSaleDetails.getColumnModel().getColumn(i);
            switch (i) {
                case 0: // Item
                    column.setPreferredWidth(20);
                    break;
                case 1: // Name
                    column.setPreferredWidth(100);
                    break;
                case 2: // Unit
                    column.setPreferredWidth(30);
                    break;
                case 3: // Qty
                    column.setPreferredWidth(20);
                    break;
                case 4: // Price
                    column.setPreferredWidth(30);
                    break;
                default:
                    column.setPreferredWidth(50);
                    break;
            } // ENDSWITCH (col)
        } // ENDFOR (i)
    }

    //File | Exit action performed


    public void fileExit_actionPerformed(ActionEvent e) {
        if (preCancelCheck() == JOptionPane.OK_OPTION) {
            System.exit(0);
        }
    }

    //Help | About action performed


    public void helpAbout_actionPerformed(ActionEvent e) {
        POSFrame_AboutBox dlg = new POSFrame_AboutBox(this);
        Dimension dlgSize = dlg.getPreferredSize();
        Dimension frmSize = getSize();
        Point loc = getLocation();
        dlg.setLocation((frmSize.width - dlgSize.width) / 2 + loc.x, (frmSize.height - dlgSize.height) / 2 + loc.y);
        dlg.setModal(true);
        dlg.show();
    }
    //=================================================

    /**
     * Create a model of the data. NOTE: We could have built our own local vector to store
     * the sale detail objects, but we are using the GUI object's own instance of a CashSale object to access
     * the information pertaining to its collection of SaleDetails.*/
    class TableDataModel extends AbstractTableModel {
        // These 3 methods always need to be implemented.


        public int getColumnCount() {
            return colNames.length;
        }

        public int getRowCount() {
            Vector detailList = currentSale.getDetailList();
            if (detailList != null) {
                return detailList.size();
            }
            else {
                return 0;
            }
        } // END getRowCount()


        public Object getValueAt(int row, int col) {
            // First get the detail item "row"
            CashSaleDetail detail = (CashSaleDetail)currentSale.getDetailList().elementAt(row);
            ProductDesc product = detail.getProductDesc();
            Object data; // Lets table auto-format native data types
            // Now do the grunt work to map a column to an attribute
            // {"Item", "Name", "Unit", "Qty", "Price"};
            switch (col) {
                case 0: // Item
                    data = product.getItemNumber();
                    break;
                case 1: // Name
                    data = product.getName();
                    break;
                case 2: // Unit Price
                    data = currencyFormat.format(product.getPrice());
                    break;
                case 3: // Qty
                    data = new Integer(detail.getQty());
                    break;
                case 4: // Price
                    data = currencyFormat.format(product.calcPriceForQty(detail.getQty()));
                    break;
                default:
                    data = "Unknown!";
                    break;
            } // ENDSWITCH
            return data;
        } // END getValueAt()

        // The default implementations of these methods in
        // AbstractTableModel would work, but we can refine them.


        public String getColumnName(int column) {
            return colNames[column];
        }

        // Don't allow any editing


        public boolean isCellEditable(int row, int col) {
            return false;
        }

        public void setValueAt(Object aValue, int row, int column) {
            //                                                              data[row][column] = aValue;
        }

        /** Update the table with the changed data model. */
        public void dataChanged(int changeType, int index) {
            if (changeType == ADDED_DETAIL) {
                // Paint the part of the table that changed
                fireTableDataChanged();
                //                      fireTableRowsInserted(index, index);
            }
            else {
                // Use this to redo the whole table
                // since all rows are effectively deleted!
                fireTableDataChanged();
            }
        } // END dataChanged()

        /** JTable uses this method to determine the default renderer/ editor for each cell to improve the display. */
        public Class getColumnClass(int c) {
            return getValueAt(0, c).getClass();
        }
    }; // ENDCLASS TableDataModel


    //=================================================


    private int preCancelCheck() {
        if (currentSale.isPending()) {
            return JOptionPane.showConfirmDialog(null, "OK to Cancel Current Sale?", "Sale Pending",
                JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE);
        }
        else {
            return JOptionPane.OK_OPTION;
        }
    } // END preCancelCheck()


    private void cancelSale() {
        newSale();

/*
                if ( preCancelCheck() == JOptionPane.OK_OPTION )
                {
                        newSale();
                }
                else
                {
                        // do nothing
                }*/
    } // END cancelSale()

    /**
     * Start off with a fresh CashSale object for the transaction. It will reflect the current timestamp and have no details.*/
    private void newSale() {
        if (!currentSale.isCompleted()) {
            if (preCancelCheck() != JOptionPane.OK_OPTION) {
                return;
            }
        }
        currentSale = new CashSale();
        TableDataModel model = (TableDataModel)jTableSaleDetails.getModel();
        model.dataChanged(REMOVED_DETAIL, 0);
        // Reset widgets
        loadGUIWithPD();
        initButtons();
    } // END newSale()


    private void makePayment() {
        String paymentValue = JOptionPane.showInputDialog("Please enter payment");
        paymentValue = removeIllegalChars(paymentValue);
        // See if input is valid number
        try {
            NumberFormat numberFormat = NumberFormat.getInstance();
            numberFormat.setMinimumFractionDigits(2);
            double n = numberFormat.parse(paymentValue).doubleValue();
            // Compute change due
            BigDecimal change = currentSale.makeCashSale(new BigDecimal(paymentValue));
            jTextFieldChange.setText(currencyFormat.format(change));
            jTextFieldChange.setForeground(Color.red);
            jTextFieldPayment.setText(currencyFormat.format(new BigDecimal(paymentValue)));
            // Once payment is made, you can no
            // longer cancel or hit payment.
            jButtonPayment.setEnabled(false);
            jButtonCancel.setEnabled(false);
            this.getRootPane().setDefaultButton(jButtonNewSale);
        }
        catch(ParseException pe) {
            JOptionPane.showMessageDialog(null, pe.getMessage(), "Illegal Text Format", JOptionPane.ERROR_MESSAGE);
            makePayment();
        }
        catch(InsuffPaymentException ipe) {
            JOptionPane.showMessageDialog(null, ipe.getMessage(), "Insufficient Payment", JOptionPane.ERROR_MESSAGE);
        }
    } // END makePayment()


    private void totalSale() {
        jTextFieldSubTotal.setText(currencyFormat.format(currentSale.calcSubtotal()));
        jTextFieldTax.setText(currencyFormat.format(currentSale.calcTax()));
        jTextFieldTotal.setText(currencyFormat.format(currentSale.calcTotal()));
        // Once you total the sale, you can
        // then make a payment. But we'll disable
        // the ability to scan in more or re-calc
        // total (maybe a bit unrealistic).
        jButtonPayment.setEnabled(true);
        jButtonTotal.setEnabled(false);
        jButtonScan.setEnabled(false);
    } // END totalSale()

    /** Use this to simulate scanning items */
    private void scan() {
        int pseudoIndex = (int)(Math.random() * 10.0);
        ProductDesc prod = products[pseudoIndex];
        jTextFieldItem.setText(prod.getDescription());
        CashSaleDetail detail = new CashSaleDetail(prod);
        // Now add it to the current sale
        currentSale.addSaleDetail(detail);
        // And notify the table
        addDetailToTable();
        jButtonCancel.setEnabled(true);
        jButtonTotal.setEnabled(true);
    } // END scan()


    private void addDetailToTable() {
        int index = currentSale.getDetailList().size() - 1;
        TableDataModel model = (TableDataModel)jTableSaleDetails.getModel();
        model.dataChanged(ADDED_DETAIL, index);
    } // END addDetailToTable()

    private String removeIllegalChars(String string) {
        StringBuffer sb = new StringBuffer(string);
        for (int i = 0; i < sb.length(); i++)
            if (sb.charAt(i)==',')
                sb.setCharAt(i, '.');
        return sb.toString();
    } // END removeIllegalChars()


/* =======================
 *       BUTTON EVENTS
 * ======================= */

    void jButtonNewSale_actionPerformed(ActionEvent e) {
        newSale();
    }

    void jButtonCancel_actionPerformed(ActionEvent e) {
        cancelSale();
    }

    void jButtonPayment_actionPerformed(ActionEvent e) {
        makePayment();
    }

    void jButtonTotal_actionPerformed(ActionEvent e) {
        totalSale();
    }

    void jButtonScan_actionPerformed(ActionEvent e) {
        scan();
    }
} // ENDCLASS POSFrame

