package gui;

import java.awt.*;
import java.awt.event.*;
import java.rmi.RemoteException;

import javax.swing.*;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;
import connectfour.*;
import server.*;
import shared.*;

public class ServerWindow implements WindowListener, ActionListener, Runnable, Updatable {
	
	private Server m_server;
	private JFrame m_frame;
	private JTabbedPane m_mainTabbedPane;
	private JTextArea m_consoleText;
	private Font m_consoleFont;
	private JScrollPane m_consoleScrollPane;
	private JTable m_userTable;
    private JScrollPane m_userScrollPane;
	private JTable m_gameSessionTable;
    private JScrollPane m_gameSessionScrollPane;
	private JMenuBar m_menuBar;
	private JMenu m_fileMenu;
	private JMenuItem m_fileExitMenuItem;
	private JMenu m_settingsMenu;
	private JCheckBoxMenuItem m_settingsAutoScrollConsoleWindowMenuItem;
	private JMenuItem m_settingsMaxConsoleHistoryMenuItem;
	private JMenu m_helpMenu;
	private JMenuItem m_helpAboutMenuItem;
	private boolean m_initialized;
	private boolean m_running;
	private boolean m_updating;
	private boolean m_autoSizeColumns;
	private Thread m_updateThread;
	
	public ServerWindow() {
		// set window properties
		m_frame = new JFrame("Matchmaking Server Window");
		m_frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
		m_frame.setMinimumSize(new Dimension(320, 240));
		m_frame.setSize(new Dimension(640,480));
		m_frame.addWindowListener(this);
		
		// create the server
		try { m_server = new Server(); }
		catch(RemoteException e) {
			JOptionPane.showMessageDialog(m_frame, "Failed to create server: " + e.getMessage(), "Startup Failure", JOptionPane.ERROR_MESSAGE);
			System.exit(1);
		}
		Server.console.addTarget(this);
		m_initialized = false;
		m_running = false;
		m_updating = false;
		m_autoSizeColumns = true;
		
		// initialize gui components
		initMenu();
		initComponents();
	}
	
	// initialize the server and window
	public boolean initialize(String serverQueueName, int registryPort, String brokerHostName) {
		if(m_initialized) { return false; }
		
		// initialize the server
		if(!m_server.initialize(serverQueueName, registryPort, brokerHostName)) {
			JOptionPane.showMessageDialog(null, "Unable to initialize matchmaking server.", "Initialization Error", JOptionPane.ERROR_MESSAGE);
			return false;
		}
		
		// initialize the window and display it
		update();
		m_frame.setVisible(true);
		
		m_initialized = true;
		
		// create and initialize the window update thread 
		m_updateThread = new Thread(this);
		m_updateThread.start();
		
		return true;
	}
	
	// initialize the menu
	private void initMenu() {
		m_menuBar = new JMenuBar();
		
		m_fileMenu = new JMenu("File");
		m_fileExitMenuItem = new JMenuItem("Exit");
		
		m_settingsMenu = new JMenu("Settings");
		m_settingsAutoScrollConsoleWindowMenuItem = new JCheckBoxMenuItem("Auto-scroll Console Window");
		
		m_helpMenu = new JMenu("Help");
		m_helpAboutMenuItem = new JMenuItem("About");
		
		m_settingsAutoScrollConsoleWindowMenuItem.setSelected(Server.console.getAutoScrollConsoleWindow());
		m_settingsMaxConsoleHistoryMenuItem = new JMenuItem("Max Console History");
		
		m_fileExitMenuItem.addActionListener(this);
		m_settingsAutoScrollConsoleWindowMenuItem.addActionListener(this);
		m_settingsMaxConsoleHistoryMenuItem.addActionListener(this);
		m_helpAboutMenuItem.addActionListener(this);
		
		m_fileMenu.add(m_fileExitMenuItem);
		
		m_settingsMenu.add(m_settingsAutoScrollConsoleWindowMenuItem);
		m_settingsMenu.add(m_settingsMaxConsoleHistoryMenuItem);
		
		m_helpMenu.add(m_helpAboutMenuItem);
		
		m_menuBar.add(m_fileMenu);
		m_menuBar.add(m_settingsMenu);
		m_menuBar.add(m_helpMenu);
		
		m_frame.setJMenuBar(m_menuBar);
	}
	
	// initialize gui components
	private void initComponents() {
		// initialize the main tabbed pane
		m_mainTabbedPane = new JTabbedPane();
		
		// initialize the console tab
		m_consoleText = new JTextArea();
        m_consoleFont = new Font("Verdana", Font.PLAIN, 14);
        m_consoleText.setFont(m_consoleFont);
        m_consoleText.setEditable(false);
        m_consoleScrollPane = new JScrollPane(m_consoleText);
        m_mainTabbedPane.add(m_consoleScrollPane);
        
        m_mainTabbedPane.addTab("Console", null, m_consoleScrollPane, "Displays debugging output from the server.");
        
        m_frame.add(m_mainTabbedPane);
        
        // initialize the connected users tab
        m_userScrollPane = new JScrollPane();
        m_userTable = new JTable() {
			private static final long serialVersionUID = 1L;
			
			public boolean isCellEditable(int row, int col) {
        		return false;
        	}
        };
        
        DefaultTableModel userTableModel = new DefaultTableModel(
        	null,
        	User.TABLE_HEADERS
        );
        
        m_userTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        m_userTable.setModel(userTableModel);
        m_userScrollPane.setViewportView(m_userTable);
        m_mainTabbedPane.addTab("Users", null, m_userScrollPane, "Displays the active users");
        
        // initialize the active game sessions tab
        m_gameSessionScrollPane = new JScrollPane();
        m_gameSessionTable = new JTable() {
			private static final long serialVersionUID = 1L;
			
			public boolean isCellEditable(int row, int col) {
        		return false;
        	}
        };
        
        DefaultTableModel gameSessionTableModel = new DefaultTableModel(
        	null,
        	GameSession.TABLE_HEADERS
        );
        
        m_gameSessionTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        m_gameSessionTable.setModel(gameSessionTableModel);
        m_gameSessionScrollPane.setViewportView(m_gameSessionTable);
        m_mainTabbedPane.addTab("Game Sessions", null, m_gameSessionScrollPane, "Displays the active game sessions");
	}
	
	// update the server window
	public void update() {
		m_updating = true;
		
		m_settingsAutoScrollConsoleWindowMenuItem.setSelected(Server.console.getAutoScrollConsoleWindow());
		
		// automatically scroll the console text
		try {
			m_consoleText.setText(Server.console.toString());
			
			if(Server.console.getAutoScrollConsoleWindow()) {
				m_consoleText.setCaretPosition(m_consoleText.getText().length());
			}
		}
		catch(Exception e) { }
		
		m_updating = false;
	}
	
	public void windowActivated(WindowEvent e) { }
	public void windowClosed(WindowEvent e) { }
	public void windowDeactivated(WindowEvent e) { }
	public void windowDeiconified(WindowEvent e) { }
	public void windowIconified(WindowEvent e) { }
	public void windowOpened(WindowEvent e) { }
	
	public void windowClosing(WindowEvent e) {
		if(e.getSource() == m_frame) {
			stop();
			m_server.stop();
			m_frame.dispose();
		}
	}
	
	public void actionPerformed(ActionEvent e) {
		if(m_updating) { return; }
		
		// close the server
		if(e.getSource() == m_fileExitMenuItem) {
			stop();
			m_server.stop();
			System.exit(0);
		}
		// change the console auto scrolling
		else if(e.getSource() == m_settingsAutoScrollConsoleWindowMenuItem) {
			Server.console.setAutoScrollConsoleWindow(m_settingsAutoScrollConsoleWindowMenuItem.isSelected());
		}
		// change the maximum number of elements the console can h old
		else if(e.getSource() == m_settingsMaxConsoleHistoryMenuItem) {
			// prompt for the maximum console history size
			String input = JOptionPane.showInputDialog(m_frame, "Please enter the maximum console history size:", Server.console.getMaxConsoleHistory());
			if(input == null) { return; }
			
			// set the new console history size
			int maxConsoleHistory = -1;
			try {
				maxConsoleHistory = Integer.parseInt(input);
			}
			catch(NumberFormatException e2) {
				JOptionPane.showMessageDialog(m_frame, "Invalid number entered for maximum console history.", "Invalid Number", JOptionPane.ERROR_MESSAGE);
				return;
			}
			
			Server.console.setMaxConsoleHistory(maxConsoleHistory);
		}
		// display help message
		else if(e.getSource() == m_helpAboutMenuItem) {
			JOptionPane.showMessageDialog(m_frame, "Connect Four Matchmaking Server\nCreated by Kevin Scroggins & Corey Faibish", "About Matchmaking Server", JOptionPane.INFORMATION_MESSAGE);
		}
	}
	
	// update the connected users table
	public void updateUserTable(boolean autoSizeColumns) {
		try {
			// get the selected table cell
			int x = m_userTable.getSelectedColumn();
			int y = m_userTable.getSelectedRow();
			
			// get the table's model and clear it safely
			DefaultTableModel tableModel = (DefaultTableModel) m_userTable.getModel();
			tableModel.getDataVector().removeAllElements();
			tableModel.fireTableDataChanged();
			
			// get all of the connected users and add them to the table model
			for(int i=0;i<m_server.numberOfUsers();i++) {
				tableModel.addRow(m_server.getUser(i).getAsTableEntry());
			}
			
			// if the previous selection is still valid, re-select the previously selected cell
			if(x >= 0 && y >= 0 && x < m_userTable.getColumnCount() && y < m_userTable.getRowCount()) { 
				m_userTable.getSelectionModel().setSelectionInterval(y, y);
				m_userTable.getColumnModel().getSelectionModel().setSelectionInterval(x, x);
			}
			
			// if the columns should be autosized, then autosize them using the table renderer
			if(autoSizeColumns) {
				final TableCellRenderer renderer = m_userTable.getTableHeader().getDefaultRenderer();
				
				for(int i=0;i<m_userTable.getColumnCount();i++) {
					m_userTable.getColumnModel().getColumn(i).setPreferredWidth(renderer.getTableCellRendererComponent(m_userTable, m_userTable.getModel().getColumnName(i), false, false, 0, i).getPreferredSize().width);
				}
			}
		}
		catch(Exception e) { e.printStackTrace(); }
	}
	
	// update the active game sessions table
	public void updateGameSessionTable(boolean autoSizeColumns) {
		try {
			// get the selected table cell
			int x = m_gameSessionTable.getSelectedColumn();
			int y = m_gameSessionTable.getSelectedRow();
			
			// get the table's model and clear it safely
			DefaultTableModel tableModel = (DefaultTableModel) m_gameSessionTable.getModel();
			tableModel.getDataVector().removeAllElements();
			tableModel.fireTableDataChanged();
			
			// get all of the active game sessions and add them to the table
			for(int i=0;i<m_server.numberOfSessions();i++) {
				tableModel.addRow(m_server.getSession(i).getAsTableEntry());
			}
			
			// if the previous selection is still valid, re-select the previously selected cell
			if(x >= 0 && y >= 0 && x < m_gameSessionTable.getColumnCount() && y < m_gameSessionTable.getRowCount()) { 
				m_gameSessionTable.getSelectionModel().setSelectionInterval(y, y);
				m_gameSessionTable.getColumnModel().getSelectionModel().setSelectionInterval(x, x);
			}
			
			// if the columns should be autosized, then autosize them using the table renderer
			if(autoSizeColumns) {
				final TableCellRenderer renderer = m_gameSessionTable.getTableHeader().getDefaultRenderer();
				
				for(int i=0;i<m_gameSessionTable.getColumnCount();i++) {
					m_gameSessionTable.getColumnModel().getColumn(i).setPreferredWidth(renderer.getTableCellRendererComponent(m_gameSessionTable, m_gameSessionTable.getModel().getColumnName(i), false, false, 0, i).getPreferredSize().width);
				}
			}
		}
		catch(Exception e) { e.printStackTrace(); }
	}

	// stop the server
	public void stop() {
		// reset initialization variables
		m_initialized = false;
		m_running = false;
		
		// stop all threads
		try { m_updateThread.interrupt(); } catch(Exception e) { }
	}
	
	// indefinitely update the gui window
	public void run() {
		if(!m_initialized) { return; }
		
		m_running = true;
		
		while(m_running) {
			// update database view tables
			updateUserTable(m_autoSizeColumns);
			updateGameSessionTable(m_autoSizeColumns);
			
			// only autosize columns once
			m_autoSizeColumns = false;
			
			try { Thread.sleep(1000L); }
			catch(InterruptedException e) { }
		}
		
	}
	
}
