import java.applet.Applet;
import java.awt.Button;
import java.awt.Color;
import java.awt.Dialog;
import java.awt.Font;
import java.awt.Frame;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.Label;
import java.awt.MenuItem;
import java.awt.Panel;
import java.awt.PopupMenu;
import java.awt.TextArea;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.Vector;

public class GkGUI extends Applet implements ActionListener {
    private static final long serialVersionUID = 901576328822488148L;
    // ActionCommands sent by buttons and menu items
	private static final String CONNECT = "connect";
	private static final String DISCONNECT = "disconnect";
    private static final String REFRESH = "refresh";
    private static final String RELOAD = "reload";
	private static final String UNREG_EP = "unreg_ep";
	private static final String DISC_EP = "disc_ep";
	private static final String TRANSFERCALL_EP = "transfercall_ep";
	private static final String CALL_EP = "call_ep";
	
	// GUI components
	private TextArea txt;
	private Panel endpointPanel;
	private GkClient gk;
	private Vector epList;
	private PopupMenu popup;
	private static Frame mainWindow;	// only used for stand-alone application
    private Button connect;
    private Button disconnect;
    private Button refresh;
    private Button reload;

	// helpers for pop-up menu
	private MouseListener popupListener;
	private String selectedAlias;
	
	private static boolean isApplet = true;
	private static String gkHost;
	private static int gkPort = 7000;	// default port
	private static String gkUser;
	private static String gkPassword;

	public GkGUI() {
		gk = new GkClient();
	}

	// only used when run as stand-alone application	
	public static void main(String[] args) {
		mainWindow = new Frame("GNU Gatekeeper GUI Application");
		mainWindow.addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent e) {
				System.exit(0);
			}
		});

		if ((args.length != 2) && (args.length != 4)) {
			System.out.println("usage: java GkGUI <gk-ip> <gk-port> [<user> <password>]\n");
			System.exit(1);
		}		
		gkHost = args[0];
		gkPort = Integer.parseInt(args[1]);
		if (args.length == 4) {
			gkUser = args[2];
			gkPassword = args[3];
		}

		GkGUI gkgui = new GkGUI();
		
		isApplet = false;
		gkgui.init();
		mainWindow.add("Center", gkgui);
		mainWindow.pack();
		mainWindow.setVisible(true);
	}
	
	public String getAppletInfo() {
		return "GUI for GNU Gatekeeper";
	}

	public void init() {
		epList = new Vector();
		Panel buttonPanel;
		GridBagLayout layout = new GridBagLayout();
		GridBagConstraints constraints = new GridBagConstraints();
		setBackground(Color.white);
		setFont(new Font("Helvetica", Font.PLAIN, 12));
		setLayout(layout);

		constraints.gridwidth = GridBagConstraints.REMAINDER;

		Label titleText = new Label("GNU Gatekeeper GUI");
		titleText.setFont(new Font("Helvetica", Font.BOLD, 16));
		layout.setConstraints(titleText, constraints);
		add(titleText);

		Label copyText = new Label("(c) Jan Willamowius, 2001-2008");
		copyText.setFont(new Font("Helvetica", Font.PLAIN, 12));
		layout.setConstraints(copyText, constraints);
		add(copyText);

		constraints.insets = new Insets(5,5,5,5);
		buttonPanel = new Panel();
		layout.setConstraints(buttonPanel, constraints);
		add(buttonPanel);

		constraints.fill = GridBagConstraints.HORIZONTAL;
		txt = new TextArea("", 10, 80, TextArea.SCROLLBARS_VERTICAL_ONLY); // 4 rows, 80 columns
		txt.setEditable(false);
		txt.setFont(new Font("Monospaced", Font.PLAIN, 12));
		layout.setConstraints(txt, constraints);
		add(txt); // add text area to applet display

		connect = new Button("Connect to GK");
		buttonPanel.add(connect);
		connect.setActionCommand(CONNECT);
		connect.addActionListener(this);

        refresh = new Button("Refresh status");
        buttonPanel.add(refresh);
        refresh.setActionCommand(REFRESH);
        refresh.addActionListener(this);

        reload = new Button("Reload config");
        buttonPanel.add(reload);
        reload.setActionCommand(RELOAD);
        reload.addActionListener(this);

		disconnect = new Button("Disconnect from GK");
		buttonPanel.add(disconnect);
		disconnect.setActionCommand(DISCONNECT);
		disconnect.addActionListener(this);
		
		endpointPanel = new Panel();
		endpointPanel.setBackground(Color.gray);
		endpointPanel.setLayout(new GridLayout(0,4));
		constraints.weightx = 1.0;
		constraints.weighty = 1.0;
		constraints.anchor = GridBagConstraints.NORTH;
		layout.setConstraints(endpointPanel, constraints);
		add(endpointPanel);

		popup = new PopupMenu("Commands");
		popup.setFont(new Font("Helvetica", Font.BOLD, 12));
		MenuItem pmi1 = new MenuItem("Disconnect");
		pmi1.setActionCommand(DISC_EP);
        pmi1.addActionListener(this);
		popup.add(pmi1);
		MenuItem pmi2 = new MenuItem("Unregister");
		pmi2.setActionCommand(UNREG_EP);
        pmi2.addActionListener(this);
		popup.add(pmi2);
        if (!isApplet) {
				// applet can't get its parent window for dialog
        	MenuItem pmi3 = new MenuItem("Transfer Call");
	    	pmi3.setActionCommand(TRANSFERCALL_EP);
            pmi3.addActionListener(this);
	        popup.add(pmi3);
			MenuItem pmi4 = new MenuItem("Call");
			pmi4.setActionCommand(CALL_EP);
            pmi4.addActionListener(this);
			popup.add(pmi4);
		}
        popup.addActionListener(this);
        add(popup);
		popupListener = new PopupListener();

        disconnected();     // GUI starts with gk disconnected

		if (isApplet) {
			gkHost = getCodeBase().getHost();	// doesn't work with IE ??
			try {
				gkPort = Integer.parseInt(getParameter("port"));
			} finally { gkPort = 7000; };	// default port
			try {
				gkUser = getParameter("user");
				gkPassword = getParameter("password");
			} finally { gkUser = ""; gkPassword = ""; };	// no password
		}
		gk.init(this, gkHost, gkPort, gkUser, gkPassword);
	}

	public void actionPerformed(ActionEvent e) {
		String command = e.getActionCommand();
		// react to buttons
		if (command == CONNECT) {
			new Thread(gk).start();
		} else if (command == DISCONNECT) {
			epList.removeAllElements();
			endpointPanel.removeAll();
			relayout();		// force re-layout
			gk.stop();
        } else if (command == REFRESH) {
            epList.removeAllElements();
            endpointPanel.removeAll();
            relayout();		// force re-layout
            gk.refresh();
        } else if (command == RELOAD) {
            gk.reloadConfig();
		} else if (command == DISC_EP) {
			gk.disconnectAlias(selectedAlias);
		} else if (command == UNREG_EP) {
			Endpoint ep = endpointByAlias(selectedAlias);
			if (ep != null)
				removeEndpoint(ep.epid);	// since we send the UCF ourself, we won't see an event
			gk.unregisterAlias(selectedAlias);
        } else if (command == TRANSFERCALL_EP) {
            Endpoint ep = endpointByAlias(selectedAlias);
            if ((ep != null) && !isApplet) {
                // applet can't get its parent window
                Dialog d = new TransferDialog(mainWindow, gk, ep);
                d.setVisible(true);
            }
        } else if (command == CALL_EP) {
            Endpoint ep = endpointByAlias(selectedAlias);
            if ((ep != null) && !isApplet) {
                // applet can't get its parent window
                Dialog d = new CallDialog(mainWindow, gk, ep);
                d.setVisible(true);
            }
		} else {
			// if it wasn't a command button
			// it's probably a click on an endpoint button
			// too dangerous: gk.disconnectEndpoint(command);
		}
	}

	public void destroy() {
		gk.stop();
	}
  
	public void stop() {
		gk.stop();
	}

	public void start() {
	}

	private void relayout() {
		validate();		// force relayout (not only of endpointPanel)
		if (!isApplet)
			mainWindow.pack();
	}

    /**
     * Callback: now connected to gatekeeper.
     */
    public void connected() {
        connect.setEnabled(false);
        disconnect.setEnabled(true);
        refresh.setEnabled(true);
        reload.setEnabled(true);
    }

    /**
     * Callback: now disconnected from gatekeeper.
     */
    public void disconnected() {
        connect.setEnabled(true);
        disconnect.setEnabled(false);
        refresh.setEnabled(false);
        reload.setEnabled(false);
    }

	public synchronized void appendLog(String _str) {
		if (txt != null) {
			txt.append(_str);
		};
	}
	
	public void addEndpoint(String _epid, String _alias) {
		if (endpointById(_epid) == null) {
			Button ep = new Button(_alias);
			// ep.setActionCommand(_epid);	// only needed for direct click command
			// ep.addActionListener(this);	// only needed for direct click command
			ep.addMouseListener(popupListener);	// add popup menu
			epList.addElement(new Endpoint(_epid, _alias, ep));
			setEndpointState(_epid, Endpoint.AVAILABLE);
			endpointPanel.add(ep);
			relayout();		// force relayout (not only of endpointPanel)
		}
	}
	
	public void removeEndpoint(String _epid) {
		for(int i = 0; i < epList.size(); i++ ) {
			Endpoint ep = (Endpoint) epList.elementAt(i);
			if (_epid.equals(ep.epid)) {
				endpointPanel.remove(ep.button);
				relayout();		// force relayout (not only of endpointPanel)
				epList.removeElementAt(i);
				return;
			}
		}
	}
	
	public void setEndpointState(String _epid, int _state) {
		Endpoint ep = endpointById(_epid);
		if (ep != null) {
			if (_state == Endpoint.AVAILABLE)
				ep.button.setBackground(Color.green);
			else if (_state == Endpoint.TALKING)
				ep.button.setBackground(Color.red);
			else
				ep.button.setBackground(Color.white);
		}
	}
	
	private Endpoint endpointById(String _epid) {
		for(int i = 0; i < epList.size(); i++ ) {
			Endpoint ep = (Endpoint) epList.elementAt(i);
			if (_epid.equals(ep.epid)) {
				return ep;
			}
		}
		return null;
	}
	
	private Endpoint endpointByAlias(String _alias) {
		for(int i = 0; i < epList.size(); i++ ) {
			Endpoint ep = (Endpoint) epList.elementAt(i);
			if (_alias.equals(ep.alias)) {
				return ep;
			}
		}
		return null;
	}

    private class PopupListener extends MouseAdapter {
		public void mousePressed(MouseEvent e) {
            if (e.isPopupTrigger()) {
                // the label of the button is the endpoint alias
                // remember it, so we know for what endpoint an action was requested
                selectedAlias = ((Button)e.getComponent()).getLabel();
                popup.show(e.getComponent(), e.getX(), e.getY());
            }
		}
		public void mouseReleased(MouseEvent e) {
            if (e.isPopupTrigger()) {
                // the label of the button is the endpoint alias
                // remember it, so we know for what endpoint an action was requested
                selectedAlias = ((Button)e.getComponent()).getLabel();
                popup.show(e.getComponent(), e.getX(), e.getY());
            }
		}
	}
}
