package tmcsim.cadsimulator.viewer;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.border.EtchedBorder;
import javax.swing.border.TitledBorder;
import tmcsim.common.CADEnums.PARAMICS_STATUS;
import tmcsim.common.CADEnums.SCRIPT_STATUS;

/**
 * SimulationStatusPanel is a GUI object used for displaying information for the
 * current simulation. This information includes:
 *
 * <ul>
 * <li>Current simulation time.</li>
 * <li>Current simulation status.</li>
 * <li>Number of remote CAD Clients connected.</li>
 * <li>Status of Simulation Manager connection.</li>
 * <li>Status of Paramics connection.</li>
 * <li>Paramics Network Loaded</li>
 * <li>Information log messages.</li>
 * <li>Error log messages</li>
 * </ul>
 *
 * @author Matthew Cechini
 * @version
 */
@SuppressWarnings("serial")
public class SimulationStatusPanel extends JPanel
{

    /**
     * Logging Handler to listen for Information and Error messages logged for
     * the CAD Simulator. Received LogRecords are displayed in the info or error
     * message Text Area.
     *
     * @author Matthew Cechini
     */
    private class SimulatorErrorHandler extends Handler
    {

        public void close() throws SecurityException
        {
        }

        public void flush()
        {
        }

        public void publish(LogRecord rec)
        {
            StringBuffer msgBuffer = new StringBuffer();

            msgBuffer.append(rec.getSourceClassName() + "."
                    + rec.getSourceMethodName() + " = "
                    + rec.getMessage());

            if (rec.getLevel() == Level.INFO)
            {
                infoMessagesTA.setText(infoMessagesTA.getText()
                        + msgBuffer.toString() + "\n");
            } else
            {
                errorMessagesTA.setText(errorMessagesTA.getText()
                        + msgBuffer.toString() + "\n");
            }
        }
    }
    /**
     * Count of how many CAD clients have connected.
     */
    private int numClientsConnected = 0;
    /**
     * Logging ErrorHandler.
     */
    private SimulatorErrorHandler errorHandler;

    /**
     * Constructor. Initialize GUI Objects. Register the logging handler to
     * listen for log records from all loggers that exist in the
     * "tmcsim.cadsimulator" package structure.
     */
    public SimulationStatusPanel()
    {

        initTimeAndStatus();
        initAdditionalInfo();
        initMessagesPanes();

        errorHandler = new SimulatorErrorHandler();
        Logger.getLogger("tmcsim.cadsimulator").addHandler(errorHandler);

        CADSimulatorViewerBox = Box.createVerticalBox();
        CADSimulatorViewerBox.add(simulationTimeAndStatusBox);
        CADSimulatorViewerBox.add(additionalInfoBox);
        CADSimulatorViewerBox.add(infoMessagesPane);
        CADSimulatorViewerBox.add(errorMessagesPane);

        add(CADSimulatorViewerBox);
    }

    /**
     * Method is called when a CAD Client disconnects from the CAD Simulator.
     * The displayed number of connected clients is incremented by one.
     */
    public void connectClient()
    {

        numClientsConnected++;

        termConnectedTF.setText(String.valueOf(numClientsConnected));
    }

    /**
     * Method is called when a CAD Client disconnects from the CAD Simulator.
     * The displayed number of connected clients is decremented by one.
     */
    public void disconnectClient()
    {

        if (numClientsConnected > 0)
        {
            numClientsConnected--;
        }

        termConnectedTF.setText(String.valueOf(numClientsConnected));
    }

    /**
     * Method is called when Simulation Manager connects or disconnects.
     *
     * @param connection True if simulation manager is connected, false if not.
     */
    public void setSimManagerStatus(boolean connection)
    {

        if (connection)
        {
            managerConnectedTF.setText("Yes");
        } else
        {
            managerConnectedTF.setText("No");
        }

    }

    /**
     * Method called to convert current simulation time (parameter long value)
     * to a string of format H:MM:SS. Time is then updated on GUI.
     *
     * @param seconds Long value of current time
     */
    public void setTime(long seconds)
    {
        String time = new String();
        long timeSegment;

        timeSegment = seconds / 3600;
        time += String.valueOf(timeSegment) + ":";

        seconds = seconds % 3600;

        timeSegment = seconds / 60;
        if (timeSegment < 10)
        {
            time += "0";
        }

        time += String.valueOf(timeSegment) + ":";
        seconds = seconds % 60;

        timeSegment = seconds;
        if (timeSegment < 10)
        {
            time += "0";
        }

        time += String.valueOf(timeSegment);

        simulationClockLabel.setText(time);

    }

    /**
     * This method is called within the CADSimulator whenever an error occurs.
     * The message is then displayed to the user in the "Error Messages" portion
     * of the CAD Simulator Viewer. Invoke method with null parameter to clear
     * messages.
     *
     * @param errorMessage String message that will be displayed
     */
    protected void displayError(String errorMessage)
    {
        if (errorMessage == null)
        {
            errorMessagesTA.setText("");
        } else
        {
            errorMessagesTA.append(errorMessage + "\n");
        }
    }

    /**
     * Method is called to display the current status of the simulation.
     *
     * @param newStatus Current status of simulation. The following table
     * describes each possible status and what is displayed. Each status code is
     * found as a public static int in the Coordinator Class.
     *
     * <table cellpadding="2" cellspacing="2" border="1" style="text-align:
     * left; width: 250px;">
     * <tbody>
     * <tr>
     * <th>Status<br></th>
     * <th>Actions Taken<br></th>
     * </tr>
     * <tr>
     * <td>NO_SCRIPT<br></td>
     * <td>Set the simulation status text to a black "No Script". <br></td>
     * </tr>
     * <tr>
     * <td>SCRIPT_STOPPED_NOT_STARTED<br></td>
     * <td>Set the simulation status text to a red "Ready". <br></td>
     * </tr>
     * <tr>
     * <td>SCRIPT_PAUSED_STARTED<br></td>
     * <td>Set the simulation status text to a red "Paused". <br></td>
     * </tr>
     * <tr>
     * <td>SCRIPT_RUNNING<br></td>
     * <td>Set the simulation status text to a green "Running". <br></td>
     * </tr>
     * <tr>
     * <td>ATMS_SYNCHRONIZATION<br></td>
     * <td>Set the simulation status text to an orange "Synchronizing".
     * <br></td>
     * </tr>
     * </tbody>
     * </table>
     */
    public void setScriptStatus(SCRIPT_STATUS newStatus)
    {

        switch (newStatus)
        {
            case NO_SCRIPT:
                simulationStatusText.setText("No Script");
                simulationStatusText.setForeground(Color.BLACK);
                break;

            case SCRIPT_STOPPED_NOT_STARTED:
                simulationStatusText.setText("Ready");
                simulationStatusText.setForeground(Color.RED);
                break;

            case SCRIPT_PAUSED_STARTED:
                simulationStatusText.setText("Paused");
                simulationStatusText.setForeground(Color.RED);
                break;

            case SCRIPT_RUNNING:
                simulationStatusText.setText("Running");
                simulationStatusText.setForeground(Color.GREEN);
                break;
            case ATMS_SYNCHRONIZATION:
                simulationStatusText.setText("Synchronizing");
                simulationStatusText.setForeground(Color.ORANGE);
                break;

        }
    }

    /**
     * Method is called when a connection to paramics is made or dropped.
     *
     * @param newStatus The status denoting whether a connection has been made
     * or dropped.
     */
    public void setParamicsStatus(PARAMICS_STATUS newStatus)
    {

        switch (newStatus)
        {
            case CONNECTED:
                paramicsConnectedTF.setText("Yes");
                break;
            case DISCONNECTED:
                paramicsConnectedTF.setText("No");
                break;
        }
    }

    /**
     * Method is called when a paramics network is loaded.
     *
     * @param networkID Unique ID for Paramics network that has been loaded.
     */
    public void setParamicsNetworkLoaded(String networkID)
    {
        networkLoadedTF.setText(networkID);
    }

    /**
     * Initialize Time and Status GUI Components
     */
    private void initTimeAndStatus()
    {

        simulationTime = new JPanel();
        simulationClock = new JPanel();
        simulationStatus = new JLabel("Simulation Status");
        simulationStatusText = new JLabel("No Script");

        simulationTime.setLayout(new BorderLayout());
        simulationClock.setPreferredSize(new Dimension(100, 60));
        simulationTimeAndStatusBox = new Box(BoxLayout.X_AXIS);
        simulationStatusBox = new Box(BoxLayout.Y_AXIS);
        simulationTimeBox = new Box(BoxLayout.Y_AXIS);
        simulationClockBox = new Box(BoxLayout.X_AXIS);

        simulationStatus.setAlignmentX(Box.CENTER_ALIGNMENT);
        simulationStatusText.setAlignmentX(Box.CENTER_ALIGNMENT);
        simulationStatusText.setName("simulationStatusText");

        TitledBorder title = BorderFactory.createTitledBorder(
                BorderFactory.createEtchedBorder(EtchedBorder.LOWERED), "Status");
        title.setTitleJustification(TitledBorder.LEFT);
        simulationStatusBox.setBorder(title);

        simulationStatusBox.setMaximumSize(new Dimension(140, 60));
        simulationStatusBox.setAlignmentX(Box.CENTER_ALIGNMENT);

        simulationStatusBox.add(Box.createHorizontalStrut(120));
        simulationStatusBox.add(Box.createVerticalGlue());
        simulationStatusBox.add(simulationStatusText);
        simulationStatusBox.add(Box.createVerticalGlue());

        simulationClockLabel = new JLabel("0:00:00");
        simulationClockLabel.setFont(new Font("Geneva", Font.BOLD, 70));
        simulationClockLabel.setName("simulationClockLabel");
        simulationClockLabel.setForeground(Color.BLACK);
        simulationClockLabel.setBackground(Color.BLACK);
        simulationClockBox.setForeground(Color.BLACK);
        simulationClockBox.setBackground(Color.BLACK);
        simulationClockBox.add(simulationClockLabel);
        simulationClockBox.setAlignmentX(Box.CENTER_ALIGNMENT);
        simulationTimeBox.add(simulationClockBox);

        simulationTimeAndStatusBox.add(Box.createHorizontalStrut(20));
        simulationTimeAndStatusBox.add(simulationTimeBox);
        simulationTimeAndStatusBox.add(Box.createHorizontalStrut(20));
        simulationTimeAndStatusBox.add(simulationStatusBox);
        simulationTimeAndStatusBox.add(Box.createHorizontalStrut(20));
    }

    /**
     * Initialize Additional Info Label GUI Components
     */
    private void initAdditionalInfo()
    {

        terminalsConnectedLabel = new JLabel("Connected CAD Terminals: ");
        termConnectedTF = new JTextField("   " + String.valueOf(numClientsConnected));
        termConnectedTF.setEditable(false);
        termConnectedTF.setName("termConnectedTF");

        termConnectedBox = new Box(BoxLayout.X_AXIS);
        termConnectedBox.add(terminalsConnectedLabel);
        termConnectedBox.add(Box.createHorizontalGlue());
        termConnectedBox.add(termConnectedTF);


        managerConnectedLabel = new JLabel("Simulation Manager Connected: ");
        managerConnectedTF = new JTextField("  No");
        managerConnectedTF.setEditable(false);
        managerConnectedTF.setName("managerConnectedTF");

        managerConnectedBox = new Box(BoxLayout.X_AXIS);
        managerConnectedBox.add(managerConnectedLabel);
        managerConnectedBox.add(Box.createHorizontalGlue());
        managerConnectedBox.add(managerConnectedTF);


        paramicsConnectedLabel = new JLabel("Connected to Paramics: ");
        paramicsConnectedTF = new JTextField("  No");
        paramicsConnectedTF.setEditable(false);
        paramicsConnectedTF.setName("paramicsConnectedTF");

        paramicsConnectedBox = new Box(BoxLayout.X_AXIS);
        paramicsConnectedBox.add(paramicsConnectedLabel);
        paramicsConnectedBox.add(Box.createHorizontalGlue());
        paramicsConnectedBox.add(paramicsConnectedTF);


        networkLoadedLabel = new JLabel("Network Loaded: ");
        networkLoadedTF = new JTextField("None");
        networkLoadedTF.setEditable(false);
        networkLoadedTF.setName("networkLoadedTF");

        networkLoadedBox = new Box(BoxLayout.X_AXIS);
        networkLoadedBox.add(networkLoadedLabel);
        networkLoadedBox.add(Box.createHorizontalGlue());
        networkLoadedBox.add(networkLoadedTF);


        additionalInfoBox = new Box(BoxLayout.Y_AXIS);
        additionalInfoBox.setMinimumSize(new Dimension(300, 150));

        additionalInfoBox.add(Box.createVerticalStrut(10));
        additionalInfoBox.add(termConnectedBox);
        additionalInfoBox.add(Box.createVerticalStrut(10));
        additionalInfoBox.add(managerConnectedBox);
        additionalInfoBox.add(Box.createVerticalStrut(10));
        additionalInfoBox.add(paramicsConnectedBox);
        additionalInfoBox.add(Box.createVerticalStrut(10));
        additionalInfoBox.add(networkLoadedBox);
        additionalInfoBox.add(Box.createVerticalStrut(20));


    }

    /**
     * Initialize Info & Error Messages GUI Components
     */
    private void initMessagesPanes()
    {

        infoMessagesTA = new JTextArea(6, 30);
        infoMessagesTA.setEditable(false);
        infoMessagesTA.setName("infoMessagesTA");
        infoMessagesPane = new JScrollPane(infoMessagesTA);
        infoMessagesPane.setPreferredSize(new Dimension(300, 100));
        infoMessagesPane.setName("infoMessagesPane");
        infoMessagesPane.setBorder(BorderFactory.createTitledBorder(
                BorderFactory.createEtchedBorder(EtchedBorder.LOWERED), "Info Messages"));
        infoMessagesPane.setName("infoMessagesPane");

        errorMessagesTA = new JTextArea(6, 30);
        errorMessagesTA.setForeground(Color.RED);
        errorMessagesTA.setEditable(false);
        errorMessagesTA.setName("errorMessagesTA");
        errorMessagesPane = new JScrollPane(errorMessagesTA);
        errorMessagesPane.setPreferredSize(new Dimension(300, 150));
        errorMessagesPane.setName("errorMessagesPane");
        errorMessagesPane.setBorder(BorderFactory.createTitledBorder(
                BorderFactory.createEtchedBorder(EtchedBorder.LOWERED), "Error Messages"));

    }
    private Box additionalInfoBox;
    private Box termConnectedBox;
    private Box managerConnectedBox;
    private Box paramicsConnectedBox;
    private Box networkLoadedBox;
    private Box CADSimulatorViewerBox;
    private Box simulationTimeAndStatusBox;
    private Box simulationStatusBox;
    private Box simulationTimeBox;
    private Box simulationClockBox;
    private JLabel managerConnectedLabel;
    private JLabel paramicsConnectedLabel;
    private JLabel simulationStatus;
    private JLabel simulationClockLabel;
    private JLabel simulationStatusText;
    private JLabel terminalsConnectedLabel;
    private JLabel networkLoadedLabel;
    private JPanel simulationTime;
    private JPanel simulationClock;
    private JTextField managerConnectedTF;
    private JTextField paramicsConnectedTF;
    private JTextField termConnectedTF;
    private JTextField networkLoadedTF;
    private JScrollPane infoMessagesPane;
    private JScrollPane errorMessagesPane;
    private JTextArea infoMessagesTA;
    private JTextArea errorMessagesTA;
}
