package interfacing;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;

import javax.swing.*;

import javax.swing.filechooser.FileFilter;

import logging.MueReader;
import logging.ServersReader;
import mapping.data.Server;
import mapping.data.ServersTableListener;

/**
 * An object from this class and and ServersTableModelListerner act as the view
 * and controller of the ServerTable in the Mapper object. They simply view the
 * ServersTable and allow user to insert new classes.
 */
public class ServersTableJPanel extends JPanel implements PropertyChangeListener, ActionListener, MouseListener,
		KeyListener, ServersTableListener {

	/**  */
	private static final long serialVersionUID = -1314942526341209428L;

	/** Names of the columns in jTable (Attributes edited) */
	private String[] columnNames = { "ID", "Hostname", "Down" };

	private MainFrame frame;

	/** The view component of the table. */
	private JTable jTable;

	/** The tableModel object of JTable */
	private OurDefaultTableModel tableModel;

	/** The listener of the tableModel */
	private ServersTableModelListener serversTableModelListener;

	/** The scroll pane that the table is put in. */
	private JScrollPane jTableScrollPane;

	/** This main SplitPane that splits this JPanel */
	private JSplitPane splitPane;

	/** The left side of the main splitPane */
	private JScrollPane leftScrollPane;

	/** The right side of the main splitPane */
	private JScrollPane rightScrollPane;

	/** The panel that holds the jTable and its scroll pane */
	private JPanel jTableContainer;

	/** The add button. Used to add new Server object. */
	private JButton addButton;

	/** The remove button. Used to remove a Server object. */
	private JButton removeButton;

	final JFileChooser fileChooser = new JFileChooser();

	/** The import button is used to import servers from a file */
	private JButton importButton;

	/** The import button is used to import processing Rates from a file */
	private JButton importPRButton;

	/** The panel that holds the add and remove buttons. */
	private JPanel buttonsConatainers;

	/** The panel used to insert info about a new Server object. */
	private PropertiesJPanel propPanel;

	/** This button used to submit the information inserted in proPanel. */
	private JButton addServerButton;

	/** Just a container for both propPanel and submitButton. */
	private JPanel propPanelContainer;

	/** This JPanel is used to show and control the processing rates */
	private ServersProcessingRatesJPanel serversProcessingRates;

	private JPanel rightJPanel;
	private JPanel serverLabelJPanel;
	private JLabel serverLabel;
	private JLabel serverName;
	private JTabbedPane rightTabbedPane;
	private ServersProcessingRatesJPanel processingRatesJPanel;
	private FailuresTraceJPanel failureTracesJPanel;

	final private int PROCESSING_RATES = 0;
	final private int FAILURE_RATES = 1;
	private int rightTabbedPaneContent = PROCESSING_RATES;

	private boolean editable = true;

	/**
	 * 
	 * @param frame
	 *            is the MainFrame object that contains this Panel.
	 * 
	 */
	public ServersTableJPanel(MainFrame frame) {
		this.frame = frame;

		// Init the GUI components
		leftScrollPane = new JScrollPane(); // left side of of splitPane
		rightScrollPane = new JScrollPane(); // right side of of splitPane
		jTable = new JTable(); // jTable component that shows the table
		jTableScrollPane = new JScrollPane(); // The scroll pane where the
		// jTable lives.
		jTableContainer = new JPanel(); // The panel that holds the jTable and
		// its scrollPane
		this.setLayout(new BorderLayout());

		/*--------- Right Panel building --------------*/
		rightJPanel = new JPanel();
		rightJPanel.setLayout(new BorderLayout());
		serverLabelJPanel = new JPanel();
		serverLabel = new JLabel("Server: ");
		serverName = new JLabel();
		serverName.setFont(new Font("", Font.BOLD, 13));
		serverLabelJPanel.add(serverLabel);
		serverLabelJPanel.add(serverName);
		rightJPanel.add(serverLabelJPanel, BorderLayout.NORTH);
		rightTabbedPane = new JTabbedPane();
		processingRatesJPanel = new ServersProcessingRatesJPanel();
		failureTracesJPanel = new FailuresTraceJPanel();
		rightTabbedPane.add("Processing Rates", processingRatesJPanel);
		rightTabbedPane.add("Failure Periods", failureTracesJPanel);
		rightJPanel.add(rightTabbedPane, BorderLayout.CENTER);
		rightTabbedPane.addMouseListener(this);

		rightScrollPane.setViewportView(rightJPanel);

		splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, leftScrollPane, rightScrollPane);
		splitPane.setDividerLocation(450);

		// Registering the Listener which responsible for validating data and
		// send it the view
		serversTableModelListener = new ServersTableModelListener(this.frame);

		/* Building jTableContainer */
		jTableContainer.setLayout(new GridBagLayout());
		GridBagConstraints gbConstraints = new GridBagConstraints();
		gbConstraints.gridx = 0;
		gbConstraints.gridy = 1;
		gbConstraints.fill = GridBagConstraints.NONE;
		gbConstraints.anchor = GridBagConstraints.NORTH;
		gbConstraints.gridheight = 1;
		gbConstraints.gridwidth = 1;

		jTableScrollPane.setViewportView(jTable); // adding jTable to its
		// scroll pane
		jTableContainer.add(jTableScrollPane, gbConstraints); // add the
		// jTable and
		// its pane to
		// jTableContainer
		gbConstraints.gridy = 0;
		// Building a panel containing + and - buttons
		buttonsConatainers = new JPanel();
		importButton = new JButton("Import Servers");
		importButton.setIcon(new ImageIcon(MainFrame.iconsFolder + "/" + "filerevert_22.png"));
		importButton.addActionListener(this);
		addButton = new JButton("");
		addButton.setIcon(new ImageIcon(MainFrame.iconsFolder + "/" + "edit_add_22.png"));
		addButton.addActionListener(this);
		removeButton = new JButton("");
		removeButton.setIcon(new ImageIcon(MainFrame.iconsFolder + "/" + "edit_remove_22.png"));
		removeButton.addActionListener(this);
		importPRButton = new JButton("Import Mue");
		importPRButton.setIcon(new ImageIcon(MainFrame.iconsFolder + "/" + "import_u_22.png"));
		importPRButton.addActionListener(this);

		buttonsConatainers.add(importButton);
		buttonsConatainers.add(addButton);
		buttonsConatainers.add(removeButton);
		buttonsConatainers.add(importPRButton);
		jTableContainer.add(buttonsConatainers, gbConstraints); // add buttons
		// panel and its
		// pane to
		// jTableContainer

		// This segment forces the north alignment of the components
		gbConstraints.weighty = 1.0;
		gbConstraints.fill = GridBagConstraints.BOTH;
		gbConstraints.gridy = 2;
		jTableContainer.add(javax.swing.Box.createVerticalGlue(), gbConstraints);

		leftScrollPane.setViewportView(jTableContainer);
		this.add(splitPane);
		jTable.setGridColor(Color.BLACK);
		jTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
		buildPropretyJPanel();
		this.hidePropertyJPanel();

		/* JTable Cosmetic */
		this.fillJTable();
		jTable.getColumnModel().getColumn(0).setPreferredWidth(50);
		jTable.getColumnModel().getColumn(1).setPreferredWidth(250);
		jTable.getColumnModel().getColumn(2).setPreferredWidth(100);
		jTable.setDragEnabled(false);

		int h = (1 + jTable.getModel().getRowCount()) * jTable.getRowHeight() + 100;
		this.setPreferredSize(new Dimension(600, h));
		splitPane.addPropertyChangeListener(this);

		jTable.addMouseListener(this);
		jTable.addKeyListener(this);
	}

	/**
	 * This method fill the table model (tableModel) of the corresponding data
	 * from the ServerTable object that this JPanel is the view for. The jTable
	 * object is changed correspondingly.
	 */

	public void fillJTable() {
		tableModel = new OurDefaultTableModel(columnNames, 0);
		jTable.setModel(tableModel);

		Server server;

		String downString;
		Object[] objects = this.frame.getMapper().getServersTable().keySet().toArray();

		for (int i = 0; i < objects.length; i++) {
			
			server = this.frame.getMapper().getServer(i + 1);
			if (this.frame.getMapper().getServersTable().isServerDown(i + 1))
				downString = "Down";
			else
				downString = "Up";

			String[] s = { server.getIndex() + "", server.getHostName() + "", downString + "", };
			tableModel.addRow(s);
		}

		jTable.getColumnModel().getColumn(0).setPreferredWidth(50);
		jTable.getColumnModel().getColumn(1).setPreferredWidth(250);
		jTable.getColumnModel().getColumn(2).setPreferredWidth(100);

		serversTableModelListener = new ServersTableModelListener(this.frame);
		tableModel.addTableModelListener(serversTableModelListener);
	}

	/**
	 * This method build the property table
	 */
	public void buildPropretyJPanel() {
		// Properties of JobClass
		String[] names = { "Hostname" };
		// Creating a property JPanel
		propPanel = new PropertiesJPanel(names, null);
		addServerButton = new JButton("Add Server");
		addServerButton.addActionListener(this);
		propPanelContainer = new JPanel();
		propPanelContainer.setLayout(new GridBagLayout());
		GridBagConstraints gbConstraints = new GridBagConstraints();
		gbConstraints.anchor = GridBagConstraints.CENTER;
		gbConstraints.gridx = 0;
		gbConstraints.gridy = 0;
		propPanelContainer.add(propPanel, gbConstraints);
		gbConstraints.gridx = 0;
		gbConstraints.gridy = 1;
		propPanelContainer.add(addServerButton, gbConstraints);

		// This segment forces the north alignment of the components
		gbConstraints.weighty = 1.0;
		gbConstraints.fill = GridBagConstraints.BOTH;
		gbConstraints.gridy = 2;
		propPanelContainer.add(javax.swing.Box.createVerticalGlue(), gbConstraints);

		gbConstraints.gridheight = 3;
		gbConstraints.weightx = 0.0;
		gbConstraints.weightx = 1.0;
		gbConstraints.gridx = 1;
		gbConstraints.gridy = 0;
		propPanelContainer.add(javax.swing.Box.createHorizontalGlue(), gbConstraints);

		this.repaint();
	}

	/**
	 * This method resizes JTable
	 */
	private void reSizeJTable() {
		if (jTable == null)
			return;
		int rowCount = (1 + jTable.getModel().getRowCount());
		int rowHeight = jTable.getRowHeight();
		int h = rowCount * rowHeight + 5;
		Dimension d = new Dimension(this.splitPane.getDividerLocation() - 10, h);
		jTable.setSize(d);
		jTableContainer.setPreferredSize(d);
		jTableScrollPane.setPreferredSize(d);
		rightJPanel.setPreferredSize(d); // d should be changed
		this.setPreferredSize(new Dimension(500, d.height + 100));
	}

	public void actionPerformed(ActionEvent e) {
		if (e.getSource() == importButton) {

			FileFilter filter = new ExtensionFileFilter("SRS", new String[] { "SRS" });
			fileChooser.setCurrentDirectory(new File("Servers/servers.srs"));
			fileChooser.setFileFilter(filter);
			int returnVal = fileChooser.showOpenDialog(this);
			if (returnVal == JFileChooser.APPROVE_OPTION) {
				ServersReader.readServersFromFile(fileChooser.getSelectedFile().getPath(), this.frame.getMapper()
						.getServersTable());
				this.frame.getMapper().fillProcessingRates();
			}

			this.fillJTable();
		}

		if (e.getSource() == addServerButton) {
				if (propPanel.getValue(0).trim().equalsIgnoreCase("")) {
					JOptionPane.showMessageDialog(this, "Please enter valid HostName.", "Error",
							JOptionPane.ERROR_MESSAGE);
					return;
				}

			this.frame.getMapper().getServersTable()
					.addServer(new Server(propPanel.getValue(0)));

			this.fillJTable();
			hidePropertyJPanel();
			this.splitPane.setDividerLocation(this.splitPane.getDividerLocation() + 1);
		}

		if (e.getSource() == addButton) {
			showPropertyJPanelToAddServer();
		}
		if (e.getSource() == removeButton) {
			int previousSelection = -1;
			if (jTable.getSelectedRow() >= 0) {
				previousSelection = jTable.getSelectedRow();
			}
			removeSelectedServer();

			if (previousSelection >= 0 && jTable.getModel().getRowCount() > 0) {
				jTable.getSelectionModel().setSelectionInterval(previousSelection, previousSelection);
			}
			showServerInfo();
			this.repaint();
		}

		if (e.getSource() == importPRButton) {
			FileFilter filter = new ExtensionFileFilter("MUE", new String[] { "MUE" });
			fileChooser.setFileFilter(filter);
			fileChooser.setCurrentDirectory(null);
			int returnVal = fileChooser.showOpenDialog(this);
			if (returnVal == JFileChooser.APPROVE_OPTION) {
				try {
					MueReader.readMue(this.frame.getMapper(), fileChooser.getSelectedFile().getPath());
				} catch (Exception ex) {
					JOptionPane.showMessageDialog(this, "Mue file has invalid format!\nPlease make sure the file has "
							+ this.frame.getMapper().getClassesTable().size() + " lines (one for every job class) and " + this.frame.getMapper().getServersTable().size() + " comma-separated numbers in each line (for every server). " , "Error", JOptionPane.ERROR_MESSAGE);
				}
			}
			showServerInfo();
		}
	}

	/**
	 * This method shows a Panel that allow the user to enter info about a new
	 * Server object to be added
	 */
	public void showPropertyJPanelToAddServer() {
		this.rightScrollPane.setViewportView(propPanelContainer);
		this.propPanel.clearFields();
		this.propPanelContainer.setVisible(true);
	}

	/**
	 * This hides the property JPanel
	 */
	public void hidePropertyJPanel() {
		this.propPanelContainer.setVisible(false);
	}

	/**
	 * This method removes a Server when removeButton is clicked
	 */
	public void removeSelectedServer() {
		if (jTable.getSelectedRow() < 0) { // Not selected
			JOptionPane.showMessageDialog(this, "Please select a server from the table.", "Error",
					JOptionPane.ERROR_MESSAGE);
			return;
		}

		this.frame.getMapper().getServersTable().removeServer(jTable.getSelectedRow() + 1);

		this.fillJTable();
		this.splitPane.setDividerLocation(this.splitPane.getDividerLocation());
	}

	/**
	 * This handler is triggered when the divider location of the splitPane is
	 * changed.
	 */
	public void propertyChange(PropertyChangeEvent evt) {
		if (evt.getSource() == jTable) {
			return;
		}
		reSizeJTable();
	}

	public void printModel(OurDefaultTableModel model) {
		for (int i = 0; i < model.getRowCount(); i++) {
			for (int j = 0; j < model.getColumnCount(); j++) {
				System.out.print(model.getValueAt(i, j) + " ");
			}
			System.out.println();
		}
	}

	/**
	 * This method disables input from user
	 */
	public void disableInput() {
		this.editable = false;
		tableModel.setEditable(false);
		if (this.serversProcessingRates != null) {
			this.serversProcessingRates.disableInput();
		}
		this.addButton.setEnabled(false);
		this.removeButton.setEnabled(false);
		this.importButton.setEnabled(false);
	}

	/**
	 * This method enables input from user
	 */
	public void enableInput() {
		this.editable = true;
		tableModel.setEditable(true);
		if (this.serversProcessingRates != null) {
			this.serversProcessingRates.enableInput();
		}
		this.addButton.setEnabled(true);
		this.removeButton.setEnabled(true);
		this.importButton.setEnabled(true);
	}

	/**
	 * This method shows the info about the selected Server. It is invoked when a
	 * user clicks on a Server in the JTable.
	 */
	public void showServerInfo() {
		if (this.serverName == null)
			return;

		if (jTable.getSelectedRow() < 0 || jTable.getSelectedRow() > this.frame.getMapper().getServersTable().size()) {
			this.serverName.setText("Not Selected");
			this.rightTabbedPane.remove(this.processingRatesJPanel);
			this.rightTabbedPane.remove(this.failureTracesJPanel);
			return;
		}

		if (this.frame.getMapper().getServer(jTable.getSelectedRow() + 1) == null)
			return;
		this.serverName.setText(this.frame.getMapper().getServer(jTable.getSelectedRow() + 1).getHostName());
		this.rightTabbedPane.remove(this.processingRatesJPanel);
		this.rightTabbedPane.remove(this.failureTracesJPanel);

		this.processingRatesJPanel = new ServersProcessingRatesJPanel(frame, jTable.getSelectedRow() + 1);

		if (!editable && this.serversProcessingRates != null) {
			this.serversProcessingRates.disableInput();
		}
		this.failureTracesJPanel = new FailuresTraceJPanel(this.frame, this.frame.getMapper().getServer(
				jTable.getSelectedRow() + 1));
		rightTabbedPane.add("Processing Rates", processingRatesJPanel);
		rightTabbedPane.add("Failure Periods", failureTracesJPanel);
		this.rightTabbedPane.repaint();
		this.rightScrollPane.setViewportView(rightJPanel);
		this.rightTabbedPane.setSelectedIndex(this.rightTabbedPaneContent);

	}

	/**
	 * This method is invoked when the user clicks on a Server
	 */
	public void mouseClicked(MouseEvent e) {
		if (e.getSource() == jTable) {
			if (jTable.getSelectedRow() >= 0
					&& jTable.getSelectedRow() < this.frame.getMapper().getServersTable().size()) {
				showServerInfo();
			}
		}
		if (e.getSource() == rightTabbedPane) {
			if (rightTabbedPane.getSelectedIndex() == 0)
				this.rightTabbedPaneContent = PROCESSING_RATES;
			if (rightTabbedPane.getSelectedIndex() == 1)
				this.rightTabbedPaneContent = FAILURE_RATES;
		}
	}

	public void mouseEntered(MouseEvent e) {
	}

	public void mouseExited(MouseEvent e) {
	}

	public void mousePressed(MouseEvent e) {
	}

	public void mouseReleased(MouseEvent e) {
	}

	public void repaint() {
		super.repaint();
		reSizeJTable();

	}

	public void keyPressed(KeyEvent e) {

	}

	/**
	 * This method is invoked when the user select a Server from the JTable by
	 * using the keyboard
	 */
	public void keyReleased(KeyEvent e) {
		if (e.getSource() == jTable) {
			if (jTable.getSelectedRow() >= 0
					&& jTable.getSelectedRow() < this.frame.getMapper().getServersTable().size()) {
				showServerInfo();
			}
		}
	}

	public void keyTyped(KeyEvent e) {

	}

	/**
	 * @see ServersTableListener
	 */
	public void ServersTableLister(long serverID, String hostname, String password, boolean down) {

	}

	/**
	 * @see ServersTableListener
	 */
	public void hostNameChanged(int serverID, String hostname) {

	}

	/**
	 * @see ServersTableListener
	 */
	public void passwordChanged(int serverID, String password) {

	}

	/**
	 * @see ServersTableListener
	 */
	public void upDownStatusChanged(int serverID, boolean down) {
		if (down)
			this.jTable.getModel().setValueAt("Down", serverID - 1, 2);
		else
			this.jTable.getModel().setValueAt("Up", serverID - 1, 2);
	}

}
