package ee.ut.math.tvt2008.salessystem.ui;

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.beans.EventHandler;
import java.util.ArrayList;
import java.util.List;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.JTableHeader;

import org.apache.log4j.Logger;
import org.hibernate.Session;

import com.jgoodies.looks.windows.WindowsLookAndFeel;

import ee.ut.math.tvt2008.salessystem.domain.controller.SalesDomainController;
import ee.ut.math.tvt2008.salessystem.domain.controller.VerificationFailedException;
import ee.ut.math.tvt2008.salessystem.domain.data.Client;
import ee.ut.math.tvt2008.salessystem.domain.data.Sale;
import ee.ut.math.tvt2008.salessystem.domain.data.StockItem;
import ee.ut.math.tvt2008.salessystem.ui.dialog.SaleInfoDialog;
import ee.ut.math.tvt2008.salessystem.ui.model.SalesSystemModel;
import ee.ut.math.tvt2008.salessystem.ui.panel.PurchaseItemPanel;
import ee.ut.math.tvt2008.salessystem.util.HibernateUtil;
import ee.ut.math.tvt2008.salessystem.util.Translations;
import java.awt.GridLayout;
import java.math.BigDecimal;
import javax.swing.JLabel;
import javax.swing.JTextField;

/**
 * Graphical user interface of the sales system.
 */
public class SalesSystemUI extends JFrame {

    private static final long serialVersionUID = 1L;
    private static final Logger log = Logger.getLogger(SalesSystemUI.class);
    private static final String NEW_PURCHASE_HANDLER = "newPurchaseEventHandler";
    private static final String SUBMIT_PURCHASE_HANDLER = "submitPurchaseEventHandler";
    private static final String CANCEL_PURCHASE_HANDLER = "cancelPurchaseEventHandler";
    private JTextField idStock,  nameStock,  quantityStock,  priceStock;
    private JTextField idClient,  firstNameClient,  lastNameClient,  discountClient;
    private JButton addItem;
    private JButton newPurchase;
    private JButton submitPurchase;
    private JButton cancelPurchase;
    private PurchaseItemPanel purchasePane;
    private final SaleInfoDialog saleInfoDialog;
    private final SalesDomainController domainController;
    private StockItem selectedStockItem;
    private Client selectedClient;

    // application data model
    private SalesSystemModel model;

    /**
     * Constructs sales system GUI.
     * @param domainController Sales domain controller. 
     */
    public SalesSystemUI(SalesDomainController domainController) {
        this.domainController = domainController;
        model = new SalesSystemModel(domainController);

        setTitle(Translations.getString("main.title"));

        // set L&F to the nice Windows style
        try {
            UIManager.setLookAndFeel(new WindowsLookAndFeel());

        } catch (UnsupportedLookAndFeelException e1) {
            log.warn(e1.getMessage());
        }

        saleInfoDialog = new SaleInfoDialog(model);

        drawWidgets();

        // size & location
        int width = 600;
        int height = 400;
        setSize(width, height);
        Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
        setLocation((screen.width - width) / 2, (screen.height - height) / 2);

        addWindowListener(new WindowAdapter() {

            @Override
            public void windowClosing(WindowEvent e) {
                Session session = HibernateUtil.currentSession();
                // close database connection
                session.close();
                log.info("SaleSystem shutdown.");
                System.exit(0);
            }
        });
    }

    private void drawSelectClientDialog() {
        // load clients
        List<Client> allClients = domainController.loadAllClients();
        List<String> clients = new ArrayList<String>();

        for (Client client : allClients) {
            clients.add(client.getId() + ". " + client.getFirstName() + " " + client.getLastName());
        }

        String selectedClientString = (String) JOptionPane.showInputDialog(
                this,
                Translations.getString("main.chooseCustomer"),
                Translations.getString("main.chooseCustomer"),
                JOptionPane.OK_CANCEL_OPTION,
                null,
                clients.toArray(),
                0);

        Client currentClient = null;
        try {
            // XXX - lets assume that client name is displayed as Id. Name
            // split name with space and return first element with dot removed
            if (selectedClientString != null) {
                currentClient = domainController.getClientById(Long.parseLong(selectedClientString.split(" ")[0].replaceAll("\\.", "")));
            }
        } catch (NumberFormatException e) {
            // Failed to parse client id, probably no client was selected
        }
        if (currentClient != null) {
            log.info("Client " + currentClient.getFirstName() + " with ID=" + currentClient.getId() + " got selected.");
        } else {
            log.info("No client selected");
        }
        // update selected client
        model.setCurrentClient(currentClient);
    }

    private void drawWidgets() {
        JTabbedPane tabbedPane = new JTabbedPane();

        tabbedPane.add(Translations.getString("main.register"), drawPurchaseTab());
        tabbedPane.add(Translations.getString("main.stock"), drawStockTab());
        tabbedPane.add(Translations.getString("main.history"), drawSalesHistoryTab());
        tabbedPane.add(Translations.getString("main.customers"), drawCustomersTab());

        getContentPane().add(tabbedPane);
    }

    private Component drawCustomersTab() {
        JPanel panel = new JPanel();
        JPanel addPanel = new JPanel();
        addPanel.setLayout(new GridLayout(5, 2));
        firstNameClient = new JTextField();
        lastNameClient = new JTextField();
        discountClient = new JTextField();
        addPanel.add(new JLabel(Translations.getString("main.firstname")));
        addPanel.add(firstNameClient);
        addPanel.add(new JLabel(Translations.getString("main.lastName")));
        addPanel.add(lastNameClient);
        addPanel.add(new JLabel(Translations.getString("main.discount")));
        addPanel.add(discountClient);
        JButton addButton = new JButton(Translations.getString("main.add"));
        addButton.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                addClient();
            }
        });
        JButton killButton = new JButton(Translations.getString(Translations.getString("main.delete")));
        killButton.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                if (selectedClient != null) {
                    model.deleteClient(selectedClient);
                    log.info("would delete: " + selectedClient.getName());
                }
            }
        });
        addPanel.add(addButton);
        addPanel.add(killButton);
        panel.setBorder(BorderFactory.createLineBorder(Color.BLACK));

        GridBagLayout gb = new GridBagLayout();
        GridBagConstraints gc = new GridBagConstraints();
        panel.setLayout(gb);

        gc.fill = GridBagConstraints.HORIZONTAL;
        gc.anchor = GridBagConstraints.NORTH;
        gc.gridwidth = GridBagConstraints.REMAINDER;
        gc.weightx = 1.0d;
        gc.weighty = 0d;

        panel.add(addPanel, gc);

        gc.weighty = 1.0;
        gc.fill = GridBagConstraints.BOTH;
        final JTable table = new JTable(model.getClientTableModel());
        table.getSelectionModel().addListSelectionListener(new ListSelectionListener() {

            public void valueChanged(ListSelectionEvent event) {
                if (table.getSelectionModel().isSelectedIndex(
                        event.getFirstIndex())) {
                    selectedClient = model.getClientTableModel().getTableRows().get(event.getFirstIndex());
                } else if (table.getSelectionModel().isSelectedIndex(
                        event.getLastIndex())) {
                    selectedClient = model.getClientTableModel().getTableRows().get(event.getLastIndex());
                }
            }
        });

        JTableHeader header = table.getTableHeader();
        header.setReorderingAllowed(false);

        JScrollPane scrollPane = new JScrollPane(table);

        scrollPane.setBorder(BorderFactory.createTitledBorder(Translations.getString("customers.tab.title")));
        panel.add(scrollPane, gc);
        return panel;
    }

    private Component drawSalesHistoryTab() {
        JPanel panel = new JPanel();
        JTable table = new JTable(model.getPurchaseHistoryTableModel());

        table.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        // lisame listeneri, et oleks vĆµimalik nĆ¤idata detailsemat infot ostu kohta
        table.getSelectionModel().addListSelectionListener(new ListSelectionListener() {

            public void valueChanged(ListSelectionEvent e) {
                //Ignore extra messages.
                if (e.getValueIsAdjusting()) {
                    return;
                }
                ListSelectionModel lsm = (ListSelectionModel) e.getSource();
                if (!lsm.isSelectionEmpty()) {
                    int selectedRow = lsm.getMinSelectionIndex();
                    // show sale details
                    saleInfoDialog.updateInfo(model.getPurchaseHistoryTableModel().getTableRows().get(selectedRow).getId());
                    saleInfoDialog.setVisible(true);
                }
            }
        });

        JTableHeader header = table.getTableHeader();
        header.setReorderingAllowed(false);

        JScrollPane scrollPane = new JScrollPane(table);

        GridBagConstraints gc = new GridBagConstraints();
        GridBagLayout gb = new GridBagLayout();
        gc.fill = GridBagConstraints.BOTH;
        gc.weightx = 1.0;
        gc.weighty = 1.0;

        panel.setLayout(gb);
        panel.add(scrollPane, gc);

        panel.setBorder(BorderFactory.createTitledBorder(Translations.getString("saleshistory.tab.title")));
        return panel;
    }

    // warehouse stock tab - consists of a menu and a table
    private Component drawStockTab() {
        JPanel panel = new JPanel();
        panel.setBorder(BorderFactory.createLineBorder(Color.BLACK));

        GridBagLayout gb = new GridBagLayout();
        GridBagConstraints gc = new GridBagConstraints();
        panel.setLayout(gb);

        gc.fill = GridBagConstraints.HORIZONTAL;
        gc.anchor = GridBagConstraints.NORTH;
        gc.gridwidth = GridBagConstraints.REMAINDER;
        gc.weightx = 1.0d;
        gc.weighty = 0d;

        panel.add(drawStockMenuPane(), gc);

        gc.weighty = 1.0;
        gc.fill = GridBagConstraints.BOTH;
        panel.add(drawStockMainPane(), gc);
        return panel;
    }

    // warehouse menu
    private Component drawStockMenuPane() {
        JPanel panel = new JPanel();
        panel.setLayout(new GridLayout(4, 2));


        addItem = new JButton(Translations.getString("stock.tab.add"));
        addItem.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                addStockItem();
            }
        });
        JLabel nameStockLabel = new JLabel(Translations.getString("main.name"));
        nameStock = new JTextField("");
        JLabel quantityStockLabel = new JLabel(Translations.getString("main.quantity"));
        quantityStock = new JTextField("");
        JLabel priceStockLabel = new JLabel(Translations.getString("main.price"));
        priceStock = new JTextField("");
        panel.add(nameStockLabel);
        panel.add(nameStock);
        panel.add(quantityStockLabel);
        panel.add(quantityStock);
        panel.add(priceStockLabel);
        panel.add(priceStock);


        panel.setBorder(BorderFactory.createLineBorder(Color.BLACK));
        JButton killButton = new JButton(Translations.getString(Translations.getString("main.delete")));
        killButton.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                if (selectedStockItem != null) {
                    model.deleteFromStock(selectedStockItem);
                    log.info("would delete: " + selectedStockItem.getName());
                }
            }
        });
        panel.add(addItem);
        panel.add(killButton);
        return panel;
    }

    // table of the wareshouse stock
    private Component drawStockMainPane() {
        JPanel panel = new JPanel();

        final JTable table = new JTable(model.getWarehouseTableModel());
        table.getSelectionModel().addListSelectionListener(new ListSelectionListener() {

            public void valueChanged(ListSelectionEvent event) {
                if (table.getSelectionModel().isSelectedIndex(
                        event.getFirstIndex())) {
                    selectedStockItem = model.getWarehouseTableModel().getTableRows().get(event.getFirstIndex());
                } else if (table.getSelectionModel().isSelectedIndex(
                        event.getLastIndex())) {
                    selectedStockItem = model.getWarehouseTableModel().getTableRows().get(event.getLastIndex());
                }
            }
        });


        JTableHeader header = table.getTableHeader();
        header.setReorderingAllowed(false);

        JScrollPane scrollPane = new JScrollPane(table);

        GridBagConstraints gc = new GridBagConstraints();
        GridBagLayout gb = new GridBagLayout();
        gc.fill = GridBagConstraints.BOTH;
        gc.weightx = 1.0;
        gc.weighty = 1.0;

        panel.setLayout(gb);
        panel.add(scrollPane, gc);

        panel.setBorder(BorderFactory.createTitledBorder(Translations.getString("stock.tab.title")));
        return panel;
    }

    // purchase tab - consists of purchase menu, current purchase dialog and
    // shopping cart table.
    private Component drawPurchaseTab() {
        JPanel panel = new JPanel();
        panel.setBorder(BorderFactory.createLineBorder(Color.BLACK));

        GridBagLayout gb = new GridBagLayout();
        GridBagConstraints gc = new GridBagConstraints();
        panel.setLayout(gb);

        gc.fill = GridBagConstraints.HORIZONTAL;
        gc.anchor = GridBagConstraints.NORTH;
        gc.gridwidth = GridBagConstraints.REMAINDER;
        gc.weightx = 1.0d;
        gc.weighty = 0d;
        // purchase menu
        panel.add(drawPurchaseMenuPane(), gc);

        gc.weighty = 1.0;
        gc.fill = GridBagConstraints.BOTH;

        purchasePane = new PurchaseItemPanel(model);
        panel.add(purchasePane, gc);
        return panel;
    }
    
    // purchase menu
    private Component drawPurchaseMenuPane() {
        JPanel panel = new JPanel();

        GridBagConstraints gc = new GridBagConstraints();
        GridBagLayout gb = new GridBagLayout();

        panel.setLayout(gb);

        gc.anchor = GridBagConstraints.CENTER;
        gc.weightx = 0;

        newPurchase = new JButton(Translations.getString("main.newSale"));
        newPurchase.addActionListener(EventHandler.create(ActionListener.class,
                this, NEW_PURCHASE_HANDLER));

        gc.gridwidth = GridBagConstraints.RELATIVE;
        panel.add(newPurchase, gc);

        submitPurchase = new JButton(Translations.getString("main.submitSale"));
        submitPurchase.addActionListener(EventHandler.create(ActionListener.class,
                this, SUBMIT_PURCHASE_HANDLER));
        submitPurchase.setEnabled(false);
        panel.add(submitPurchase, gc);

        cancelPurchase = new JButton(Translations.getString("main.cancel"));
        cancelPurchase.addActionListener(EventHandler.create(ActionListener.class,
                this, CANCEL_PURCHASE_HANDLER));
        cancelPurchase.setEnabled(false);
        panel.add(cancelPurchase, gc);

        return panel;
    }
     // switch UI to the state that allows to proceed with the purchase
    private void startNewSale() {
        purchasePane.reset();
        model.getCurrentPurchaseTableModel().setSale(new Sale());
        drawSelectClientDialog();
        if (model.getCurrentClient() != null) {
            purchasePane.setEnabled(true);
            submitPurchase.setEnabled(true);
            cancelPurchase.setEnabled(true);
            newPurchase.setEnabled(false);
        }
    }

    // switch UI to the state that allows to initiate new purchase
    private void endSale() {
        purchasePane.reset();
        model.getCurrentPurchaseTableModel().setSale(null);
        model.getCurrentPurchaseTableModel().fireTableDataChanged();

        cancelPurchase.setEnabled(false);
        submitPurchase.setEnabled(false);
        newPurchase.setEnabled(true);
        purchasePane.setEnabled(false);
    }


    // switch UI to the state that allows to initiate new purchase
    private void cancelSale() {
        purchasePane.reset();
        model.getCurrentPurchaseTableModel().setSale(null);
        model.getCurrentPurchaseTableModel().fireTableDataChanged();
        cancelPurchase.setEnabled(false);
        submitPurchase.setEnabled(false);
        newPurchase.setEnabled(true);
        purchasePane.setEnabled(false);
    }

    /**
     * Event handler for the <code>new purchase</code> event.
     */
    public void newPurchaseEventHandler() {
        log.info("New sale process started");
        try {
            domainController.startNewPurchase();
            startNewSale();
        } catch (VerificationFailedException e1) {
            log.error(e1.getMessage());
        }
    }

    /**
     * Event handler for the <code>cancel purchase</code> event.
     */
    public void cancelPurchaseEventHandler() {
        log.info("Sale cancelled");
        try {
            domainController.cancelCurrentPurchase();
            cancelSale();
            model.getCurrentPurchaseTableModel().clear();
        } catch (VerificationFailedException e1) {
            log.error(e1.getMessage());
        }
    }

    /**
     * Event handler for the <code>submit purchase</code> event.
     */
    public void submitPurchaseEventHandler() {
        log.info("Sale complete");
        try {
            log.debug("Contents of the current basket:\n" + model.getCurrentPurchaseTableModel());
            if (model.getCurrentPurchaseTableModel().getRowCount() == 0) {
                return;
            }
            model.submitCurrentPurchase();
            endSale();
        } catch (VerificationFailedException e1) {
            log.error(e1.getMessage());
        }
    }

    private void addClient() {
        String firstName,lastName ;
        int discount;
        firstName = firstNameClient.getText();
        lastName = lastNameClient.getText();
        try {
            discount = Integer.parseInt(discountClient.getText());
        } catch (Exception e) {

            return;
        }
        Client client = new Client();
        client.setFirstName(firstName);
        client.setLastName(lastName);
        client.setDiscountPercentage(discount);
        if ((discount >= 0) && (discount <= 100) && !(firstName.isEmpty()) && (!lastName.isEmpty())) {
            firstNameClient.setText("");
            lastNameClient.setText("");
            discountClient.setText("");
            model.addClient(client);

        }
    }

    private void addStockItem() {
        try {
            int quantity;
            long id;
            BigDecimal price;
            String name;
            StockItem stockItem = new StockItem();
            quantity = Integer.parseInt(quantityStock.getText());
            name = nameStock.getText();
            stockItem.setName(name);
            stockItem.setQuantity(quantity);
            price = BigDecimal.valueOf(Float.parseFloat(priceStock.getText()));
            stockItem.setPrice(price);
            if ((price.compareTo(BigDecimal.ZERO) >= 0) && (quantity >= 0) && !(name.isEmpty())) {
                quantityStock.setText("");
                nameStock.setText("");
                priceStock.setText("");
                model.addStockItem(stockItem);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
