//*****************************************************************************************
//*																						  *
//*		Congressional Memory Project 													  *
//*																						  *		                                                                                Bill Wallis 																	  *			  *
//*																		            	  *
//*     Last Revisions: 3/13/97															  *
//*																						  *
//*****************************************************************************************

//{{ import class libraries

import java.awt.*;
import java.applet.*;
import sun.audio.*;
import java.net.URL;
import java.net.URLConnection;
import java.net.MalformedURLException;
import java.io.ByteArrayOutputStream;
import java.io.ByteArrayInputStream;
import java.io.DataOutputStream;
import java.io.DataInputStream;
import java.io.InputStream;
import java.io.IOException;
import java.util.StringTokenizer;
import java.util.Vector;
import java.util.Date;
import java.util.Hashtable;

//}}



public class House extends Applet {


    //{{ Declare parameter variables

    String date_parameter = null;    
    String speaker_file_suffix = null;
    String audio_file_prefix = null;
    String audio_file_suffix = null;
	String segment_toc_file = null;
	String speaker_info_file = null;
	String imageFileNames[] = null;
	int number_of_images = 0;
	int button_height = 0;
	int button_width = 0;
	

    //}}


    //{{ Declare search labels

    String timeCustomLabel = null;
	String listAllLabel = null;
	
    //}}

    Vector listItemVector = new Vector();
    AudioClip audio = null;



    //{{ Declare interface components

	Panel searchPanel = null;
    Canvas canvas = null;
    List messageArea = null;
    TextArea transcriptArea = null;
    TextField statusField = null;
    ImageButton searchButton = null;
    ImageButton displayButton = null;
    ImageButton clearButton = null;
    Choice searchChoice = null;
    
	//}}



	//{{ Declare Audio Controls 

	AudioBar audioBar = null;

	//}}


	//{{ Declare reference tables

	RepInfoHashtable repTable = null;
	SegmentHashtable segmentTable = null;

	//}}


    public void init() {

        //{ Execute Setup Routine
        
		getParameters();
		createHashTables();
        createGUI();
        
		//}}
    }


    public void getParameters() {

        //{{ Initialize HTML parameterss

        date_parameter = getParameter("date");
        speaker_file_suffix = getParameter("speaker_file_suffix");
        audio_file_prefix = getParameter("audio_file_prefix");
        audio_file_suffix = getParameter("audio_file_suffix");
		segment_toc_file = getParameter("segment_toc_file");
		speaker_info_file = getParameter("speaker_info_file");
		number_of_images = Integer.parseInt(getParameter("number_of_images"));
		button_height = Integer.parseInt(getParameter("button_height"));
		button_width = Integer.parseInt(getParameter("button_width"));


		imageFileNames = new String[number_of_images];

		for (int i = 0; i < number_of_images; i++) {
			imageFileNames[i] = getParameter("image"+Integer.toString(i+1));
		}

		//}}
    }


	public void createHashTables() {
		
		//{{ initialize hashtables
		
		repTable = new RepInfoHashtable(getCodeBase(),speaker_info_file);
	    segmentTable = new SegmentHashtable(getCodeBase(),segment_toc_file);
		
		//}}
	}


    public void setStatus(String string) {
		
		//{{ display a message in the southern status field
        
		statusField.setText(string);
		
		//}}
    }

	  
    public void listAllSpeakers() {
		
		//{{ perform a search without search constraints
        
		setStatus("Compiling speaker list...");
        search(new SearchConstraints());
		
		//}}
    }


    public void processResult(XFrame source, SearchConstraints result) {
		
		//{{ perform a search based on parameters obtained from class XFrame
		
		setStatus("Processing Result...");

		search(result);        

		//}}
    }

    public String getSearchType() {
		
		//{{ determine the the global filter type
        
		return searchChoice.getSelectedItem();
		
		//}}
    }

    public void search(SearchConstraints constraints) {
 
		//{{ initialize local variables
        
		boolean resultsFound = false;
		StringTokenizer tokenizedFile = null;       
		String searchType = getSearchType();
		
		//}}
        
		//{{ clear the results of a previous search 
        
		processClearButton();
		
		//}}

        setStatus("Searching by "+searchType+"..."+constraints.getParameterString());
		
		//{{tokenize the speaker index file by line and apply the search constraints
        
		tokenizedFile = new StringTokenizer(FileContent.toString(getCodeBase(),date_parameter+speaker_file_suffix),"\n");
		

        String currentToken = null;
        SpeakerListItem speakerItem = null;

        while (tokenizedFile.hasMoreTokens()) {

			speakerItem = new SpeakerListItem(tokenizedFile.nextToken(), repTable, segmentTable);
    										  
			if (constraints.testForConstraints(speakerItem)) {		
                if (resultsFound == false) {resultsFound = true;}
				listItemVector.addElement(speakerItem);				
            }
        }
	
		//{{add the time and name of result items to the result message area
       
		for (int i = 0; i < listItemVector.size(); i++) {
			SpeakerListItem item = (SpeakerListItem)listItemVector.elementAt(i);
			int offset = segmentTable.getStartTime(item.segmentNumber);
			String time = item.getAbsoluteTime();
            messageArea.addItem(time+" "+item.speakerName);
        }
	   
		//}}

        if (resultsFound) {
			messageArea.select(0);
			displayItem(true);
		} else {setStatus("No matches found.");}

        //}}
    }

    public void displayItem(boolean searchSpecified) {

        if (messageArea.countItems() > 0) {

			//{{ destory any existing audio threads
			
			audioBar.killAudio();
			
			//}}

			//{{ load and display transcript information
            
			transcriptArea.setText("");
            setStatus("Loading transcript file..");
            SpeakerListItem item = (SpeakerListItem)listItemVector.elementAt(messageArea.getSelectedIndex());
            transcriptArea.setText(FileContent.toString(getCodeBase(),date_parameter+"."+item.transcriptNumber+".txt"));        		
			
			//}}

			//{{ load and initialize audio data
            
			String filename = audio_file_prefix+"."+item.segmentNumber+audio_file_suffix;								
			setStatus("Loading Audio data...");
			audioBar.setSoundClip(getCodeBase(),filename,item.startTime,item.duration);				
			
			//}}

			//{{ display a message when data has been loaded
		
			setStatus("Speaker: "+item.speakerFullName+"    Audio: "+ConvertTime.getLongFormat(item.duration));
			
			//}}

		} else if (searchSpecified) {setStatus("No Search Specified.");
        } else {setStatus("No Current Item Selected.");}		
    }

    public void initDialog() {

		//{{ initialize a new search dialog
        
		setStatus("Spawning Search Dialog.");
		XFrame g = new XFrame(this,"Speaker Search Dialog");
		g.show();
		//}}

    }

	public boolean handleEvent(Event event) {		


		if (event.target instanceof List) {
			if (event.id == Event.ACTION_EVENT) {
				displayItem(true);
			}
		}

		//{{ process imagebutton events 
		
		if (event.target == searchButton && event.id == Event.MOUSE_UP) {
            serveDialog(searchChoice.getSelectedItem());
        } else if (event.target == clearButton && event.id == Event.MOUSE_UP) {
            processClearButton();
        } else if (event.target == displayButton && event.id == Event.MOUSE_UP) {
            displayItem(true);
		}	
		
		//}}

		return super.handleEvent(event);
	}


    public void processClearButton() {
        messageArea.clear();
        listItemVector.removeAllElements();
        transcriptArea.setText("");
		setStatus("Search Results Cleared.");
		audioBar.killAudio();
    }

    public void serveDialog(String dialogType) {
        if (dialogType.equals(timeCustomLabel)) {
            initDialog();
        } else if (dialogType.equals(listAllLabel)) {
            listAllSpeakers();
        }
    }


    public void createGUI() {

     
		//{{ Initialize Level 0 Components

        setBackground(new Color(192,192,192));
		GridBagLayout gbl = new GridBagLayout();
		GridBagConstraints gbc = new GridBagConstraints();
		gbc.weightx = 100;
		gbc.weighty = 25;
        setLayout(gbl);

		//}}
		
		//{{ Construct Level 1 

			//{{ Construct Northern Search Panel

			searchPanel = new Panel();
			searchPanel.setLayout(new FlowLayout(FlowLayout.LEFT,8,0));      		

				//{{ Construct Search Label		
				
				Label searchLabel = new Label("Search by:",Label.LEFT);
				
				//}}

				//{{ Construct Search Choice Box
				
				searchChoice = new Choice();		 
				timeCustomLabel = "Chronological Speaker search";
				listAllLabel = "List All Speakers";        
				searchChoice.addItem(timeCustomLabel);
				searchChoice.addItem(listAllLabel);
				
				//}}

				//{{ Construct Button Panel
				
				Panel buttonPanel = new Panel();
				buttonPanel.setLayout(new GridLayout(1,3));        
				searchButton = new ImageButton(getCodeBase(),imageFileNames[0],button_width,button_height);
				displayButton = new ImageButton(getCodeBase(),imageFileNames[1],button_width,button_height);
				clearButton = new ImageButton(getCodeBase(),imageFileNames[2],button_width,button_height);
				audioBar = new AudioBar(getCodeBase(),imageFileNames,button_width,button_height);               
				
				//}}
			

			//{{ Add SearchPanel Components
			
			buttonPanel.add(searchButton);
			buttonPanel.add(displayButton);	
			buttonPanel.add(clearButton);
			searchPanel.add(searchLabel);
			searchPanel.add(searchChoice);
			searchPanel.add(buttonPanel);
			searchPanel.add(audioBar);
			gbc.fill = GridBagConstraints.HORIZONTAL;
			gbc.anchor = GridBagConstraints.CENTER;
			add(searchPanel,gbl,gbc,0,0,4,1);
			
			//}}
       

			//{{ Construct and Add Text Area Labels
			
			Label transcriptLabel = new Label("Transcript Text:",Label.CENTER);
			Label messageLabel = new Label("Search Results: ",Label.CENTER);	
			add(transcriptLabel,gbl,gbc,0,1,2,1);
			add(messageLabel,gbl,gbc,2,1,2,1);						
			//}}


			//{ Construct and Add Text Areas
			
			transcriptArea = new TextArea();
			messageArea = new List(20,false); 
			gbc.weighty = 100;
			gbc.fill = GridBagConstraints.BOTH;
			add(transcriptArea,gbl,gbc,0,2,2,2);
			add(messageArea,gbl,gbc,2,2,2,2);			
			//}}					


			//{{ Construct Southern Status Field
			
			Panel statusPanel = new Panel();
			statusPanel.setLayout(new BorderLayout());
			statusField = new TextField(40);
			statusField.setEditable(false);
			statusField.setBackground(new Color(192,192,192));
			statusPanel.add("Center",statusField);
			gbc.fill = GridBagConstraints.HORIZONTAL;
			add(statusPanel,gbl,gbc,0,5,4,1);
			
			//}}        			

		//}}
       		
        setStatus("Session Initialized.");		
    }   


	private void add(Component c, GridBagLayout gbl, GridBagConstraints gbc, int x, int y, int w, int h) {
	   
	   //{{ bind gridBagConstraints arguments and add the compoenent to the layout manager

	   gbc.gridx = x;
	   gbc.gridy = y;
	   gbc.gridwidth = w;
	   gbc.gridheight = h;
	   gbl.setConstraints(c,gbc);
	   add(c);	 
	   //}}
	}

}



//{{ Class Usage: store and apply search constraints

class SearchConstraints {
	
	//{{ initialize local variables

	boolean ignoreCase = true; 

	final String nameLabel = "name";
	final String stateLabel = "state";
	final String partyLabel = "party";
	final String timeLabel = "time";

    private Vector nameConstraints = new Vector();
	private Vector stateConstraints = new Vector();
	private Vector partyConstraints = new Vector();
	private Vector timeConstraints = new Vector();

	//}}

	//{{ create an empty constructor

	public SearchConstraints() {
	}

	//}}
    

	//{{ create convenience methods for adding search constraints

	public void addNameConstraint(String nameString) {
		addToConstraintVector(nameLabel,nameString);				
	}

	public void addStateConstraint(String stateString) {
		addToConstraintVector(stateLabel,stateString);
	}

	public void addPartyConstraint(String partyString) {
		addToConstraintVector(partyLabel,partyString);
	}

	public void addTimeConstraint(String timeString) {
		addToConstraintVector(timeLabel,timeString);	
	}

	//}}


	//{{ create a generic constraint server, allowing expansion for tagged data types

	private void addToConstraintVector(String constraintType, String constraints) {
	
			StringTokenizer tokenizer = new StringTokenizer(constraints,"&");	
			while (tokenizer.hasMoreTokens()) {
				if (constraintType.equals(nameLabel)) {
					nameConstraints.addElement(tokenizer.nextToken());;
				} else if (constraintType.equals(stateLabel)) {
					stateConstraints.addElement(tokenizer.nextToken());
				} else if (constraintType.equals(partyLabel)) {
					partyConstraints.addElement(tokenizer.nextToken());
				} else if (constraintType.equals(timeLabel)) {
					timeConstraints.addElement(tokenizer.nextToken());
				}
			}
	}

	//}}


	//{{ allow itemization of search constraints

    public String getParameterString() {

        StringBuffer stringBuffer = new StringBuffer();
		int i, j, k;
	
        for (i = 0; i < nameConstraints.size(); i++) {
            if (i > 0) {stringBuffer.append("&");}
            stringBuffer.append(nameConstraints.elementAt(i));
        }

		for (j = 0; j < stateConstraints.size(); j++) {
			if (j > 0 || i > 0) {stringBuffer.append("&");}
			stringBuffer.append(stateConstraints.elementAt(j));
		}

		for (k = 0; k < partyConstraints.size(); k++) {
			if (k > 0 || i > 0 || j > 0) {stringBuffer.append("&");}
			stringBuffer.append(partyConstraints.elementAt(k));
		}

        return stringBuffer.toString();
    }

	//}}


	//{{ compare two strings, allows case insensitivity

	private boolean compare(String string1, String string2) {

		if (ignoreCase) {
			return string1.toLowerCase().equalsIgnoreCase(string2);
		} else return string1.equals(string2);	 
	}

	//}}


	//{{ test for constraints by type

    private boolean testName(String string) {
        
		if (nameConstraints.size() > 0) {        	
			for (int i = 0; i < nameConstraints.size();i++) {
				if (compare(string,(String)nameConstraints.elementAt(i)))
					return true;
			}
			return false;
        } else return true;
    }

	private boolean testState(String string) {
		if (stateConstraints.size() > 0) {
			for (int i = 0; i < stateConstraints.size(); i++) {
				if (compare(string,(String)stateConstraints.elementAt(i)))
					return true;
			}
			return  false;
		} else return true;
	}

	private boolean testParty(String string) {
		if (partyConstraints.size() > 0) {
			return partyConstraints.elementAt(0).toString().startsWith(string);
		} else return true;
	}

	private boolean testTime(String string) {

		if (timeConstraints.size() > 0) {
			if (timeConstraints.elementAt(0).toString().compareTo(string) > 0) 
				return false;
			if (timeConstraints.size() > 1) 
				if (timeConstraints.elementAt(1).toString().compareTo(string) < 0)
					return false;
			return true;	
		} else return true;
	}

	//}}

	//{{ decompose speakerItem and test for constraints

    public boolean testForConstraints(SpeakerListItem item) {	

		String n = item.speakerName;
		String s = item.speakerState;
		String p = item.speakerParty;
		String t = ConvertTime.get24HourFormat(item.startTime,item.absoluteOffset).substring(0,5);

        return testName(n)&testState(s)&testParty(p)&testTime(t);
    }
   
	//}}

}



class SpeakerListItem {


	//{{ initialize local variables

    public static final String filename = "segment.toc";

    URL path = null;
    boolean initialized;
	String speakerFullName = null;
    String speakerName = null;
	String speakerState = null;
	String speakerParty = null;
    String segmentNumber = null;
    String transcriptNumber  = null;
    int transcriptIndex = 0;
    int startTime = 0;
    int finishTime = 0;
    int duration = 0;
	int absoluteOffset = 0;

	//}}

    public SpeakerListItem(String listString, RepInfoHashtable r, SegmentHashtable s) {
		 initialized = false;
		 assignLocalVariables(listString,r,s);      
    }

    public void assignLocalVariables(String listString, RepInfoHashtable r, SegmentHashtable s) {

         StringTokenizer tokenizedLine = null;
         tokenizedLine = new StringTokenizer(listString,":");
         speakerName = capitalizeWord(tokenizedLine.nextToken());
         transcriptIndex = Integer.parseInt(tokenizedLine.nextToken());
         segmentNumber = tokenizedLine.nextToken();
         startTime = Integer.parseInt(tokenizedLine.nextToken());
		 tokenizedLine.nextToken();
         finishTime = Integer.parseInt(tokenizedLine.nextToken());
         duration = Integer.parseInt(tokenizedLine.nextToken());
         transcriptNumber = tokenizedLine.nextToken();
		 speakerFullName = r.getFullName(speakerName);
		 speakerState = r.getState(speakerName);
		 speakerParty = r.getParty(speakerName);
		 absoluteOffset = s.getStartTime(segmentNumber);
    }

	public String capitalizeWord(String word) {

		//{{ capitalize the first letter of a string

        char chars[] = word.toCharArray();
        if ((int)chars[0] > 90) {
            chars[0] = (char)((int)chars[0]-32);
        }

		//}}

        return new String(chars,0,word.length());
    }


    public String getAbsoluteTime() {

		//{{ get the start time of the audioclip relative to midnight
		
        return ConvertTime.getShortFormat(startTime,absoluteOffset);

		//}}
    }
                 
}


class ConvertTime {

	
	public static String getShortFormat(int seconds) {
		return getShortFormat(seconds, 0);
	}


	public static String getShortFormat(int secs, int offset) {
		
		//{{ get a 12-hour base time string in the format HH:MM:SS timeSuffix

		StringBuffer timeString = null;	
		String timeSuffix = "am";

		int interval = secs+offset;		
		int hours = 0;
		int minutes = 0;
		int seconds = 0;		

		timeString = new StringBuffer();
        hours = (int)(interval / 3600);
        interval -= hours*3600;
        if (hours > 12) {hours-=12; timeSuffix = "pm";}
		if (hours < 10) {timeString.append("0");}
        timeString.append(hours+":");

        minutes = (int)(interval / 60);
        interval -= minutes*60;
        if (minutes < 10) {timeString.append("0");}
        timeString.append(minutes+":");

        seconds = (int)(interval);
        if (seconds < 10) {timeString.append("0");}
        timeString.append(seconds);

		//}}

		return timeString.toString()+" "+timeSuffix;
	}
	 	
	public static String get24HourFormat(int secs, int offset) {


		//{{ construct a string for comparision in the SeachConstraints module

		StringBuffer timeString = null;	

		int interval = secs+offset;		
		int hours = 0;
		int minutes = 0;
		int seconds = 0;		

		timeString = new StringBuffer();
        hours = (int)(interval / 3600);
        interval -= hours*3600;
		if (hours < 10) {timeString.append("0");}
        timeString.append(hours+":");

        minutes = (int)(interval / 60);
        interval -= minutes*60;
        if (minutes < 10) {timeString.append("0");}
        timeString.append(minutes+":");

        seconds = (int)(interval);
        if (seconds < 10) {timeString.append("0");}
        timeString.append(seconds);

		//}}

		return timeString.toString();
	}


	public static String getLongFormat(int secs) {

		//{{ get a time string in the format M minutes, S seconds

		int interval = secs;
		int minutes = 0;
		int seconds = 0;

        minutes = (int)(interval / 60);
        interval -= minutes*60;
        seconds = (int)(interval);

		//}}

        return minutes+" minute(s) "+seconds+" seconds";
    }
}


class AudioBar extends Panel {


	//{{ intialize local variables

    String textMessage = null;
    URL soundURL = null;
    TextField textField = null;
    ImageButton pauseButton = null,stopButton = null, playButton=null;
	Thread audioThread = null;
    AudioController audioController = null;
    AudioStream audioStream = null;	
	byte bytes[] = null;
	boolean threadSuspend = false;
	boolean isByteInput = false;
	boolean showMessageBar = true;

	//}}

    public AudioBar(URL codeBase, String imageFileNames[], int w, int h) {

		audioController = new AudioController();
		audioThread = null;
		soundURL = null; 
		setupGUI(codeBase,imageFileNames,w,h);
    }

	public void setupGUI(URL codeBase,String imageFileNames[], int w, int h) {
		
		setLayout(new FlowLayout());
		
		//allow message bar to be abscent from the display
		
		textField = new TextField(18);	
		textField.setEditable(false);
		textField.setBackground(new Color(255,255,255));
		add(textField);
		setDefaultMessage();
	
		//}}

		
		//{{ create audio bar buttons

		Panel buttonPanel = new Panel();
		buttonPanel.setLayout(new GridLayout(1,3));
		stopButton = new ImageButton(codeBase,imageFileNames[3],w,h);
		pauseButton = new ImageButton(codeBase,imageFileNames[4],w,h);
		playButton = new ImageButton(codeBase,imageFileNames[5],w,h);
		buttonPanel.add(stopButton);
		buttonPanel.add(pauseButton);
		buttonPanel.add(playButton);
		add(buttonPanel);

		//}}

	}


	public boolean isCurrentClip(String string) {

		if (textMessage.equals(string)) {
			return true;
		} else {return false;}

	}


	public void setDefaultMessage() {

		//{{ display a default message in the audio message bar

		textMessage = new String("No AudioClip Selected");
		textField.setText(textMessage);

		//}}
	}

    private void setMessage(String text) {

		//{{ display audiobar status messages

        textField.setText(text);

		//}}
    }


    public void setSoundClip(URL path, String filename) {
        
		//{{ setup an audioclip from a file source

		isByteInput = false;

        try { soundURL = new URL(path,filename); 
			  setMessage(filename);}
            catch (MalformedURLException e) {};	
			
		//}}
    }


	public void setSoundClip(URL path,String filename,int startTime, int duration) {

		//{{ setup an audioclip from a byteArray source

		isByteInput = true;
		SunAudioParser p = new SunAudioParser(path,filename,startTime,duration);				
		bytes = p.getIndexedAudio();										
		setMessage(filename);

		//}}
	}


	public AudioStream getAudioStream() {

		//{{ get an audioStream 

		AudioStream audioStream = null; 

		if (isByteInput) {
			try {audioStream = new AudioStream(new ByteArrayInputStream(bytes));}
				catch (IOException e) {};
		} else {
			try { audioStream = new AudioStream(soundURL.openStream());}
				catch (IOException e) {};
		}


		//}}

		return audioStream;
	}



    public void startClip() {
        
		//{{ start a new audipclip

		setMessage("AudioClip Playing.");
		audioStream = getAudioStream();
        threadSuspend = false;
        audioThread = new Thread(new AuSound(audioStream));
        audioThread.start();
        audioController.resumeAudio();       

		//}}

    }


    public void restartClip() {

		//{{ restart an audioclip which is currently playing

        setMessage("AudioClip Playing.");
		audioThread.stop();
        audioController.stopAudio(audioStream);
		audioStream = getAudioStream();
        threadSuspend = false;
        audioThread = new Thread(new AuSound(audioStream));
        audioThread.start();

		//}}
    }


	public void killAudio() {

		//{{ destroy an audioclip that is currently playing

		if (audioThread != null) {
			if (audioController.isPlaying()) {                                                       
				audioController.stopAudio(audioStream);
                audioThread.stop();
		        audioThread = null;
                
			}
		}
		setDefaultMessage();
		soundURL = null;

		//}}
	}


    public boolean handleEvent(Event event) {
   
		//{{ handle audio control events

        if (event.target instanceof ImageButton) {
            if (event.target == playButton && event.id == Event.MOUSE_UP) {
               if (audioThread == null) {
                    startClip();                       
               } else if (threadSuspend == false) {
                    restartClip();                      
                } else {
				    setMessage("AudioClip Playing");
                    audioController.resumeAudio();      
                    threadSuspend = false;
                }
                return true;
            } else if (event.target == pauseButton && event.id == Event.MOUSE_UP) {                                                       
                if (audioThread != null) {
                    if (threadSuspend == false) {                                                      
                     audioController.pauseAudio();
                     threadSuspend = true;
					 setMessage("AudioClip Paused.");
                    } else  {
						setMessage("AudioClip Playing");
                        audioController.resumeAudio();
                        threadSuspend = false;
                    }
                    return true;
                }
            } else if (event.target == stopButton && event.id == Event.MOUSE_UP) {                                                        
                if (audioThread != null) {
                    if (audioController.isPlaying()) {                                                       
                        audioController.stopAudio(audioStream);
                        audioThread.stop();
                        audioThread = null;
						setMessage("AudioClip Stopped.");
                        return true;
                    }
                }
            }

        }

		//}}

        return super.handleEvent(event);
    }

}


class AudioController {


    public AudioController() {
    }


	//{{ provide convenience methods for controlling the internal audio server

    public void playAudio(AudioStream inputStream) {
        AudioPlayer.player.start(inputStream);
    }

    public void pauseAudio() {
        AudioPlayer.player.suspend();
    }

    public void resumeAudio() {
        AudioPlayer.player.resume();
    }


    public void stopAudio(AudioStream inputStream) {
        AudioPlayer.player.stop(inputStream);
    }

    public boolean isPlaying() {
        return AudioPlayer.player.isAlive();
    }

	//}}

}


class AuSound implements Runnable  {


    public static AudioStream inputStream;
    URL soundURL;

    public AuSound(String string) {

		//{{ construct an audioStream from a string url

		try { soundURL = new URL(string); }
            catch (MalformedURLException e) {};

        try { inputStream = new AudioStream(soundURL.openStream());}
            catch (IOException e) {};

		//}}
    }

    public AuSound(AudioStream audioStream) {

		// obtain a audioStream directly

        inputStream = audioStream;

		//}}
    }


    public void playAudio() {

		//{{ start the audio clip

        AudioPlayer.player.start(inputStream);

		//}}
    }


	//{{ run the audioclip as a background process

    public void run() {
        playAudio();
    }

	//}}

}


class ImageButton extends Canvas { 


	//{{ initialize local variables

	boolean paintIndentBorder = false;
	boolean paintExtendBorder = false;
	boolean needMetrics = true;
	boolean isImage = true;
		
	int xCord = 0; 
	int yCord = 0;
	int margin = 0;

	String text = null;
	Image image = null;
	URL imageURL = null;
	MediaTracker tracker = null;
	FontMetrics fm = null;

	//}}
	
	
	public ImageButton(URL path, String filename, int x, int y) {
	
		//{{ construct a button with an image source

		setBackground(new Color(192,192,192));					
		try {imageURL = new URL(path,filename);}
		catch (MalformedURLException e) {};

		image = Toolkit.getDefaultToolkit().getImage(imageURL);
		tracker = new MediaTracker(this);
		tracker.addImage(image,0);
		try {tracker.waitForID(0);}
		catch (InterruptedException e) {};

		margin = 4;
		xCord = x;
		yCord = y;
		resize(xCord+2*margin,yCord+2*margin);	
		repaint();

		//}}

	}


	public ImageButton (String string, int x , int y) {

		//{{ construct button with a text source

		isImage = false;
		setBackground(new Color(192,192,192));
		text = string;
		margin =2;
		xCord = x;
		yCord = y;
		resize(xCord+2*margin,yCord+2*margin);	

		//}}
	}


	public void paint(Graphics g) { 

		//{{ paint button states

		g.setColor(getBackground());
		g.fillRect(0,0,size().width,size().height);
		
		if (paintExtendBorder) {
			g.setColor(new Color(240,240,240));			
			g.drawLine(1,1,size().width-1,1);
			g.drawLine(1,1,1,size().height);		
			g.setColor(new Color(64,64,64));
			g.drawLine(1, size().height-1,size().width,size().height-1);
			g.drawLine(size().width-1,1,size().width-1,size().height);			
			paintExtendBorder = false;
		}

		if (paintIndentBorder) {
		 g.setColor(new Color(64,64,64));
		 g.drawLine(1,1,size().width-1,1);
		 g.drawLine(1,1,1,size().height);
		 g.setColor(new Color(240,240,240));
		 g.drawLine(1, size().height-1,size().width,size().height-1);
		 g.drawLine(size().width-1,1,size().width-1,size().height);
		 paintIndentBorder = false;
		}				

		if (isImage) {
			int startWidth = margin+(xCord-image.getWidth(this))/2;
			int startHeight = margin+(yCord-image.getHeight(this))/2;				
			g.drawImage(image,startWidth,startHeight,this);
		} else {
			if (needMetrics) {
				g.setFont(new Font("Times",Font.PLAIN,12));
				fm = g.getFontMetrics();			
				needMetrics = false;
			}
			
			int startWidth = (int)((size().width-fm.stringWidth(text))/2);
			int startHeight = (int)((size().height+fm.getAscent()+fm.getDescent())/2); 
			
			g.setColor(new Color(0,0,0));
			g.drawString(text,startWidth,startHeight);			
		}		

		//}}
	}
	
	public boolean mouseDown(Event event, int x, int y) {

		//{{ create button down effect

			paintIndentBorder = true;
			repaint();

		//}}

		return super.mouseDown(event,x,y);
	}


	public boolean mouseUp(Event event, int x, int y) {

		// create button up effect on mouse up event

		paintExtendBorder = true;
		repaint();

		//}}

		return super.mouseUp(event,x,y);
	}

	public boolean mouseEnter(Event event, int x, int y) {

		//{{ create button up effect on mouse enter event 

			paintExtendBorder = true;
			repaint();	

		//}}

		return super.mouseEnter(event,x,y);

	}

	public boolean mouseExit(Event event, int x, int y) {

		//{{ make button flush to background panel

			paintExtendBorder = false;
			repaint();

		 //}}

		 return super.mouseExit(event,x,y);
	}	
}


	
class SunAudioParser {

	  //{{initialie local variables

	  byte optionalChars[] = null;
	  byte dataSegment[] = null;
	  int magicNumber = 0; 
	  int dataLocation = 0;
	  int dataSize = 0; 
	  int dataFormat = 0;
	  int samplingRate = 0; 
	  int channelCount = 0;
	  int byteLength = 0;
	  int startByte = 0;
	  InputStream in = null;
	  URL codebaseURL = null;
	  String filename = null;

	  //}}
	  

	public SunAudioParser(URL codebase, String string, int startTime, int duration) {		
		codebaseURL = codebase;
		filename = string;		
		extractSunHeader(codebaseURL,filename, startTime, duration);
	}


	public void extractSunHeader(URL codebaseURL, String filename, int startTime, int duration) {
	

		//{{ extract sun audio header

		URL fileURL = null;

		try {fileURL = new URL(codebaseURL,filename);}
			catch (MalformedURLException e) {};

		DataInputStream in = null;

		try {in = new DataInputStream(fileURL.openStream());}
			catch (IOException e) {};

		try {
			magicNumber = in.readInt();
			dataLocation = in.readInt();
			dataSize = in.readInt();
			dataFormat = in.readInt();
			samplingRate = in.readInt();
			channelCount = in.readInt();
			optionalChars = new byte[dataLocation-24];
			in.readFully(optionalChars,0,15);
			
			startByte = convertSecondsToBytes(startTime);
			byteLength = convertSecondsToBytes(duration);
			dataSegment = new byte[byteLength];   
			in.skipBytes(startByte);
			in.readFully(dataSegment,0,byteLength);
		} 
		catch (IOException e) {};

		//}}
	}


	public byte[] getIndexedAudio() {

		//{{ reconstruct the audio and return a byte array of the the new audio segment


		ByteArrayOutputStream bout = new ByteArrayOutputStream();
		DataOutputStream dout = new DataOutputStream(bout); 
				
		try {
			dout.writeInt(magicNumber);
			dout.writeInt(dataLocation);
			dout.writeInt(dataSize);
			dout.writeInt(dataFormat);
			dout.writeInt(samplingRate);
			dout.writeInt(channelCount);
			dout.write(optionalChars);
			dout.write(dataSegment,0,byteLength);
		} 
		catch (IOException e) {};

	   //}}	

	   return bout.toByteArray();
	}


	public int convertSecondsToBytes(int seconds) {

		//{{ converts the number of seconds into a number of bytes

		return seconds*samplingRate;

		//}}
	}

	public String getInfoString() {

		//{{ obtained an information string on a audio segment

		StringBuffer string = new StringBuffer();
		string.append("Magic Number: "+Integer.toString(magicNumber)+"\n");
		string.append("Data Start Location: "+dataLocation+"\n");
		string.append("Data Size: "+dataSize+"\n");
		string.append("DataFormat: "+dataFormat+"\n");
		string.append("Sampling rate: "+samplingRate+"\n");
		string.append("Channel Count: "+channelCount+"\n");
		string.append(new String(optionalChars,0,0,15));

		return string.toString();

		//}}
	}

}



//{{ a class for display messages during debugging

class ErrorLog extends Frame {

	TextArea textArea = null;

	public ErrorLog(String s) {
	super(s);
	setLayout(new BorderLayout());
	textArea = new TextArea(40,40);
	textArea.setText("Error Log Intialized.\n");
	add("Center",textArea);
	pack();
	show();
		
	}

	public void set(String string) {
		textArea.appendText(string+"\n");
	}

	public boolean handleEvent(Event event) {
		if (event.id == Event.WINDOW_DESTROY) {
			dispose();
		}
		return super.handleEvent(event);
	}
}


//}}



class RepInfo {

	public String fullName = null;
	public String party = null;
	public String state = null;

	public void setFullName(String string) {
		fullName = string;
	}
	public void setParty(String string) {
		party = string;
	}
	public void setState(String string) {
		state = string;
	}
}


class RepInfoHashtable extends Hashtable {

	URL p ;
	URL path = p;
	String filename = null;

	public RepInfoHashtable(URL p,String f) {
		super();
		path = p;
		filename = f;
		createHashtable();
	}

	
	public void createHashtable() {
		
		String fileContents = FileContent.toString(path,filename);

		StringTokenizer fileTokenizer = new StringTokenizer(fileContents,"\n");
	
		while (fileTokenizer.hasMoreTokens()) {

			StringTokenizer lineTokenizer = new StringTokenizer(fileTokenizer.nextToken(),":");
			
			RepInfo info = new RepInfo();
			String key = lineTokenizer.nextToken().toLowerCase();;
			info.setFullName(lineTokenizer.nextToken());
			info.setParty(lineTokenizer.nextToken());
			info.setState(lineTokenizer.nextToken());
			put(key,info);
		}
	}


	public String getFullName(String key) {

		return ((RepInfo)get(key.toLowerCase())).fullName;
	}

	public String getParty(String key) {

		return ((RepInfo)get(key.toLowerCase())).party;
	}

	public String getState(String key) {

		return ((RepInfo)get(key.toLowerCase())).state;
	}
	
}


class SegmentInfo {

	public int startTime = 0;
	public int finishTime = 0;
	public int duration = 0;

	public void setStartTime(int i) {
		startTime = i;
	}

	public void setFinishTime(int i) {
		finishTime = i;
	}

	public void setDuration(int i) {
		duration = i;
	}

}



class SegmentHashtable extends Hashtable {

	public SegmentHashtable(URL path, String filename) {
		super();
		createHashtable(path,filename);
	}


	public void createHashtable(URL path, String filename) {


		String delimiter = "segment";

		String fileContent = FileContent.toString(path,filename);

		int old_index = 0;
		int eol_index = 0;
		int new_index = 0;
		
		new_index = fileContent.indexOf(delimiter,old_index);

		while (new_index >= 0) {
		 eol_index = fileContent.indexOf("\n",new_index);
		 String segmentHeaderLine = fileContent.substring(new_index,eol_index);
		 StringTokenizer tokenizer = new StringTokenizer(segmentHeaderLine,":");
		 SegmentInfo info = new SegmentInfo();
		 String key = tokenizer.nextToken();
		 info.setStartTime(Integer.parseInt(tokenizer.nextToken()));
		 info.setFinishTime(Integer.parseInt(tokenizer.nextToken()));
		 info.setDuration(Integer.parseInt(tokenizer.nextToken()));
		 put(key,info);
		 old_index = new_index+1;
		 new_index = fileContent.indexOf(delimiter,old_index);
		}
	}

	
	public int getStartTime(String sequenceNumber) {
		String key = "segment."+sequenceNumber+".au";
		return ((SegmentInfo)get(key)).startTime;
	}

	public int getFinishTime(String sequenceNumber) {
		String key = "segment."+sequenceNumber+".au";
		return ((SegmentInfo)get(key)).finishTime;
	}

	public int getDuration(String sequenceNumber) {
		String key = "segment."+sequenceNumber+".au";
		return ((SegmentInfo)get(key)).duration;
	}	
}


class FileContent {


	public static String toString(URL path, String filename) {


		//{{ extract the contents of a file, assumes line char 13, char 10 line delimitation

        URL u= null;

	    try {
	        u= new URL(path,filename);
	    }
	        catch(MalformedURLException e) {return null;}


	    StringBuffer file_content= new StringBuffer("");

	    try {
	        InputStream in= u.openStream();
	        char c;
	        while (true) {
		    int i= in.read();
		    c= (char)i;
		    if (i==-1) break;
		    if (i==13) continue;
		    if (i==10) file_content.append("\n");
			    else file_content.append(c);
	        }
	    }
	    catch(IOException e) {return null;}

	   //}}

        return file_content.toString();
	}

}


//{{ Dialog window, requires SearchConstraint class





class XFrame extends Frame {


	private static final String EMPTY_STRING = new String("");
	private	Checkbox nameCheckbox = null, timeCheckbox = null;
	private Checkbox ignoreCaseCheckbox = null,stateCheckbox = null;
	private Label nameLabel = null, startTimeLabel = null, endTimeLabel = null;
	private Label stateLabel = null, partyLabel = null;
    private TextField nameField = null, startTimeField = null;
	private TextField endTimeField = null, stateField = null;
	private Choice partyChoice = null;
    private int currentField = 0;
    private Button doneButton = null,cancelButton = null, clearButton=null;
    private House parent = null;


	public XFrame(House applet,String s) {
		super(s);
		parent = applet;
		createGUI();
	}

	public void createGUI() {

		//{{ Setup global layout
		
		setLayout(new BorderLayout(20,0));
		setBackground(new Color(192,192,192));		
		
		//}}


		//{{ Create Center Panel

		Panel centerPanel = new Panel();
		add("Center",centerPanel);
		GridBagLayout gbl = new GridBagLayout();		
		centerPanel.setLayout(gbl);
		GridBagConstraints gbc = new GridBagConstraints();
		gbc.fill = GridBagConstraints.HORIZONTAL;
		gbc.anchor = GridBagConstraints.CENTER;
		gbc.weightx = 100;
		gbc.weighty = 100;
		
		//}}

		//{{ Construct Field and Add Field Labels
		
		nameLabel = new Label("Name: ",Label.LEFT);
		startTimeLabel = new Label("StartTime: ",Label.LEFT);
		endTimeLabel = new Label("EndTime: ",Label.LEFT);
		stateLabel = new Label("State: ",Label.LEFT);
		partyLabel = new Label("Party: ",Label.LEFT);       
		add(centerPanel,nameLabel,gbl,gbc,1,1,1,1);
		add(centerPanel,startTimeLabel,gbl,gbc,1,2,1,1);
		add(centerPanel,endTimeLabel,gbl,gbc,3,2,1,1);
		add(centerPanel,partyLabel,gbl,gbc,1,3,1,1);
		add(centerPanel,stateLabel,gbl,gbc,3,3,1,1);
		
		//}}

		//{{ Add TextFields
		
		nameField = new TextField();
		startTimeField = new TextField();
		endTimeField = new TextField();
		stateField = new TextField(2);
		partyChoice = new Choice();
		partyChoice.addItem("All Parties");
		partyChoice.addItem("Democratic");
		partyChoice.addItem("Republican");
		add(centerPanel,nameField,gbl,gbc,2,1,3,1);
		add(centerPanel,startTimeField,gbl,gbc,2,2,1,1);
		add(centerPanel,endTimeField,gbl,gbc,4,2,1,1);
		add(centerPanel,partyChoice,gbl,gbc,2,3,1,1);
		add(centerPanel,stateField,gbl,gbc,4,3,1,1);
		
		//}}
		
   		
		//{{ Create Eastern Panel
		
		Panel eastPanel = new Panel();
		add("East",eastPanel);		
		eastPanel.setLayout(gbl);
		gbc.gridwidth = GridBagConstraints.REMAINDER;
		gbc.anchor = GridBagConstraints.WEST;
		
		//}}
		
		//{{ Add Buttons
		
		doneButton = new Button("Search");
		clearButton = new Button("Clear");
		cancelButton = new Button("Cancel");
		add(eastPanel,doneButton,gbl,gbc,1,1,1,1);
		add(eastPanel,clearButton,gbl,gbc,1,3,1,1);		
		add(eastPanel,cancelButton,gbl,gbc,1,5,1,1);
		
		//}}

		//{{ Create Southern Panel
		
		Panel southPanel = new Panel();				
		add("South",southPanel);
		southPanel.setLayout(new FlowLayout(FlowLayout.CENTER));
		
		//}}
		       
		//{{ Add Checkbox Components 
		
		nameCheckbox = new Checkbox("All Speakers");
		timeCheckbox = new Checkbox("All Times");
		stateCheckbox = new Checkbox("All States");
		ignoreCaseCheckbox = new Checkbox("Ignore Case");
		ignoreCaseCheckbox.setState(true);
		southPanel.add(nameCheckbox);
		southPanel.add(timeCheckbox);
		southPanel.add(stateCheckbox);
		southPanel.add(ignoreCaseCheckbox);
		
		//}}
	
		//{{ Resize Dialog
		
		resize(400,150);       
		setResizable(false);
		
		//}}
	}
	

		private void add(Container container,Component c, GridBagLayout gbl, GridBagConstraints gbc, int x, int y, int w, int h) {

       //{{ bind gridBagConstraints arguments and add the compoenent to the layout manager

	   gbc.gridx = x;
	   gbc.gridy = y;
	   gbc.gridwidth = w;
	   gbc.gridheight = h;
	   gbl.setConstraints(c,gbc);
	   container.add(c);

	   //}}
	}


	public void advanceFocus() {	
		// future implementation for advancing the component focus
	}


    public boolean keyDown(Event event, int key) {

		// keyEvents need restructuring

        if ((char)key == '\t') {
            advanceFocus();		
		} else if ((char)key == 10) {
			if (event.target instanceof TextField) {
				advanceFocus(); 
			} else if (event.target == doneButton) {
				processDoneButton();	
			} else if (event.target == cancelButton) {
				dispose();
			}
		}
        return super.keyDown(event,key);

		//}}
    }


    public boolean handleEvent(Event event) {

		//{{ destruct dialog window

        if (event.id == Event.WINDOW_DESTROY) {
            dispose();
            return true;
        }
        //}}

		return super.handleEvent(event);		
     }

    public boolean action(Event event,Object obj) {

		//{{ handle button and checkbox events

        if (event.target instanceof Button) {
			if (event.target == doneButton) {
            processDoneButton();
			} else if (event.target == clearButton) {
				processClearButton();
			} else if (event.target == cancelButton) {
			dispose();
			}			
		} else if (event.target instanceof Checkbox) {
			if (event.target == nameCheckbox) {
				processNameCheckbox();					
			} else if (event.target == timeCheckbox) {
				processTimeCheckbox();
			} else if (event.target == stateCheckbox) {
				processStateCheckbox();
			}
		}

		//}}

        return super.action(event,obj);
    }


	public void processNameCheckbox() {

		//{{ sychronize nameField information with name checkbox

		if (nameCheckbox.getState()) {
			nameField.setText("All Speakers");
		} else {
			nameField.setText("");
		}

		//}}
	}


	public void processTimeCheckbox() {

		//{{ synchronize timeField information with time checkbox

		if (timeCheckbox.getState()) {
			startTimeField.setText("0:00");
			endTimeField.setText("24:00");
		} else {
			startTimeField.setText("");
			endTimeField.setText("");
		}

		//}}
	}

	public void processStateCheckbox() {

		//{{ synchronize stateField information with state checkbox

		if (stateCheckbox.getState()) {
			stateField.setText("All States");
		} else stateField.setText("");

		//}}
	}

    public void processDoneButton() {                      

		//{{ package constraints into an instance of SearchConstraints

		SearchConstraints constraints = new SearchConstraints();
		if (!(nameCheckbox.getState())) 
			constraints.addNameConstraint(nameField.getText());
		if (!(timeCheckbox.getState())) {
			constraints.addTimeConstraint(startTimeField.getText());
			constraints.addTimeConstraint(endTimeField.getText());
		} 
		if (!(stateCheckbox.getState())) {
			constraints.addStateConstraint(stateField.getText());
		}
		if (!(partyChoice.getSelectedItem().equals("All Parties"))) 
			constraints.addPartyConstraint(partyChoice.getSelectedItem());
		if (ignoreCaseCheckbox.getState()) {
			constraints.ignoreCase = true;
		} else constraints.ignoreCase = false;
		
		//}}

		//{{ deconstruct window and pass constraints to the parent applet
		
		dispose();
        parent.processResult(this,constraints);

		//}}
    }
      	

	public void processClearButton() {

		//{{ clear textFields and reset checkboxes

		nameField.setText("");
		startTimeField.setText("");
		endTimeField.setText("");
		stateField.setText("");
		nameCheckbox.setState(false);
		timeCheckbox.setState(false);
		ignoreCaseCheckbox.setState(true);

		//}}
	}



}

