/*
 * Wireless Host Monitor
 *
 * Copyright (C) 2003, Jonathan Sevy <jsevy@mcs.drexel.edu>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

package airporthostmon;


import java.util.*;
import java.net.*;
import java.awt.*;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.plaf.*;
import java.awt.event.*;
import java.io.*;
import java.math.*;

import snmp.*;



public class HostMonitor extends JFrame
							implements ActionListener, Runnable
{	
	
	
	JButton newPreferencesButton, discoverDevicesButton, knownHostsButton;
	JTextField airportInternalIPField;
	JTextField airportExternalIPField, airportNameField, airportLocationField;
	JPanel hostPanel;
	
	MenuBar theMenubar;
	Menu fileMenu;
	MenuItem quitItem, aboutItem, savePreferencesItem;
	
	JTextArea messagesArea;
	JTextAreaWriter messagesAreaWriter;
	
	HostInfoTable hostInfoTable;
	
	
	Thread inspectionThread;
	
	Preferences2 preferences;
	boolean preferencesSaved = true;
	String preferencesFilename = "HostMonitor.ini";
	
	TreeMap knownHostTreeMap;
	boolean knownHostsSaved = true;
	String knownHostFilename = "KnownHosts.ini";
	
	SNMPv1CommunicationInterface communicationInterface;
	
	private Vector changeListeners;
	
	HostInfoTreeMap currentInfo;
	
	HostInfoRetriever currentRetriever;
	
	
	
	// WindowCloseAdapter to catch window close-box closings
	private class WindowCloseAdapter extends WindowAdapter
	{ 
		public void windowClosing(WindowEvent e)
		{
		    checkSettings();
		    System.exit(0);
		}
	}
		
		
	
	public HostMonitor()
	{
		    	
    	// read any settings that have previously been saved
		readPreferences(preferencesFilename);
		
		readKnownHostInfo(knownHostFilename);
		
		changeListeners = new Vector();
    	currentInfo = new HostInfoTreeMap();
    	
    	// create thread, but don't start it
    	inspectionThread = new Thread(this);
        
        setUpDisplay();
    	
    	// create Writer interface into messages area for use by change listeners
    	messagesAreaWriter = new JTextAreaWriter(messagesArea);
    	
    	this.pack();
		this.show();
        
		// put up dialog to solicit new settings; this also sets up stuff as indicated
		if (getPreferences() == false)
		{
		    messagesArea.setText("Problem with supplied information; set options again");
		}
		
		
	}
	
	
	
	public void addChangeListener(HostInfoChangeListener changeListener)
	{
	    if (!changeListeners.contains(changeListener))
	        changeListeners.add(changeListener);
	}
	
	
	
	public void removeChangeListener(HostInfoChangeListener changeListener)
	{
	    if (changeListeners.contains(changeListener))
	        changeListeners.remove(changeListener);
	}
	
	
	
	public void removeAllChangeListeners()
	{
	    changeListeners.removeAllElements();
	}
	
	
	
	private void setUpDisplay()
	{
		
		
		
		// set fonts to smaller-than-normal size, for compaction!
		UIManager manager = new UIManager();
		FontUIResource appFont = new FontUIResource("SansSerif", Font.PLAIN, 10);
		UIDefaults defaults = manager.getLookAndFeelDefaults();
		Enumeration keys = defaults.keys();
		
		while (keys.hasMoreElements())
		{
			String nextKey = (String)(keys.nextElement());
			if ((nextKey.indexOf("font") > -1) || (nextKey.indexOf("Font") > -1))
			{
				manager.put(nextKey, appFont);
			}
		}
		
		
		
		// add WindowCloseAdapter to catch window close-box closings
		addWindowListener(new WindowCloseAdapter());

		
		
		this.setTitle("Host Monitor");
		
		this.getRootPane().setBorder(new BevelBorder(BevelBorder.RAISED));
		
		theMenubar = new MenuBar();
		this.setMenuBar(theMenubar);
		fileMenu = new Menu("File");
		
		
		aboutItem = new MenuItem("About...");
		aboutItem.setActionCommand("about");
		aboutItem.addActionListener(this);
		fileMenu.add(aboutItem);
		
		savePreferencesItem = new MenuItem("Save settings");
		savePreferencesItem.setActionCommand("save settings");
		savePreferencesItem.addActionListener(this);
		fileMenu.add(savePreferencesItem);
			
		fileMenu.addSeparator();

		quitItem = new MenuItem("Quit");
		quitItem.setShortcut(new MenuShortcut('q'));
		quitItem.setActionCommand("quit");
		quitItem.addActionListener(this);
		fileMenu.add(quitItem);
		
		theMenubar.add(fileMenu);
		
		
		JLabel airportInternalIPLabel = new JLabel("Base station LAN address:");
		airportInternalIPField = new JTextField(12);
		airportInternalIPField.setText("10.0.1.1");
		airportInternalIPField.setEditable(false);
		airportInternalIPField.setMinimumSize(airportInternalIPField.getPreferredSize());
				
		JLabel airportExternalIPLabel = new JLabel("Base station WAN address:");
		airportExternalIPField = new JTextField(12);
		airportExternalIPField.setEditable(false);
		airportExternalIPField.setMinimumSize(airportExternalIPField.getPreferredSize());
		
		JLabel airportNameLabel = new JLabel("Base station name:");
		airportNameField = new JTextField(20);
		airportNameField.setEditable(false);
		airportNameField.setMinimumSize(airportNameField.getPreferredSize());

		JLabel airportLocationLabel = new JLabel("Base station location:");
		airportLocationField = new JTextField(20);
		airportLocationField.setEditable(false);
		airportLocationField.setMinimumSize(airportLocationField.getPreferredSize());
		
		
        newPreferencesButton = new JButton("Change settings");
		newPreferencesButton.setActionCommand("new preferences");
		newPreferencesButton.addActionListener(this);
		
		discoverDevicesButton = new JButton("Discover devices");
		discoverDevicesButton.setActionCommand("discover devices");
		discoverDevicesButton.addActionListener(this);
		
		knownHostsButton = new JButton("Edit known hosts");
		knownHostsButton.setActionCommand("edit known hosts");
		knownHostsButton.addActionListener(this);
		
		
		
		hostInfoTable = new HostInfoTable(currentInfo, this);
    	hostInfoTable.setMinimumSize(hostInfoTable.getPreferredSize());
		
		
		// set params for layout manager
		GridBagLayout  theLayout = new GridBagLayout();
		GridBagConstraints c = new GridBagConstraints();
		
		c.gridwidth = 1;
		c.gridheight = 1;
		c.fill = GridBagConstraints.NONE;
		c.ipadx = 0;
		c.ipady = 0;
		Insets theMargin = new Insets(2,2,2,2);
		c.insets = theMargin;
		c.anchor = GridBagConstraints.CENTER;
		c.weightx = .5;
		c.weighty = .5;
		
		
		
		// layout buttons in panel
		JPanel buttonPanel = new JPanel();
		buttonPanel.setLayout(theLayout);
		
		
		c.gridx = 1;
		c.gridy = 1;
		theLayout.setConstraints(newPreferencesButton, c);
		buttonPanel.add(newPreferencesButton);
		
		c.gridx = 2;
		c.gridy = 1;
		theLayout.setConstraints(knownHostsButton, c);
		buttonPanel.add(knownHostsButton);
		
		
		
		
		
		// layout host info in panel
		hostPanel = new JPanel();
		hostPanel.setLayout(theLayout);
		
		c.anchor = GridBagConstraints.EAST;
		c.gridx = 1;
		c.gridy = 1;
		theLayout.setConstraints(airportInternalIPLabel, c);
		hostPanel.add(airportInternalIPLabel);
		
		c.anchor = GridBagConstraints.WEST;
		c.gridx = 2;
		c.gridy = 1;
		theLayout.setConstraints(airportInternalIPField, c);
		hostPanel.add(airportInternalIPField);
		
		c.anchor = GridBagConstraints.EAST;
		c.gridx = 1;
		c.gridy = 2;
		theLayout.setConstraints(airportExternalIPLabel, c);
		hostPanel.add(airportExternalIPLabel);
		
		c.anchor = GridBagConstraints.WEST;
		c.gridx = 2;
		c.gridy = 2;
		theLayout.setConstraints(airportExternalIPField, c);
		hostPanel.add(airportExternalIPField);
		
		c.anchor = GridBagConstraints.EAST;
		c.gridx = 1;
		c.gridy = 3;
		theLayout.setConstraints(airportNameLabel, c);
		hostPanel.add(airportNameLabel);
		
		c.anchor = GridBagConstraints.WEST;
		c.gridx = 2;
		c.gridy = 3;
		theLayout.setConstraints(airportNameField, c);
		hostPanel.add(airportNameField);
		
		c.anchor = GridBagConstraints.EAST;
		c.gridx = 1;
		c.gridy = 4;
		theLayout.setConstraints(airportLocationLabel, c);
		hostPanel.add(airportLocationLabel);
		
		c.anchor = GridBagConstraints.WEST;
		c.gridx = 2;
		c.gridy = 4;
		theLayout.setConstraints(airportLocationField, c);
		hostPanel.add(airportLocationField);
		
		
		
		c.anchor = GridBagConstraints.CENTER;
		
		
		
		JPanel messagesPanel = new JPanel();
		messagesPanel.setLayout(theLayout);
		
		messagesArea = new JTextArea(6,40);
		messagesArea.setMinimumSize(messagesArea.getPreferredSize());
		
		JScrollPane messagesScroll = new JScrollPane(messagesArea);
		messagesScroll.setMinimumSize(messagesScroll.getPreferredSize());
		
		c.fill = GridBagConstraints.NONE;
		c.weightx = 0;
		c.weighty = 0;
		c.gridx = 1;
		c.gridy = 1;
		JLabel messagesLabel = new JLabel("Messages:");
		theLayout.setConstraints(messagesLabel, c);
		messagesPanel.add(messagesLabel);
		
		c.fill = GridBagConstraints.BOTH;
		c.weightx = .5;
		c.weighty = .5;
		c.gridx = 1;
		c.gridy = 2;
		theLayout.setConstraints(messagesScroll, c);
		messagesPanel.add(messagesScroll);
		
		
		
		
		this.getContentPane().setLayout(theLayout);
		
		c.fill = GridBagConstraints.NONE;
		c.weightx = 0;
		c.weighty = 0;
		c.gridx = 1;
		c.gridy = 1;
		theLayout.setConstraints(hostPanel, c);
		this.getContentPane().add(hostPanel);
		
		c.gridx = 1;
		c.gridy = 2;
		theLayout.setConstraints(buttonPanel, c);
		this.getContentPane().add(buttonPanel);
		
		c.fill = GridBagConstraints.BOTH;
		c.weightx = .5;
		c.weighty = .5;
		c.gridx = 1;
		c.gridy = 3;
		theLayout.setConstraints(hostInfoTable, c);
		this.getContentPane().add(hostInfoTable);
		
		c.fill = GridBagConstraints.HORIZONTAL;
		c.weightx = .5;
		c.weighty = .5;
		c.gridx = 1;
		c.gridy = 4;
		theLayout.setConstraints(messagesPanel, c);
		this.getContentPane().add(messagesPanel);
		
		c.fill = GridBagConstraints.NONE;
		c.weightx = 0;
		c.weighty = 0;
		c.gridx = 1;
		c.gridy = 5;
		JLabel authorLabel = new JLabel(" Version 2.1        J. Sevy, October 2003 ");
		authorLabel.setFont(new Font("SansSerif", Font.ITALIC, 8));
		theLayout.setConstraints(authorLabel, c);
		this.getContentPane().add(authorLabel);
		
		
		
	}
	
	
	
	
	public void actionPerformed(ActionEvent theEvent)
	// respond to button pushes, menu selections
	{
		String command = theEvent.getActionCommand();
		
	
		if (command.equals("quit"))
		{
			
			checkSettings();
			System.exit(0);
		}
		
		
		
		if (command.equals("about"))
		{
			AboutDialog aboutDialog = new AboutDialog(this);
		}
		
		
		
		if (command.equals("discover devices"))
		{
			AirportDiscoverer discoverer = new AirportDiscoverer();
		}
		
		
		
		if (command.equals("edit known hosts"))
		{
			if ( (theEvent.getSource() instanceof JDataButton) && (((JDataButton)theEvent.getSource()).getData() instanceof MacAddress) )
			{
			    JDataButton button = (JDataButton)theEvent.getSource();
			    MacAddress macAddress = (MacAddress)button.getData();
			    
			    // create new known host
			    KnownHostInfo newKnownHostInfo = new KnownHostInfo("", macAddress);
			    
			    // bring up dialog with new known host added
			    editKnownHostInfo(newKnownHostInfo);
			    
			}
			else
			{
			    editKnownHostInfo();
			}
		}
		
		
		
		if (command.equals("new preferences"))
		{
			getPreferences();
		}
		
		
		if (command.equals("save settings"))
		{
			saveSettings();
		}
		
	}
	
		
	
	
	private void editKnownHostInfo(KnownHostInfo newKnownHostInfo)
	{
	    
	    KnownHostDialog knownHostDialog = new KnownHostDialog(this, knownHostTreeMap);
	    knownHostDialog.addHost(newKnownHostInfo);
	    
	    // now setup and display dialog
        knownHostDialog.pack();

        // tweak app size to make it a little larger than necessary, to address the
        // "shrunken textfields" problem arising from the layout manager packing stuff
        // a little too tightly.
        Dimension dim = knownHostDialog.getSize();
        dim.height += 20;
        dim.width += 20;
        knownHostDialog.setSize(dim);

        knownHostDialog.show();

        // on return, get known host info
        if (!knownHostDialog.isCanceled())
        {
            knownHostTreeMap = knownHostDialog.getKnownHostTreeMap();
            knownHostsSaved = false;
            // new known host info will be used when info next retrieved
        }
        
	}
	
	
	
	private void editKnownHostInfo()
	{
	    
	    KnownHostDialog knownHostDialog = new KnownHostDialog(this, knownHostTreeMap);
        knownHostDialog.pack();

        // tweak app size to make it a little larger than necessary, to address the
        // "shrunken textfields" problem arising from the layout manager packing stuff
        // a little too tightly.
        Dimension dim = knownHostDialog.getSize();
        dim.height += 20;
        dim.width += 20;
        knownHostDialog.setSize(dim);

        knownHostDialog.show();

        // on return, get known host info
        if (!knownHostDialog.isCanceled())
        {
            knownHostTreeMap = knownHostDialog.getKnownHostTreeMap();
            knownHostsSaved = false;
            // new known host info will be used when info next retrieved
        }
        
	}
	
	
	
	private void getBaseStationInfo()
	    throws SocketException, IOException, SNMPGetException, SNMPBadValueException
	{
	    
	    SNMPVarBindList varBindList;
		String valueString;
		
	    varBindList = communicationInterface.getMIBEntry("1.3.6.1.2.1.1.5.0");
		valueString = ((SNMPSequence)varBindList.getSNMPObjectAt(0)).getSNMPObjectAt(1).toString();
		airportNameField.setText(valueString);
		
		varBindList = communicationInterface.getMIBEntry("1.3.6.1.2.1.1.6.0");
		valueString = ((SNMPSequence)varBindList.getSNMPObjectAt(0)).getSNMPObjectAt(1).toString();
		airportLocationField.setText(valueString);
		
		
		if (currentRetriever != null)
		{	    
    		// get WAN IP address (corresponding to interface 3 on Snow and Graphite, 2 on Extreme)
    		// need to retrieve table data with two separate gets, due to Snow AirPort retrieval bug
    		int wanIndex = currentRetriever.getWANInterfaceIndex();
    		SNMPVarBindList ipAddressVarBindList = communicationInterface.retrieveMIBTable("1.3.6.1.2.1.4.20.1.1");
    		SNMPVarBindList interfaceIndexVarBindList = communicationInterface.retrieveMIBTable("1.3.6.1.2.1.4.20.1.2");
    		valueString = getInterfaceAddress(ipAddressVarBindList, interfaceIndexVarBindList, wanIndex);
    		airportExternalIPField.setText(valueString);
		}
		
	}
	
	
	
	
	public String getInterfaceAddress(SNMPVarBindList ipAddressVarBindList, SNMPVarBindList interfaceIndexVarBindList, int interfaceIndex)
	{
	    
	    // make list of interface index / IP address pairs;
	    // find entry corresponding to specified interface, and return its IP address
	    int listSize = interfaceIndexVarBindList.size();
	    if (ipAddressVarBindList.size() < interfaceIndexVarBindList.size())
	    {
	        listSize = ipAddressVarBindList.size();
	    }
	    
	    //System.out.println("ipAddressVarBindList: \n" + ipAddressVarBindList.toString());
	    //System.out.println("interfaceIndexVarBindList: \n" + interfaceIndexVarBindList.toString());
	    
	    for (int i = 0; i < listSize; i++)
        {
            SNMPSequence variablePair = (SNMPSequence)ipAddressVarBindList.getSNMPObjectAt(i);
            SNMPObjectIdentifier snmpOID = (SNMPObjectIdentifier)variablePair.getSNMPObjectAt(0);
            SNMPIPAddress snmpIPAddress = (SNMPIPAddress)variablePair.getSNMPObjectAt(1);
            String ipAddress = snmpIPAddress.toString();
            
            variablePair = (SNMPSequence)interfaceIndexVarBindList.getSNMPObjectAt(i);
            snmpOID = (SNMPObjectIdentifier)variablePair.getSNMPObjectAt(0);
            SNMPInteger snmpValue = (SNMPInteger)variablePair.getSNMPObjectAt(1);
            int retrievedInterfaceIndex = ((BigInteger)snmpValue.getValue()).intValue();
            
            // see if have entry for desired interface index
            if (retrievedInterfaceIndex == interfaceIndex)
            {
                return ipAddress;
            }
            
        }
        
        // if get here, didn't find it...
	    return "";
	}
	
	
	
	public String oldGetInterfaceAddress(SNMPVarBindList varBindList, int interfaceIndex)
	{
	    class InterfaceInfo
	    {
	        String ipAddress;
	        int interfaceIndex;
	    }
	    
	    TreeMap interfaceInfoMap = new TreeMap();
	    
	    
	    
	    
	    // make list of interface index / IP address pairs;
	    // find entry corresponding to specified interface, and return its IP address
	    if (varBindList.size() > 0)
        {
            int i = 0;
            SNMPSequence variablePair = (SNMPSequence)varBindList.getSNMPObjectAt(i);
            SNMPObjectIdentifier snmpOID = (SNMPObjectIdentifier)variablePair.getSNMPObjectAt(0);
            String oid = snmpOID.toString();
                
            String oidBase = "1.3.6.1.2.1.4.20.1.1";
            while(oid.startsWith(oidBase))
            {
                // get index
                String tableIndex = oid.substring(oidBase.length() + 1);
                    
                // add a new InterfaceInfo object to hashtable, hashed by index
                interfaceInfoMap.put(tableIndex, new InterfaceInfo());
                
                // this entry also gives IP address
                InterfaceInfo interfaceInfo = (InterfaceInfo)interfaceInfoMap.get(tableIndex);
                SNMPIPAddress ipAddress = (SNMPIPAddress)variablePair.getSNMPObjectAt(1);
                interfaceInfo.ipAddress = ipAddress.toString();
                i++;

                if (i >= varBindList.size())
                    break;
                
                variablePair = (SNMPSequence)varBindList.getSNMPObjectAt(i);
                snmpOID = (SNMPObjectIdentifier)variablePair.getSNMPObjectAt(0);
                oid = snmpOID.toString();
                
                
            }
            
            oidBase = "1.3.6.1.2.1.4.20.1.2";
            while(oid.startsWith(oidBase))
            {
                // get index
                String tableIndex = oid.substring(oidBase.length() + 1);
                    
                // modify PortInfo in hashtable
                InterfaceInfo interfaceInfo = (InterfaceInfo)interfaceInfoMap.get(tableIndex);
                SNMPInteger snmpInterfaceIndex = (SNMPInteger)variablePair.getSNMPObjectAt(1);
                interfaceInfo.interfaceIndex = ((BigInteger)snmpInterfaceIndex.getValue()).intValue();
                
                // see if have entry for desired interface index
                if (interfaceInfo.interfaceIndex == interfaceIndex)
                {
                    return interfaceInfo.ipAddress;
                }
                
                i++;

                if (i >= varBindList.size())
                    break;
                
                variablePair = (SNMPSequence)varBindList.getSNMPObjectAt(i);
                snmpOID = (SNMPObjectIdentifier)variablePair.getSNMPObjectAt(0);
                oid = snmpOID.toString();
            }
            
        }
        
        // if get here, didn't find it...
	    return "";
	}
	
	
	
	
	private void refreshInfoDisplay(HostInfoTreeMap newInfo)
	{
		// update displayed info
		//messagesArea.append(newInfo.toString());
		hostInfoTable.setInfo(newInfo);
		
	}
	
	
	
	/*
	private String hexByte(byte b)
	{
		int pos = b;
		if (pos < 0)
			pos += 256;
		String returnString = new String();
		returnString += Integer.toHexString(pos/16);
		returnString += Integer.toHexString(pos%16);
		return returnString;
	}
	
	
	
	private String printHexBytes(byte[] bytes)
	{
		String returnString = new String();
		
		for(int i = 0; i < bytes.length; i++)
		{
			returnString += hexByte(bytes[i]) + " ";
			
			if (((i+1)%16) == 0)
				returnString += "\n";
				
		}
		
		return returnString;
		
	}
	
	
	private static boolean arraysEqual(byte[] a, byte[] b)
	{
		if (a.length != b.length)
		{
			return false;
		}
		else
		{
			for (int i = 0; i < a.length; i++)
			{
				if (a[i] != b[i])
					return false;
			}
		}
		
		return true;
	}
	
	
	
	private static byte[] maskBytes(byte[] inBytes, byte[] mask)
	{
		byte[] maskedBytes = new byte[inBytes.length];
		
		for (int i = 0; i < inBytes.length; i++)
		{
			maskedBytes[i] = (byte)(inBytes[i] & mask[i % inBytes.length]);
		}
		
		return maskedBytes;
	}
	*/
	
	
	
	private void invokeChangeListeners(HostInfoTreeMap oldInfo, HostInfoTreeMap newInfo)
	{
	    // just call the processPortInfoChange method of each listener
	    for (int i = 0; i < changeListeners.size(); i++)
	    {
	        try
	        {
	            ((HostInfoChangeListener)changeListeners.elementAt(i)).processHostInfoChange(oldInfo, newInfo);
	        }
	        catch (HostInfoChangeException e)
	        {
	            messagesArea.append("Problem processing retrieved info: " + e.getMessage());
	        }
	    }
	}
	
	
	
	
	
	
	public void run()
	{
		
		
		while(!Thread.currentThread().isInterrupted())
		{
		    
		    try
		    {
    		    // retrieve current info from base station
    		    HostInfoTreeMap newInfo;
    		    
    		    // get base station info (name, location, etc)
    		    getBaseStationInfo();
    		    
    		    // if no retriever assigned, throw exception
    			if (currentRetriever == null)
    			    throw new IOException("No access point type specified");
    			    
    			// get info on attached hosts, using current retriever
    			newInfo = currentRetriever.getHostInfo(communicationInterface);
    			
    			// fill in host names
    			newInfo.setHostNames(knownHostTreeMap);
    			
    			messagesArea.setText("Information retrieved " + (new Date()).toString() + ".\n");
			    
			    // now refresh display
    			refreshInfoDisplay(newInfo);
    			
    			// see if info has changed; if so, take action
    			invokeChangeListeners(currentInfo, newInfo);
    			
    			currentInfo = newInfo;
			}
			catch(IllegalArgumentException ex)
			{
				// thrown by PortInfoTreeMap constructor if no data retrieved
				messagesArea.setText("Error retrieving information (check password)\n");
			}
			catch(SocketException e)
    		{
    			messagesArea.setText("Error retrieving information: " + e + "\n");
    		}
    		catch(IOException e)
    		{
    			messagesArea.setText("Error retrieving information: " + e + "\n");
    		}
    		catch (Exception e)
    		{
    			messagesArea.setText("Error retrieving information: " + e + "\n");
    		}
		
		
			try
			{
				// sleep for inspection interval seconds
				Thread.currentThread().sleep(1000 * preferences.queryInterval);
			}
			catch(InterruptedException e)
			{
				// don't bother informing of interruption
				Thread.currentThread().interrupt();
			}
			
		
		}
		
		
	}
	
	
	
	
	private void checkSettings()
	{
	    
	    if (preferencesSaved == false)
		{
		    // put up dialog to ask if settings should be saved
		    if (JOptionPane.showConfirmDialog(this, "Save current base station settings?","Save settings?",JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION)
		    {
		        savePreferences(preferencesFilename);
		    }
		}
		
		if (knownHostsSaved == false)
		{
		    // put up dialog to ask if settings should be saved
		    if (JOptionPane.showConfirmDialog(this, "Save current known host information?","Save known hosts?",JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION)
		    {
		        saveKnownHosts(knownHostFilename);
		    }
		}
	
	}
	
	
	
	private void saveSettings()
	{
	    savePreferences(preferencesFilename);
	    saveKnownHosts(knownHostFilename);
	}
	
	
	
	private boolean getPreferences()
	{
	    
	    // get list of registered retrievers
    	TreeMap retrievers = HostInfoRetriever.getRetrievers();
    	String[] retrieverNames = new String[retrievers.size()];
    	retrieverNames = (String[])retrievers.keySet().toArray(retrieverNames);
    	
    	
    	PreferencesDialog theDialog = new PreferencesDialog(this, preferences, retrieverNames);
			
        if (!theDialog.isCancelled())
        {
        	
        	// stop thread if needed
        	if (inspectionThread.isAlive())
        	{
        	    inspectionThread.interrupt();
        	}
        	
        	// get preferences
        	Preferences2 newPreferences = theDialog.getPreferences();
        	
        	
        	try
    		{
    			
    			if (!newPreferences.equals(preferences))
    		        preferencesSaved = false;
    		        
    		    // determine which retriever should be used
    		    currentRetriever = (HostInfoRetriever)retrievers.get(newPreferences.accessPointType);
    		    
    		    InetAddress airportInternalIPAddress = InetAddress.getByName(newPreferences.ipAddress);
    			airportInternalIPField.setText(newPreferences.ipAddress);
    			
    			// create new SNMP communication interface
    			communicationInterface = new SNMPv1CommunicationInterface(0, airportInternalIPAddress, newPreferences.password);
    			
    			// remove current action object(s)
                this.removeAllChangeListeners();
                
                // create and add new action object(s)
                if (newPreferences.logFlag == true)
                {
                    // add logger
                    HostInfoLogger logger = new HostInfoLogger(newPreferences.logFile, newPreferences.logMode);
                	this.addChangeListener(logger);
                }
                
                if (newPreferences.beepNotifyFlag == true)
                {
                    HostInfoBeepAlerter changeBeeper = new HostInfoBeepAlerter(newPreferences.notifyMode);
    	            changeListeners.add(changeBeeper);
    	        }
    	        
    	        
    	        if (newPreferences.emailNotifyFlag == true)
                {
                    HostInfoChangeEmailer changeEmailer = new HostInfoChangeEmailer(newPreferences.emailAddress, newPreferences.emailSMTPHost, newPreferences.notifyMode, new JTextAreaWriter(messagesArea));
    	            changeListeners.add(changeEmailer);
    	        }
    	        
    	        
    			preferences = newPreferences;
    			
    			inspectionThread = new Thread(this);
    		    inspectionThread.start();
    		    
    		    return true;
    		
    		}
    		catch(UnknownHostException e)
    		{
    			JOptionPane.showMessageDialog(this, "Unknown host name supplied.");
    		}
    		catch(Exception e)
    		{
    			JOptionPane.showMessageDialog(this, "Error setting new preferences: " + e);
    		}
	        
        }
        
        return false;
	}



	private void savePreferences(String fileName)
	{
	    // save into file HostMonitor.ini
	    ObjectOutputStream outStream;
	    
	    try
	    {
    	    outStream = new ObjectOutputStream(new FileOutputStream(fileName));
    	    outStream.writeObject(preferences);
    	    outStream.flush();
    	    outStream.close();
    	    preferencesSaved = true;
	    }
	    catch (Exception e)
	    {
	        // oh well...
	        messagesArea.setText("Couldn't write base station settings: " + e.toString());
	        System.out.println("Couldn't write base station settings: " + e.toString());
	        preferencesSaved = false;
	    }
	    
	}
	
	
	
	private void saveKnownHosts(String fileName)
	{
	    // save into file HostMonitor.ini
	    ObjectOutputStream outStream;
	    
	    try
	    {
    	    outStream = new ObjectOutputStream(new FileOutputStream(fileName));
    	    outStream.writeObject(knownHostTreeMap);
    	    outStream.flush();
    	    outStream.close();
    	    knownHostsSaved = true;
	    }
	    catch (Exception e)
	    {
	        // oh well...
	        messagesArea.setText("Couldn't write known host info: " + e.toString());
	        System.out.println("Couldn't write known host info: " + e.toString());
	        knownHostsSaved = false;
	    }
	    
	}
	
	
	
	private void readPreferences(String fileName)
	{
	    // read from file fileName
	    
	    try
	    {
	        ObjectInputStream inStream = new ObjectInputStream(new FileInputStream(fileName));
	        Object preferencesObject = inStream.readObject();
	        
	        if (preferencesObject instanceof Preferences2)
	        {
	            Preferences2 retrievedPreferences = (Preferences2)preferencesObject;
	            preferences = new Preferences2((Preferences2)preferencesObject);
	            preferencesSaved = true;
	        }
	        else if (preferencesObject instanceof Preferences)
	        {
	            preferences = new Preferences2((Preferences)preferencesObject);
	            preferencesSaved = false;
	        }
	        else
	        {
	            throw new ClassCastException("Didn't recognize class of stored preferences");
	        }
	           
	        inStream.close();
	        
	    }
	    catch (Exception e)
	    {
	        // couldn't read; just return empty settings
	        //messagesArea.setText("Couldn't read settings: " + e.toString());
	        //System.out.println("Couldn't read settings: " + e.toString());
	        preferences = new Preferences2();
	        preferencesSaved = false;
	    }
	    
	}
	
	
	
	private void readKnownHostInfo(String fileName)
	{
	    // read from supplied file
	    
	    try
	    {
	        ObjectInputStream inStream = new ObjectInputStream(new FileInputStream(fileName));
	        knownHostTreeMap = (TreeMap)inStream.readObject();
	        inStream.close();
	        knownHostsSaved = true;
	    }
	    catch (Exception e)
	    {
	        // couldn't read; just return empty treemap
	        //messagesArea.setText("Couldn't read known host info: " + e.toString());
	        System.out.println("Couldn't read known host info: " + e.toString());
	        knownHostTreeMap = new TreeMap();
	        knownHostsSaved = true;
	    }
	    
	}
	
	
	
	public static void main(String args[]) 
	{
		try
		{
			
			HostMonitor theApp = new HostMonitor();
			
		}
		catch (Exception e)
		{}
	}
	

}