import java.io.*;
import java.net.*;

/****************************************************************************
* 
*
* @author Michael Wales
* @version Prototype Version 0.0
****************************************************************************/
public class JessInterfaceToWeb
{
   /** This is the connection between the Java application and JESS */
   private Socket JessConnection = null;
   
   /** This is the connection between ComputerAdvice and the remote client applet */
   private Socket ClientConnection = null;
   
   /** This is the object that is used to read a line from the client applet */
   private BufferedReader StandardIn = null;
   
   /** This is the object that is used to write a line to the client applet */
   private PrintWriter StandardOut = 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;
   
   /** Flag for a dead client connection */
   public boolean RestartEverything;
   
   /*************************************************************************
   * Constructor.  It sets up the standard input reading object.  Also sets
   * up the hardware cost database service.
   *************************************************************************/
   JessInterfaceToWeb()
   {
      // Initialize the hardware cost database
      HWData = new HardwareCosts();
      
      
   }
   
   /*************************************************************************
   * This method accepts the socket connection from JESS.  
   *************************************************************************/
   public void acceptJESSConnection()
   {
      
      // Start JESS
      Runtime InterfaceProcess;
      InterfaceProcess = Runtime.getRuntime();
      
       try
      {
         InterfaceProcess.exec("java -classpath jess.jar jess.Main computer.clp");
      }
      catch (Exception E)
      {
         System.out.println("Error:  Couldn't start JESS");
      } 
      
      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() );
         
         // Close the server socket
         JessServerSocket.close();
      }
      catch (Exception E)
      {
         System.out.println("ERROR - Failed to Connect!");
         return;
      }
      
      System.out.println("CONNECTED");
      
      // Can't start listening to JESS immediantly, must accept web client connection
      // listenToJessConnection();
      acceptWebConnection();
   }
   
   /*************************************************************************
   * This method accepts the socket connection from JESS.  
   *************************************************************************/
   public void acceptWebConnection()
   {
      InputStream WebClientIS = null;
      
      System.out.print("Waiting for web client connection...");
      
      // Open server socket connection for Jess to connect to
      ServerSocket WebClientServerSocket = null;
      try
      {
         // Accept connection from JESS
         WebClientServerSocket = new ServerSocket(6789);
         ClientConnection = WebClientServerSocket.accept();
         WebClientServerSocket.close();
         
         // Initialize the output writer to JESS
         StandardOut = new PrintWriter( ClientConnection.getOutputStream() );
         
         // Initialize the input stream reader to web client
         WebClientIS = ClientConnection.getInputStream();
         StandardIn =new BufferedReader( new InputStreamReader(WebClientIS) );
         
         // Inform the hardware cost database of the output object
         HWData.setStandardOut(StandardOut);
      }
      catch (Exception E)
      {
         System.out.println("ERROR:  Failed to accept connection");
      }
      
      System.out.println("CONNECTED");
      RestartEverything = false;
      
      // Both sides connected, can now start paying attention to JESS
      listenToJessConnection();
   }
   
   /*************************************************************************
   * 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)
   {
      // Debugging output stuff
      System.out.println("DebugJESS:  " + 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
      try
      {
         StandardOut.println(Output);
         StandardOut.flush();
      }
      catch (Exception E)
      {
         System.out.println("Error sending data to the web client applet");
      }
            
      // Return true if the Java application can read the socket again safely
      return true;
   }
   
   /*************************************************************************
   * 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 webserver was ready");
      }
      
      boolean KeepLooping = true;
      while ( (KeepLooping == true) && (RestartEverything == false) )
      {
         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 JESS");
            KeepLooping = false;
         }
         
         if (KeepLooping == true)
            KeepLooping = analyzeJessOutput(DataFromJess);
      }

      // Program must be finished, start over
      HWData.resetPurchases();
      
      // Close client web connections
      try
      {
         StandardIn.close();
         StandardOut.close();
         ClientConnection.close();
         
         JessOutput.close();
         JessBR.close();
         JessConnection.close();
      }
      catch (Exception E)
      {
         System.out.println("Error closing the client web connection");
      }
      
      acceptJESSConnection();      
   }
   
   /*************************************************************************
   * 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 from the client applet");
         RestartEverything = true;
         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();
      
      // Do not send the null string to JESS
      if (UserInput == null)
         return;
      
      // 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");
         RestartEverything = true;
      }
   }

}
