package at.ac.tuwien.sbc.valesriegler.pizzeria.gui;

import at.ac.tuwien.sbc.valesriegler.common.Util;
import at.ac.tuwien.sbc.valesriegler.pizzeria.PizzeriaAgent;
import at.ac.tuwien.sbc.valesriegler.pizzeria.PizzeriaAgent.TablesCreatedHandler;
import at.ac.tuwien.sbc.valesriegler.pizzeria.gui.tablemodels.*;
import at.ac.tuwien.sbc.valesriegler.types.DeliveryGroupData;
import at.ac.tuwien.sbc.valesriegler.types.GroupData;
import at.ac.tuwien.sbc.valesriegler.types.Order;
import at.ac.tuwien.sbc.valesriegler.types.Table;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.swing.*;
import javax.swing.border.TitledBorder;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import java.awt.*;
import java.util.ArrayList;
import java.util.List;

/**
 * Base Frame of the Pizzeria GUI
 *
 * @author Gregor Riegler <gregor DOT riegler AT gmail DOT com>
 */
public class PizzeriaFrame extends JFrame {
    private static final Logger log = LoggerFactory.getLogger(PizzeriaFrame.class);

    private static final String WRONG_INPUT = "Wrong input";
    private static final String THE_NUMBER_MUST_BE_GREATER_OR_EQUAL_TO_1 = "The number must be greater or equal to 1";
    private static final String YOUR_INPUT_CANNOT_BE_PARSED_AS_A_NUMBER = "Your input cannot be parsed as a number!";
    private static final String PLEASE_INPUT_THE_NUMBER_OF_TABLES_TO_CREATE = "Please input the number of tables to create";
    private static String WAITER_IDS_CAPTION = "Waiter-IDs of order %d";
    private static String PIZZAS_CAPTION = "Pizzas of order %d";
    private static String FREE_TABLES = "There are currently %d free table(s)";

    private TablesCreatedHandler onTablesCreatedHandler;

    public PizzeriaFrame() {
        super("Pizzeria");
    }

    public PizzeriaFrame(String id) {
        super("Pizzeria: " + id);
    }

    public void start() {


        JPanel wrapper = new JPanel();
        GridLayout wrapperLayout = new GridLayout(3, 2);
        wrapper.setLayout(wrapperLayout);

        // Create the tables overview
        initTablesOverview(wrapper);

        // Create the waiting groups table overview
        initWaitingGroupsOverview(wrapper);

        // Create the overview table of deliveries
        JTable deliveryTable = initDeliveryOverview(wrapper);

        // Create the details table for the deliveries containing the pizza, waiter and driver information
        initDeliveryDetails(wrapper, deliveryTable);

        // Create the overview table of the orders
        JTable ordersTable = initOrdersOverview(wrapper);

        // Create the order detail tables
        initOrdersDetails(wrapper, ordersTable);

        setContentPane(wrapper);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
        this.setMaximizedBounds(env.getMaximumWindowBounds());
        this.setExtendedState(this.getExtendedState() | this.MAXIMIZED_BOTH);

        initModels();
    }

    private int getNumberOfTables() {
        int numberOfTables;
        String numberOfTablesInput = JOptionPane.showInputDialog(PLEASE_INPUT_THE_NUMBER_OF_TABLES_TO_CREATE);
        try {
            numberOfTables = Integer.parseInt(numberOfTablesInput);
        } catch (NumberFormatException e) {
            JOptionPane.showMessageDialog(this, YOUR_INPUT_CANNOT_BE_PARSED_AS_A_NUMBER, WRONG_INPUT,
                    JOptionPane.ERROR_MESSAGE);
            return getNumberOfTables();
        }

        if (numberOfTables < 0) {
            JOptionPane.showMessageDialog(this, THE_NUMBER_MUST_BE_GREATER_OR_EQUAL_TO_1, WRONG_INPUT,
                    JOptionPane.ERROR_MESSAGE);
            return getNumberOfTables();
        }

        return numberOfTables;
    }

    private void initModels() {
        PizzeriaAgent.getInstance().getOrdersModel().setItems(new ArrayList<GroupData>());

        // When the tables get created in the first place, the handler is informed
        int numberOfTables = getNumberOfTables();
        List<Table> freeTablesCreated = PizzeriaAgent.getInstance().getTablesModel().createFreeTables(numberOfTables);
        onTablesCreatedHandler.freeTablesCreated(freeTablesCreated);

    }

    private void initDeliveryDetails(JPanel wrapper, JTable deliveryTable) {
        JPanel detailWrapper = new JPanel();
        GridLayout wrapperLayout = new GridLayout(2, 1);
        detailWrapper.setLayout(wrapperLayout);

        final JPanel detailsPanel = new JPanel();
        final JPanel pizzasPanel = new JPanel();
        Util.createTableInTitledPanel(detailsPanel, PizzeriaAgent.getInstance().getDeliveryDetailsModel(), "");
        Util.createTableInTitledPanel(pizzasPanel, PizzeriaAgent.getInstance().getPizzasOfDeliveryModel(), "");

        /**
         * Update the displayed order in the waiter and pizza detail tables when an
         * order gets selected in the orders table
         */
        final DeliveryOrdersModel deliveryOrdersModel = PizzeriaAgent.getInstance().getDeliveryOrdersModel();
        deliveryTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
            @Override
            public void valueChanged(ListSelectionEvent e) {
                ListSelectionModel lsm = (ListSelectionModel) e.getSource();
                int minIndex = lsm.getMinSelectionIndex();
                if (minIndex < 0) return;

                DeliveryGroupData currentGroup = deliveryOrdersModel.getGroupOfRow(minIndex);
                final Order order = currentGroup.getOrder();
                detailsPanel.setBorder(new TitledBorder(String.format(WAITER_IDS_CAPTION, order.getId())));
                pizzasPanel.setBorder(new TitledBorder(String.format(PIZZAS_CAPTION, order.getId())));
                PizzeriaAgent.getInstance().getDeliveryDetailsModel().setCurrentDelivery(currentGroup);
                PizzeriaAgent.getInstance().getPizzasOfDeliveryModel().setCurrentOrder(order);

                detailsPanel.setVisible(true);
                pizzasPanel.setVisible(true);
            }
        });

        deliveryOrdersModel.addTableModelListener(new TableModelListener() {
            @Override
            public void tableChanged(TableModelEvent e) {
                final PizzasOfOrderModel pizzasOfDeliveryModel = PizzeriaAgent.getInstance().getPizzasOfDeliveryModel();
                final DeliveryDetailsModel deliveryDetailsModel = PizzeriaAgent.getInstance().getDeliveryDetailsModel();

                final Order currentOrder = pizzasOfDeliveryModel.getCurrentOrder();
                if (currentOrder == null) return;
                final DeliveryGroupData groupOfRow = deliveryOrdersModel.getHasOrderById(currentOrder.getGroupId());

                pizzasOfDeliveryModel.setCurrentOrder(groupOfRow.getOrder());
                deliveryDetailsModel.setCurrentDelivery(groupOfRow);

                pizzasOfDeliveryModel.fireTableDataChanged();
                deliveryDetailsModel.fireTableDataChanged();
            }
        });

        detailWrapper.add(detailsPanel);
        detailWrapper.add(pizzasPanel);
        detailsPanel.setVisible(false);
        pizzasPanel.setVisible(false);

        wrapper.add(detailWrapper);

    }

    private void initOrdersDetails(JPanel wrapper, JTable ordersTable) {
        JPanel detailWrapper = new JPanel();
        GridLayout wrapperLayout = new GridLayout(2, 1);
        detailWrapper.setLayout(wrapperLayout);

        final JPanel waitersPanel = new JPanel();
        final JPanel pizzasPanel = new JPanel();
        Util.createTableInTitledPanel(waitersPanel, PizzeriaAgent.getInstance().getWaitersModel(), "");
        Util.createTableInTitledPanel(pizzasPanel, PizzeriaAgent.getInstance().getPizzasOfOrderModel(), "");

        /**
         * Update the displayed order in the waiter and pizza detail tables when an
         * order gets selected in the orders table
         */
        ordersTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
            @Override
            public void valueChanged(ListSelectionEvent e) {
                ListSelectionModel lsm = (ListSelectionModel) e.getSource();
                int minIndex = lsm.getMinSelectionIndex();
                if (minIndex < 0) return;

                GroupData currentGroup = PizzeriaAgent.getInstance().getOrdersModel().getGroupOfRow(minIndex);
                waitersPanel.setBorder(new TitledBorder(String.format(WAITER_IDS_CAPTION, currentGroup.getOrder().getId())));
                pizzasPanel.setBorder(new TitledBorder(String.format(PIZZAS_CAPTION, currentGroup.getOrder().getId())));
                PizzeriaAgent.getInstance().getWaitersModel().setCurrentGroup(currentGroup);
                PizzeriaAgent.getInstance().getPizzasOfOrderModel().setCurrentOrder(currentGroup.getOrder());

                waitersPanel.setVisible(true);
                pizzasPanel.setVisible(true);
            }
        });
		
		/* The PizzaModel and WaitersModel and their tables only show the selected entry in the orders table
		   When the table model of the orders model changes the PizzaModel and WaitersModel have to update, too */
        final OrdersOverviewModel ordersModel = PizzeriaAgent.getInstance().getOrdersModel();
        ordersModel.addTableModelListener(new TableModelListener() {
            @Override
            public void tableChanged(TableModelEvent e) {
                final PizzasOfOrderModel pizzasOfOrderModel = PizzeriaAgent.getInstance().getPizzasOfOrderModel();
                final WaitersOfOrderModel waitersModel = PizzeriaAgent.getInstance().getWaitersModel();

                final Order currentOrder = pizzasOfOrderModel.getCurrentOrder();
                if (currentOrder == null) return;
                final int groupId = currentOrder.getGroupId();
                final GroupData groupOfRow = ordersModel.getHasOrderById(currentOrder.getGroupId());

                pizzasOfOrderModel.setCurrentOrder(groupOfRow.getOrder());
                waitersModel.setCurrentGroup(groupOfRow);

                pizzasOfOrderModel.fireTableDataChanged();
                waitersModel.fireTableDataChanged();
            }
        });

        detailWrapper.add(waitersPanel);
        detailWrapper.add(pizzasPanel);
        waitersPanel.setVisible(false);
        pizzasPanel.setVisible(false);

        wrapper.add(detailWrapper);

    }

    private JTable initOrdersOverview(JPanel wrapper) {
        JPanel tablePanel = new JPanel();
        JTable orderTable = Util.createTableInTitledPanel(tablePanel, PizzeriaAgent.getInstance().getOrdersModel(), "Orders");

        wrapper.add(tablePanel);

        return orderTable;
    }

    private JTable initDeliveryOverview(JPanel wrapper) {
        JPanel deliveryPanel = new JPanel();
        JTable deliveryTable = Util.createTableInTitledPanel(deliveryPanel, PizzeriaAgent.getInstance().getDeliveryOrdersModel(), "Delivery Orders");

        wrapper.add(deliveryPanel);

        return deliveryTable;
    }

    private void initWaitingGroupsOverview(JPanel wrapper) {
        JPanel tablePanel = new JPanel();
        Util.createTableInTitledPanel(tablePanel, PizzeriaAgent.getInstance().getGroupModel(), "Waiting groups");

        wrapper.add(tablePanel);
    }

    private void initTablesOverview(JPanel wrapper) {
        JPanel scrollPanePanel = new JPanel(new GridBagLayout());
        final JLabel freeTablesLabel = new JLabel(getNumberOfFreeTables());
        scrollPanePanel.add(freeTablesLabel, new GridBagConstraints());
        Util.createTableInTitledPanel(scrollPanePanel, PizzeriaAgent.getInstance().getTablesModel(), "Tables");

        PizzeriaAgent.getInstance().getTablesModel()
                .addTableModelListener(new TableModelListener() {
                    @Override
                    public void tableChanged(TableModelEvent e) {
                        String numberOfFreeTables = getNumberOfFreeTables();
                        log.info("Number of free tables changed: {}",
                                numberOfFreeTables);

                        freeTablesLabel.setText(numberOfFreeTables);
                    }
                });


        wrapper.add(scrollPanePanel);
    }


    private String getNumberOfFreeTables() {
        return String.format(FREE_TABLES, PizzeriaAgent.getInstance().getTablesModel().getNumberOfFreeTables());
    }

    public void setOnTablesCreatedHandler(PizzeriaAgent.TablesCreatedHandler tablesCreatedHandler) {
        onTablesCreatedHandler = tablesCreatedHandler;
    }

}
