import java.io.*;
import java.net.*;

/****************************************************************************
* This class forms a connection to JESS through a socket.  JESS runs a script
* that contains the logic for the expert system side of things.  This class 
* provides the user interface for the total application, and some of the 
* algorithmic logic.
*
* @author Michael Wales
* @version Prototype Version 0.0
****************************************************************************/
public class JessInterface
{
   /** This is the connection between the Java application and JESS */
   private Socket JessConnection = null;
   
   /** This is the object that is used to read a line from the standard in */
   private BufferedReader StandardIn = null;
   
   /** The output stream for the socket connection to JESS */
   private PrintWriter JessOutput = null;
   
   /** Hardware Costs database for JESS to interface with */
   private HardwareCosts HWData = null;
   
   /*************************************************************************
   * Constructor.  It sets up the standard input reading object.  Also sets
   * up the hardware cost database service.
   *************************************************************************/
   JessInterface()
   {
      // Initialize the standard input reader
      try
      {
         StandardIn = new BufferedReader( new InputStreamReader( System.in ) );
      }
      catch(Exception E)
      {
         System.out.println("ERROR:  Couldn't open the standard input");
      }
      
      // Initialize the hardware cost database
      HWData = new HardwareCosts();
   }
   
   /*************************************************************************
   * This method accepts the socket connection from JESS.  After the 
   * connection is established, the listenToJessConnection() is called
   * to start the read/write cycle for JESS.
   *************************************************************************/
   public void acceptConnection()
   {
      System.out.print("Waiting for JESS connection...");
      
      // Open server socket connection for Jess to connect to
      ServerSocket JessServerSocket = null;
      try
      {
         // Accept connection from JESS
         JessServerSocket = new ServerSocket(8034);
         JessConnection = JessServerSocket.accept();
         
         // Initialize the output writer to JESS
         JessOutput = new PrintWriter( JessConnection.getOutputStream() );
      }
      catch (Exception E)
      {
         System.out.println("ERROR:  Failed to accept connection");
      }
      
      System.out.println("CONNECTED");
      
      listenToJessConnection();
      
      // Start up the reading thread
      // Thread ReadingThread = null;
      // ReadingThread = new Thread(new JESSReader(JessConnection));
      // ReadingThread.start();
   }
   
   /*************************************************************************
   * This method is passed a string outputted by JESS.  JESS can either send
   * normal text over for printing, or it can send special commands to get
   * the Java application to help it do something.<BR><BR>
   *
   * JESS-PROMPT_USER = Asks the user a question and then sends the user's 
   *   input back to JESS.<BR>
   * JESS-FINISHED = JESS tells the Java application that it is thru, and 
   *   will now close.<BR>
   * JESS-PRINT_SYSTEM_INFO = Tells the Java application to get the hardware
   *   database to print out the system that was purchased.<BR>
   * JESS-HWCDB = Tells the Java application to pass the string to the 
   *   hardware database cause JESS want's to purchase some hardware.
   *
   * @param Output The string that JESS sent over the socket to the Java 
   *   application
   * @return Returns true if command was succesful, and false if the 
   *   connection has gone bad and the application should shut down.
   *************************************************************************/
   private boolean analyzeJessOutput(String Output)
   {
      if (Output == null)
      {
         // Null string means bad connection
         return false;
      }
      
      if (Output.equals("JESS-PROMPT_USER"))
      {
         // JESS requests input
         promptUser();
         return true;
      }
      
      if (Output.equals("JESS-FINISHED"))
      {
         // JESS is now closing
         return false;
      }
      
      if (Output.equals("JESS-PRINT_SYSTEM_INFO"))
      {
         // Print out all the hardware purchased
         HWData.displayFullSystem();
         return true;
      }
      
      if (Output.startsWith("JESS-HWCDB"))
      {
         // Send the JESS query onto the hardware database
         String Data = HWData.upgradeHardware(Output);
         
         // Send the hardware data back to JESS
         try
         {
            JessOutput.println(Data);
            JessOutput.flush();
         }
         catch(Exception E)
         {
            System.out.println("ERROR:  Couldn't send data to JESS");
         }
         
         return true;
      }
      
      // Default:  Just print out what JESS sent over
      System.out.println(Output);
            
      // Return true if the Java application can read the socket again safely
      return true;
   }
   
   /*************************************************************************
   * This is a method used to have the Java application itself emulate JESS.
   * This is useful during debugging, but won't have any real application
   * once the application is complete.
   *
   * This method simply connects to another Java application running normally,
   * and allows the user to send info to the other computer by just typing
   * stuff in, and the other computer will send responses that are displayed
   * on the screen.
   *************************************************************************/
   public void emulateJess()
   {
      System.out.print("Connecting to ComputerAdvice Java application...");
      
      // Open server socket connection for Jess to connect to
      try
      {
         // Accept connection from JESS
         JessConnection = new Socket("127.0.0.1", 8034);
         
         // Initialize the output writer to JESS
         JessOutput = new PrintWriter( JessConnection.getOutputStream() );
      }
      catch (Exception E)
      {
         System.out.println("ERROR:  Failed to accept connection");
      }
      
      System.out.println("CONNECTED");
      
      // Start up the reading thread
      Thread ReadingThread = null;
      ReadingThread = new Thread(new JESSReader(JessConnection));
      ReadingThread.start();    
      
      while(true)
         promptUser();
   }
   
   /*************************************************************************
   * This method opens up an input stream to JESS to read and then processes
   * any incoming info.  All incoming data is passed over to the 
   * analyzeJessOutput() method for decoding.  
   *************************************************************************/
   private void listenToJessConnection()
   {
      String DataFromJess = null;
      
      InputStream JessIS = null;
      BufferedReader JessBR = null;
      
      try
      {
         JessIS = JessConnection.getInputStream();
         JessBR = new BufferedReader( new InputStreamReader( JessIS ) );
      }
      catch(Exception E)
      {
         System.out.println("ERROR:  Failed to initialize the Jess socket reader");
         return;
      }
      
      // Tell JESS we are ready to listen
      System.out.println("Sending JESS the ready signal");
      try
      {
         JessOutput.println("Ready");
         JessOutput.flush();
      }
      catch(Exception E)
      {
         System.out.println("ERROR:  Couldn't tell JESS ComputerAdvice was ready");
      }
      
      boolean KeepLooping = true;
      while (KeepLooping == true)
      {
         try
         {
            DataFromJess = JessBR.readLine();
         }
         catch(Exception E)
         {
            // If the socket gets a read error, the other connection probably broke
            System.out.println("ERROR:  Failed to read incoming data from the socket");
            KeepLooping = false;
         }
         
         if (KeepLooping == true)
            KeepLooping = analyzeJessOutput(DataFromJess);
      }      
   }/*************************************************************************
   * This method lets the user input a string and returns it from the
   * method.
   *
   * @returns A String inputed by the user
   *************************************************************************/
   public String readString()
   {
      try
      {
         return StandardIn.readLine();
      }
      catch(Exception E)
      {
         System.out.println("ERROR:  Couldn't read the standard input");
         return null;
      }
   }
   
   /*************************************************************************
   * This method reads a string from the user, and then sends it to JESS
   * for processing.
   *************************************************************************/
   public void promptUser()
   {
      String UserInput;
      
      // Get the input from the user
      UserInput = readString();
      
      // Send the user input to JESS
      try
      {
         JessOutput.println(UserInput);
         JessOutput.flush();
      }
      catch(Exception E)
      {
         System.out.println("ERROR:  Couldn't send data to JESS");
      }
   }

}
