/*
* @(#)BujuClientJava   1.4 04/01/05 BBeans.biz
*
* Copyright (c) 2005 BBeans.biz All Rights Reserved.
*
* BBeans.biz MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE
* SUITABILITY OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING
* BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. BBeans.biz
* SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT
* OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
*
* THIS SOFTWARE IS NOT DESIGNED OR INTENDED FOR USE OR RESALE AS ON-LINE
* CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING FAIL-SAFE
* PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES, AIRCRAFT
* NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT LIFE
* SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
* SOFTWARE COULD LEAD DIRECTLY TO DEATH, PERSONAL INJURY, OR SEVERE
* PHYSICAL OR ENVIRONMENTAL DAMAGE ("HIGH RISK ACTIVITIES"). BBeans.biz
* SPECIFICALLY DISCLAIMS ANY EXPRESS OR IMPLIED WARRANTY
* OF FITNESS FOR HIGH RISK ACTIVITIES.
*
*/


import java.io.*;
import java.net.*;
import javax.net.*;
import javax.net.ssl.*;

/**
* Interactive test client for the Buju server.
* The Buju server should already be running.
*/
public class BujuClientJava
{
  private static final    int               USERF   = 1;
  private static final    int               PASSWDF = 2;
  private static final    int               HOSTF   = 3;
  private static final    int               PORTF   = 4;
  private static final    int[]             ORDER   = {USERF, PASSWDF, HOSTF, PORTF};
  private                 Socket            socket;
  private                 DataInputStream   in;
  private                 DataOutputStream  out;
  
  /**
  * Create a Buju server test client.
  * The Buju server should already be running.
  * @param host The host for the Buju server.
  * @param port The Buju server port.
  * @param noSSL If true, do not use SSL.
  * @throws UnknownHostException If Buju server host can't be found.
  * @throws IOException If socket IO streams have a problem.
  */
  public BujuClientJava(String host, int port, boolean noSSL)
  throws  UnknownHostException,
          IOException
  {
    super();
    SocketFactory sf = null;
    if (noSSL)
    {
      sf = SocketFactory.getDefault();
    }
    else
    {
      sf = SSLSocketFactory.getDefault();
    }
    connect(host, port, sf);
  }
  
  //Create a socket using the given socket factory.
  private void connect(String host, int port, SocketFactory sf)
  throws  UnknownHostException,
          IOException
  {
    socket = sf.createSocket(host, port);
    socket.setSoTimeout(30000);
    socket.setTcpNoDelay(true);
    in = new DataInputStream(socket.getInputStream());
    out = new DataOutputStream(socket.getOutputStream());    
  }
  
  /**
  * Login to the Buju server.
  * @param user The user ID.
  * @param passwd The password.
  * @return The result of login.
  * @throws IOException If socket IO streams have a problem.
  * @throws Exception If the login failed.
  */
  public String login(String user, String passwd)
  throws  IOException,
          Exception
  {
    try
    {
      return eval("user=" + user + " passwd=" + passwd);
    }
    catch (Exception exc)
    {
      socket.close();
      socket = null;
      throw exc;
    }
  }
  
  /**
  * Evaluate the Java source line.
  * @param source The Java source line.
  * @return The evaluation results.
  * @throws IOException If socket IO streams have a problem.
  * @throws Exception If the evaluation failed.
  */
  public String eval(String source)
  throws  IOException,
          Exception
  {
    String result = "";
    byte[] b = source.getBytes();
    out.writeInt(b.length);
    out.flush();
    out.write(b);
    out.flush();
    int len = in.readInt();
    b = new byte[Math.abs(len)];
    int read = in.read(b);
    if (read != b.length)
    {
      throw new IOException("Unexpected read exception.");
    }
    
    result = new String(b);
    
    if (len < 0)
    {
      throw new Exception(result);
    }
    return result;
  }
  
  /**
  * Close any unclosed socket.
  * Called on finalize().
  * @throws IOException If the socket can't be closed.
  */
  public void close()
  throws  IOException
  {
    if (socket != null)
    {
      socket.close();
      socket = null;
    }
    
  }
  
  /**
  * Closes any unclosed socket by calling close().
  * Overrides and finally calls Object.finalize().
  */
  protected void finalize()
  throws  Throwable
  {
    try
    {
      close();
    }
    catch (Exception exc)
    {
      exc.printStackTrace();
    }
    
    super.finalize();
  }
  
  //Get input from system in.
  private static String getInput(String prompt, BufferedReader reader)
  throws IOException
  {
    System.out.print(prompt);
    return reader.readLine().trim();
  }
  
  /**
  * Character console interface for interactive test.
  * Usage: BujuClientJava
  */
  public static void main(String[] args)
  {
    String user = "";
    String passwd = "";
    String host = "localhost";
    int port = 2183;
    boolean noSSL = false;
    
    System.out.println("Buju Interactive Test\n");
        
    int field = 0;
    String inp;
    try
    {
      BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
      
      while (field >= 0 && field < ORDER.length)
      {
        switch (ORDER[field])
        {
          case USERF:
          {
            inp = getInput("UserID: ", reader);
            if (inp.equals(""))
            {
              field--;
              break;
            }
            user = inp;
            field++;
            break;
          }
          case PASSWDF:
          {
            inp = getInput("Password: ", reader);
            if (inp.equals(""))
            {
              field--;
              break;
            }
            passwd = inp;
            field++;
            break;
          }
          case HOSTF:
          {
            inp = getInput("Host: ", reader);
            if (inp.equals(""))
            {
              field--;
              break;
            }
            host = inp;
            field++;
            break;
          }
          case PORTF:
          {
            inp = getInput("Port: ", reader);
            if (inp.equals(""))
            {
              field--;
              break;
            }
            try
            {
              port = Integer.parseInt(inp);
            }
            catch (Exception exc)
            {
              break;
            }
            field++;
            break;
          }
          default:
          {
            field = -1;
            break;
          }
        }
      }
      
      if (field == -1 || user.equals("") || passwd.equals(""))
      {
        return;
      }
      BujuClientJava test = new BujuClientJava(host, port, noSSL);
      test.login(user, passwd);
      System.out.println("Test client ready.");
      System.out.println("At prompt (>) enter Java code to evaluate.");
      System.out.println("Enter 'quit' to exit.");
      
      boolean running = true;
      
      while (running)
      {
        System.out.print(">");
        String source = reader.readLine();
        source = source.trim();
        if (source.equals("quit"))
        {
          try
          {
            System.out.println(test.eval(".close()"));
          }
          catch (Exception exc)
          {
            exc.printStackTrace();
          }
          running = false;
          continue;
        }
        try
        {
          System.out.println(test.eval(source));
        }
        catch (Exception exc)
        {
          System.err.println(exc.getMessage());
        }
        
      }
      
    }
    catch (Exception exc)
    {
      exc.printStackTrace();
    }
    System.out.println("Done.");
  }
  
}

