package tmcsim.client.cadclientgui.screens;

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.rmi.RemoteException;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.JSeparator;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.RowSorter.SortKey;
import javax.swing.SwingUtilities;
import javax.swing.TransferHandler;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;
import tmcsim.client.cadclientgui.enums.CADDataEnums;
import tmcsim.client.cadclientgui.enums.CADScriptTags.UNIT_TAGS;
import tmcsim.client.cadclientgui.enums.IncidentEnums;
import tmcsim.client.cadclientgui.enums.TableHeaders;
import tmcsim.client.cadclientgui.enums.UnitStatusEnums;

/**
 * This class contains the view and controller for the AssignedIncidents screen.
 * The view was not built using a GUI builder plug-in (but may want to consider
 * doing so in the future), and the controller uses listeners to control how the
 * view and data act.
 *
 * @author Vincent
 */
public class AssignedIncidents extends JFrame
{
    private final String SCREEN_TITLE = "(Shift + F3)  Assigned Incidents";
    private final Dimension SCREEN_DIMENSIONS = new Dimension(1400, 250);
    private final Dimension DROP_DOWN_MENU_LABEL_DIMENSIONS = new Dimension(
            170, 20);
    private final Dimension DROP_DOWN_MENU_DIMENSIONS = new Dimension(170, 230);
    private final int COLUMN_WIDTH = 120;
    private final String[] LABELS =
    {
        "Add Resources...", "Greater Alarm...",
        "Reconfigure", "Open", "Recall Incident", "Cancel", "Reassign",
        "Link/Append", "Map", "Recall Linked Incidents", "Read Notes",
        "Page...", "Mail...", "Fax..."
    };
    private final String LABEL_SPACING = "     ";
    private JTable assignedIncidentsTable;
    private JFrame assignedIncidentsMenu;
    // labels for the drop down menu
    private JLabel[] dropDownLabels = new JLabel[LABELS.length];
    private static Logger clientLogger = Logger.getLogger("tmcsim.client");
    private long lastLeftClick;// used for double clicking feature

    public AssignedIncidents()
    {
        initComponents();
    }

    private void initComponents()
    {
        initializeTable();
        initController();
        initializeDropDownMenu();

        JScrollPane scrollpane = new JScrollPane(assignedIncidentsTable);
        scrollpane.getViewport().setBackground(Color.WHITE);

        setTitle(SCREEN_TITLE);
        setPreferredSize(SCREEN_DIMENSIONS);
        getContentPane().add(scrollpane);
        setResizable(true);
        pack();
        Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
        setLocation(0, (int) (dim.getHeight() / 4));
        setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
        setVisible(false);
    }

    /*
     * Initializes the table and prepares the cell renderer for color
     * management. It initializes the default settings and handles the drag and
     * drop feature.
     */
    private void initializeTable()
    {
        assignedIncidentsTable = new JTable()
        {
            /*
             * Custom renderer to set different background/foreground colors
             * @see javax.swing.JTable#prepareRenderer(javax.swing.table.TableCellRenderer, int, int)
             */
            public Component prepareRenderer(TableCellRenderer renderer, int row, int column)
            {
                Component comp;
                // In a rare odd circumstance of timing, this exception is thrown:
                // http://pastebin.com/A3tZe07a
                try
                {
                    comp = super.prepareRenderer(renderer, row, column);
                } catch (Exception ex)
                {
                    clientLogger.logp(Level.INFO, "AssignedIncidents",
                            "prepareRenderer", "row=" + row + " col=" + column, ex);

                    // Our workaround is to just return a dummy value.
                    // It will be erased in the next one second refresh
                    return new JLabel("?");
                }

                comp.setForeground(Color.BLACK);
                comp.setBackground(Color.CYAN);
                if (assignedIncidentsTable.getModel().getColumnName(column).equals("Unit/s"))
                {//4 is the column for "Unit/s"
                    //System.out.println("Diagnostic: in AssignedIncidents.prepareRenderer()"); //Commenting this line breaks the client
                    comp.setBackground(Color.BLACK);
                    int primaryColumn = 3;
                    try
                    {
                        // get the unit name from the table   JD
                        String unitname = (String) assignedIncidentsTable.getValueAt(row, primaryColumn);
                        // Validate that unitname isn't blank
                        if (!unitname.equals(""))
                        {
                            // Fetch the unit's current status from server
                            UnitStatusEnums ustatus = ScreenManager.theCoordinator.getCadDataUnitStatus(unitname);
                            // Decide which color to use for displaying the status
                            switch (ustatus)
                            {
                                case Assignable:
                                    comp.setForeground(Color.GREEN);
                                    break;
                                case Arrived:
                                    comp.setForeground(Color.YELLOW);
                                    break;
                                case Enroute:
                                    comp.setForeground(Color.CYAN);
                                    break;
                            }
                        }
                    } catch (RemoteException e)
                    {
                        e.printStackTrace();
                    }
                }

                if (getSelectedRow() == row)
                {
                    comp.setForeground(Color.WHITE);
                    comp.setBackground(Color.BLUE);
                }

                return comp;
            }

            public Class getColumnClass(int c)
            {
                return getValueAt(0, c).getClass();
            }
        };

        assignedIncidentsTable.setOpaque(true);
        assignedIncidentsTable.setIntercellSpacing(new Dimension(1, 0));
        assignedIncidentsTable.setGridColor(Color.WHITE);
        assignedIncidentsTable.getTableHeader().setReorderingAllowed(false);
        assignedIncidentsTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        assignedIncidentsTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
        assignedIncidentsTable.setAutoCreateRowSorter(true);
        assignedIncidentsTable.setModel(new DefaultTableModel());

        ((DefaultTableModel) assignedIncidentsTable.getModel()).setColumnIdentifiers(TableHeaders.ASSIGNED_INCIDENTS_HEADERS);

        assignedIncidentsTable.setTransferHandler(new TransferHandler()
        {
            public boolean canImport(TransferHandler.TransferSupport info)
            {
                // Check for String flavor
                if (!info.isDataFlavorSupported(DataFlavor.stringFlavor))
                {
                    return false;
                }
                return true;
            }

            public boolean importData(TransferHandler.TransferSupport info)
            {
                if (!info.isDrop())
                {
                    return false;
                }

                DefaultTableModel tableModel = (DefaultTableModel) assignedIncidentsTable.getModel();
                JTable.DropLocation dl = (JTable.DropLocation) info.getDropLocation();
                int index = dl.getRow();

                // Get the string that is being dropped.
                Transferable t = info.getTransferable();
                String data;
                try
                {
                    data = (String) t.getTransferData(DataFlavor.stringFlavor);
                } catch (Exception e)
                {
                    return false;
                }

                // Perform the actual import
                int incidentId = (Integer) assignedIncidentsTable.getValueAt(dl.getRow(), 0);
                try
                {
                    ScreenManager.theCoordinator.setCadDataUnitAssignedId(data, incidentId);
                    ScreenManager.theCoordinator.setCadDataUnitValue(data,
                            UNIT_TAGS.UNIT_STATUS, UnitStatusEnums.Arrived);
                    ScreenManager.theCoordinator.addCadDataIncidentAssignedUnitNum(incidentId, data);
                    ScreenManager.theCoordinator.setCadDataIncidentStatus(incidentId, IncidentEnums.Assigned);
                } catch (RemoteException e)
                {
                    e.printStackTrace();
                }
                ScreenManager.refreshScreens();
                return true;
            }
        });

        for (int i = 0; i < assignedIncidentsTable.getColumnCount(); i++)
        {
            assignedIncidentsTable.getColumnModel().getColumn(i).setPreferredWidth(120);
        }
    }

    /*
     * Adds the key and mouse listeners for the table and component listener for
     * screen.
     */
    private void initController()
    {
        assignedIncidentsTable.addMouseListener(new MouseListener()
        {
            public void mouseClicked(MouseEvent e)
            {
                if (SwingUtilities.isLeftMouseButton(e))
                {
                    // TODO:  Use e.getClickCount() == 2
                    if (System.currentTimeMillis() - lastLeftClick < 1000)
                    {
                        int idColumn = 0;
                        int selectedRow = assignedIncidentsTable.getSelectedRow();
                        try
                        {
                            int selectedValue = (Integer) assignedIncidentsTable.getValueAt(selectedRow, idColumn);
                            ScreenManager.openIncidentViewer(selectedValue);
                        } catch (IndexOutOfBoundsException ex)
                        {
                            ex.printStackTrace();
                        }
                    }
                    else
                    {
                        lastLeftClick = System.currentTimeMillis();
                    }
                }
                if (SwingUtilities.isRightMouseButton(e))
                {
                    // Fixed to force right click to cause the row to be selected. JD
                    // get the coordinates of the mouse click
                    Point p = e.getPoint();
                    // get the row index that contains that coordinate
                    int rowNumber = assignedIncidentsTable.rowAtPoint(p);
                    // Get the ListSelectionModel of the JTable
                    ListSelectionModel model = assignedIncidentsTable.getSelectionModel();
                    // set the selected interval of rows. Using the "rowNumber"
                    // variable for the beginning and end selects only that one row.
                    model.setSelectionInterval(rowNumber, rowNumber);
                    // go open the drop down menu
                    openDropDownMenu(e);
                }
                else
                {
                    closeDropDownMenu();
                }
            }

            public void mouseEntered(MouseEvent e)
            {
            }

            public void mouseExited(MouseEvent e)
            {
            }

            public void mousePressed(MouseEvent e)
            {
            }

            public void mouseReleased(MouseEvent e)
            {
            }
        });

        addComponentListener(new ComponentListener()
        {
            public void componentHidden(ComponentEvent e)
            {
            }

            public void componentMoved(ComponentEvent e)
            {
                closeDropDownMenu();
            }

            public void componentResized(ComponentEvent e)
            {
            }

            public void componentShown(ComponentEvent e)
            {
            }
        });
    }

    /*
     * Creates the drop down menu that appears when a right click is performed
     * on the table.
     */
    private void initializeDropDownMenu()
    {
        Box menu = new Box(BoxLayout.Y_AXIS);
        initializeDropDownLabels();
        addLabelsToBox(menu);

        // Sets the highlighted background color, note that it does not become
        // "Highlighted" until opaque(true) is called
        setMenuHighlightedBackground(Color.BLUE);

        assignedIncidentsMenu = new JFrame();
        assignedIncidentsMenu.getContentPane().add(menu);
        assignedIncidentsMenu.setPreferredSize(DROP_DOWN_MENU_DIMENSIONS);
        assignedIncidentsMenu.setUndecorated(true);
        assignedIncidentsMenu.pack();
        assignedIncidentsMenu.setVisible(false);
    }

    /*
     * Sets the text and size and adds a listener to each activated label.
     */
    private void initializeDropDownLabels()
    {
        for (int i = 0; i < dropDownLabels.length; i++)
        {
            dropDownLabels[i] = new JLabel(LABEL_SPACING + LABELS[i]);
            dropDownLabels[i].setMaximumSize(DROP_DOWN_MENU_LABEL_DIMENSIONS);
            dropDownLabels[i].setForeground(Color.GRAY);
        }
        dropDownLabels[3].setForeground(Color.BLACK);
        addMouseListenersToLabel(dropDownLabels[3]);
        dropDownLabels[10].setForeground(Color.BLACK);
        addMouseListenersToLabel(dropDownLabels[10]);
    }

    /*
     * Add the labels to the box in order with separators and spacings in
     * between.
     */
    private void addLabelsToBox(Box menu)
    {
        menu.add(dropDownLabels[0]);
        menu.add(dropDownLabels[1]);
        menu.add(dropDownLabels[2]);

        menu.add(Box.createVerticalStrut(5));
        menu.add(new JSeparator());
        menu.add(Box.createVerticalStrut(5));

        menu.add(dropDownLabels[3]);
        menu.add(dropDownLabels[4]);
        menu.add(dropDownLabels[5]);
        menu.add(dropDownLabels[6]);
        menu.add(dropDownLabels[7]);

        menu.add(Box.createVerticalStrut(5));
        menu.add(new JSeparator());
        menu.add(Box.createVerticalStrut(5));

        menu.add(dropDownLabels[8]);
        menu.add(dropDownLabels[9]);
        menu.add(dropDownLabels[10]);

        menu.add(Box.createVerticalStrut(5));
        menu.add(new JSeparator());
        menu.add(Box.createVerticalStrut(5));

        menu.add(dropDownLabels[11]);
        menu.add(dropDownLabels[12]);
        menu.add(dropDownLabels[13]);

        menu.add(Box.createVerticalStrut(5));
    }

    /*
     * Sets the highlighted color(when the mouse is over it) of the JLabels.
     * Note: the color is not shown until .setOpaque(true) is called.
     *
     * @param color the highlighted color
     */
    public void setMenuHighlightedBackground(Color color)
    {
        for (int i = 0; i < dropDownLabels.length; i++)
        {
            dropDownLabels[i].setBackground(color);
        }
    }

    /*
     * Sets all JLabels to not display a highlighted background
     */
    public void unSelectAllLabels()
    {
        for (int i = 0; i < dropDownLabels.length; i++)
        {
            dropDownLabels[i].setOpaque(false);
        }
    }

    /*
     * Sets the label to have a highlighted background.
     *
     * @param label the label that is selected/highlighted
     */
    public void selectLabel(Object label)
    {
        ((JLabel) label).setOpaque(true);
    }

    /*
     * Performs the label action depending on which label was clicked.
     */
    public void performLabelAction(Object label)
    {
        if (label.equals(dropDownLabels[0]))
        {// Add Resources
        }
        else if (label.equals(dropDownLabels[1]))
        {// Greater Alarm
        }
        else if (label.equals(dropDownLabels[2]))
        {// Reconfigure
        }
        else if (label.equals(dropDownLabels[3]))
        {// Open
            int idColumn = 0;
            int selectedRow = assignedIncidentsTable.getSelectedRow();
            try
            {
                int selectedValue = (Integer) assignedIncidentsTable.getValueAt(selectedRow, idColumn);
                ScreenManager.openIncidentViewer(selectedValue);
            } catch (IndexOutOfBoundsException ex)
            {
                ex.printStackTrace();
            }
        }
        else if (label.equals(dropDownLabels[4]))
        {// Recall Incident
        }
        else if (label.equals(dropDownLabels[5]))
        {// Cancel
        }
        else if (label.equals(dropDownLabels[6]))
        {// Reassign
        }
        else if (label.equals(dropDownLabels[7]))
        {// Link append
        }
        else if (label.equals(dropDownLabels[8]))
        {// Map
        }
        else if (label.equals(dropDownLabels[9]))
        {// Recall Linked Incidents
        }
        else if (label.equals(dropDownLabels[10]))
        {// Read Notes
            int idColumn = 0;
            ScreenManager.openIncidentViewer((Integer) assignedIncidentsTable
                    .getValueAt(assignedIncidentsTable.getSelectedRow(),
                    idColumn));
        }
        else if (label.equals(dropDownLabels[11]))
        {// Page
        }
        else if (label.equals(dropDownLabels[12]))
        {// Mail
        }
        else if (label.equals(dropDownLabels[13]))
        {// Fax
        }
    }

    /*
     * Factory method. Adds a mouse listeners to the label. The
     * MouseMotionListener detects the mouse's location to highlight the label.
     * The MouseListener detects for clicks and performs the action of the label
     * designates.
     */
    public void addMouseListenersToLabel(JLabel label)
    {
        label.addMouseMotionListener(new MouseMotionListener()
        {
            public void mouseDragged(MouseEvent e)
            {
            }

            public void mouseMoved(MouseEvent e)
            {
                unSelectAllLabels();
                selectLabel(e.getSource());
                assignedIncidentsMenu.revalidate();
                assignedIncidentsMenu.repaint();
            }
        });
        label.addMouseListener(new MouseListener()
        {
            public void mouseClicked(MouseEvent e)
            {
                performLabelAction(e.getSource());
                unSelectAllLabels();
                assignedIncidentsMenu.revalidate();
                assignedIncidentsMenu.repaint();
                assignedIncidentsMenu.setVisible(false);
            }

            public void mouseEntered(MouseEvent e)
            {
            }

            public void mouseExited(MouseEvent e)
            {
            }

            public void mousePressed(MouseEvent e)
            {
            }

            public void mouseReleased(MouseEvent e)
            {
            }
        });
    }

    /*
     * Displays the menu where the right click occurred.
     */
    public void openDropDownMenu(MouseEvent e)
    {
        assignedIncidentsMenu.setLocation(e.getX() + this.getX(), e.getY()
                + this.getY());
        assignedIncidentsMenu.setVisible(true);
    }

    /*
     * Hides the menu.
     */
    public void closeDropDownMenu()
    {
        unSelectAllLabels();
        assignedIncidentsMenu.revalidate();
        assignedIncidentsMenu.repaint();
        assignedIncidentsMenu.setVisible(false);
    }

    /*
     * Refreshes the data in the table by updating all data and repainting the
     * screen. It saves user preferences(like column sizes, selected row, sorted preferences)
     * and applies them to the updated model it receives from the server.
     */
    public void refreshTable()
    {

        if (assignedIncidentsTable.getTableHeader().getResizingColumn() == null)
        {//only update info if resize not in progress
            try
            {
                int index = assignedIncidentsTable.getSelectedRow();
                int[] columnWidths = new int[20];
                List<? extends SortKey> keys = assignedIncidentsTable.getRowSorter().getSortKeys();
                for (int i = 0; i < assignedIncidentsTable.getColumnCount(); i++)
                {
                    columnWidths[i] = assignedIncidentsTable.getColumnModel().getColumn(i).getWidth();
                }

                assignedIncidentsTable.setModel(ScreenManager.theCoordinator.getCadDataTable(CADDataEnums.TABLE.ASSIGNED_INCIDENTS));

                for (int i = 0; i < assignedIncidentsTable.getColumnCount(); i++)
                {
                    assignedIncidentsTable.getColumnModel().getColumn(i).setPreferredWidth(columnWidths[i]);
                }
                assignedIncidentsTable.getRowSorter().setSortKeys(keys);
                assignedIncidentsTable.getSelectionModel().setSelectionInterval(index, index);
            } catch (RemoteException e)
            {
                e.printStackTrace();
            }
            revalidate();
            repaint();
        }
    }

    /*
     * Makes screen visible.
     */
    public void open()
    {
        setVisible(true);
    }

    /*
     * Hides screen.
     */
    public void close()
    {
        setVisible(false);
    }
}
