/*
 *  Team 1
 *  CS575: Software Design
 *  Project Phase II
 *  Implicit Invocation style
 *  IIInput.java
 */

import java.io.*;
import java.util.Vector;

/**
 *  <p>Input class for the Implicit Invocation KWIC system.</p>
 *
 *  <p>Listens for events that enable it to input data from a file.
 *  Once that event is received, it will input all the data in the given file(s).
 *  It will broadcast messages to inform others of the progress it is making.</p>
 *
 *  <p>This class implements the KWICListener interface which enables it to 
 *  receive message sent by a KWICBroadcaster it is registered with. The class must 
 *  registered by a third party class; it cannot register on its own.
 *  </p>
 *
 *  <p>First, the constructor of the class must be called.  After that, the class will 
 *  simply wait for message to come in to its eventArrived() method.  These events
 *  will indicate what the class should do.  Those events that are not of importance to 
 *  this class will simply be ignored by the event handling processing of this class.
 *  </p>
 *
 *  <p>This class is capable of performing the following two functions.  The first thing 
 *  it can do is read in a noise file.  The format of the file is assumed to be of the following 
 *  nature: single word per line.  This functionality will be invoked when a message is
 *  received with a list of filenames of files to be parsed.  The second functionality of 
 *  this class is reading an input file with a sentence per line and parsing it.  This will 
 *  also be invoked by a message with filename of files to be parsed.
 *  </p>
 *  @author Diana Tetelman
 */

public class IIInput implements KWICListener 
{
    /**
     * keeps track of the specified token termination character
     */
    private char tokenTermChar;
    
    /**
     * keeps track of the specified line termination character
     */
    private char lineTermChar;
    
    /**
     * specifies whether or not verbose output is to be generated
     */
    private boolean verboseFlag;
    
    /**
     * KWICBroadcaster object which allows this module to interact with the 
     * rest of the system
     */
    private KWICBroadcaster broadcaster;
    
    /**
     * list of names of the files specified for input
     */
    private Vector files;
   
    /**
     * used internally to parse the files
     */
    private StreamTokenizer in;

    /**
     * <p><b>Constructor</b></p>
     * <p> initializes member variables </p>
     * @param  broadcaster     KWICBroadcaster object that this class is to communicate through
     * @param  filenames       Vector of Strings which contain the names of the files to be parsed
     * @param  tokenTerm       token termination character (' ')
     * @param  lineTerm        line termination character ('\n')
     * @param  verbose         boolean to indicate whether or not verbose output should be generated
     */
    public IIInput(KWICBroadcaster _broadcaster,  
                   char tokenTerm, char lineTerm, 
                   boolean verbose)
    {
        broadcaster = _broadcaster;
        
        files = new Vector();
        tokenTermChar = tokenTerm;
        lineTermChar = lineTerm;        
        
        verboseFlag = verbose;		
    }
    
    /**
     * Implements the KWICListener interface method.  This method will receive events and 
     * process them accordingly base on their content.
     * @param   event       KWICEvent which has been broadcasted
     */
    public void eventArrived(KWICEvent event) 
    {
        if( event.getEventType() == KWICEvent.INPUT_DATA )
		{
			files = (Vector)(event.getPayLoad());
            parseDataFile();
		}
		else if( event.getEventType() == KWICEvent.INPUT_NOISE )
		{
			files = (Vector)(event.getPayLoad());
			parseNoiseFile();
		}
    }
    
    /**
     * This method will read in all the contents of the specified files and parse them.
     * It will broadcast a SENTENCE_INPUTED message which will contain the actual 
     * new sentence every time a new sentence is retrieved.
     */
    private void parseDataFile()
    {
        for( int fIndex = 0; fIndex < files.size(); fIndex++ )
        {
            try{
                setupTokenizer((String)(files.elementAt(fIndex)));
                
                int tokenType = in.nextToken();
                while( tokenType != in.TT_EOF )
                {
                    Vector words = new Vector();                   
                    while( tokenType != in.TT_EOF && tokenType != in.TT_EOL && 
                           ! in.sval.toCharArray().equals(new Character(lineTermChar)) )
                    {
                        if( verboseFlag )
                            System.out.println("Adding the word \"" + in.sval + "\"");
                        
                        words.add(in.sval);                     
                        tokenType = in.nextToken();
                    }
                    
                    // post message to the shifter with the index of the sentence
                    if( verboseFlag )
                        System.out.println("Sent message with " + words.toString());
                    
					broadcaster.eventBroadcast(new KWICEvent( 
                                                    "Input",
                                                    KWICEvent.SENTENCE_INPUTED,
                                                    (Object)(words)));
                                    
                    tokenType = in.nextToken();
                }   
            }
            catch(KWICException e)
            {
                if( verboseFlag )
                    e.printStackTrace();
                
                // !!! pass message to main with 
                // SendMessage(e);
            }            
            catch( FileNotFoundException e )
            {
                if( verboseFlag )
                    e.printStackTrace();
            }
            catch( IOException e )
            {
                if( verboseFlag )
                    e.printStackTrace();
             }
        }
        // post message to shifter that no more input coming in
        if( verboseFlag )
            System.out.println("INPUT_DONE Message sent");
        broadcaster.eventBroadcast(new KWICEvent(
                                    "Input",
                                    KWICEvent.INPUT_DONE,
                                    new Object()));
    }    
	
    /**
     * This method will read in all the contents of the specified files and parse them.
     * It will broadcast a INPUT_NOISE_DONE message which will contain the vector of
     * the strings inputed from the file.
     */
    private void parseNoiseFile()
    {
        for( int fIndex = 0; fIndex < files.size(); fIndex++ )
        {
            try{
                setupTokenizer((String)(files.elementAt(fIndex)));
				
                Vector allLines = new Vector();
                int tokenType = in.nextToken();
                while( tokenType != in.TT_EOF )
                {
                    while( tokenType != in.TT_EOF && tokenType != in.TT_EOL && 
                           ! in.sval.toCharArray().equals(new Character(lineTermChar)) )
                    {
                        if( verboseFlag )
                            System.out.println("Adding the word \"" + in.sval + "\"");
                        
                        allLines.add(in.sval);                     
                        tokenType = in.nextToken();
                    }
                        
                    tokenType = in.nextToken();
                }   
				// post message to shifter that no more input coming in
				if( verboseFlag )
				    System.out.println("INPUT_NOISE_DONE Message sent");
				broadcaster.eventBroadcast(new KWICEvent(
				                            "Input",
				                            KWICEvent.INPUT_NOISE_DONE,
				                            allLines));

            }
            catch(KWICException e)
            {
                if( verboseFlag )
                    e.printStackTrace();                
            }            
            catch( FileNotFoundException e )
            {
                if( verboseFlag )
                    e.printStackTrace();
            }
            catch( IOException e )
            {
                if( verboseFlag )
                    e.printStackTrace();
             }
        }
    }
    

    /**
     * Sets up the file reader along with the tokenizer for the specified file
     *
     * @param   filename    String that contains the name of the file to be opened
     */
    private void setupTokenizer(String filename) throws KWICException
    {
        try{
            in = new StreamTokenizer(
                 new BufferedReader(
                 new FileReader(filename)));

            // if the newline is not the sentence terminator, treat it as whitespace
            if( lineTermChar == '\n' )
                in.eolIsSignificant(true);
            else
                in.eolIsSignificant(false);

            // set up the termination character if it is out of the current range.
            if( ! (Character.getNumericValue(tokenTermChar) >= '\u0000' && 
                   Character.getNumericValue(tokenTermChar) <= '\u0020') )
            {
                if( verboseFlag )
                    System.out.println("Setting the token termination char to \"" + 
                                    tokenTermChar + "\"");
                in.whitespaceChars(Character.getNumericValue(tokenTermChar),
                                   Character.getNumericValue(tokenTermChar));
            }
        }
        catch( FileNotFoundException e )
        {
            if( verboseFlag )
                e.printStackTrace();
            
            throw new KWICException(filename + " not found!");                
        }
    }     
    
    /** 
     * Returns the name of the class.  Used for broadcasting purposes.
     * @return  "Input"
     */
    public String getName() { return "Input"; }
}