/*
 * The Apache Software License, Version 1.1
 *
 *
 * Copyright (c) 2000 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "SOAP" and "Apache Software Foundation" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache",
 *    nor may "Apache" appear in their name, without prior written
 *    permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation and was
 * originally based on software copyright (c) 2000, International
 * Business Machines, Inc., http://www.apache.org.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */

package samples.calculator;

import java.io.*;
import java.net.*;
import java.util.*;
import org.apache.soap.util.xml.*;
import org.apache.soap.*;
import org.apache.soap.rpc.*;

/**
 * A reverse polish notation calc that uses BSF on the server.
 * (some code stolen from the 'gui' calculator example)
 */
public class Calc {
  static int stackSize = 100 ;
  double[]   stack = new double[stackSize];
  String     encodingStyleURI;
  URL        url;

  private void doOp (String op) {
    try {
      // Build the call.
      Call call = new Call ();
      call.setTargetObjectURI ("urn:xml-soap-demo-calculator");
      call.setMethodName (op);
      call.setEncodingStyleURI(encodingStyleURI);
      Vector params = new Vector ();
      double arg2 = pull();
      double arg1 = pull();
  
      params.addElement (new Parameter("arg1", double.class,
                                       new Double (arg1), null));
      params.addElement (new Parameter("arg2", double.class,
                                       new Double (arg2), null));
      call.setParams (params);
  
      // make the call: note that the action URI is empty because the
      // XML-SOAP rpc router does not need this. This may change in the
      // future.
      Response resp = call.invoke (/* router URL */ url, /* actionURI */ "" );
  
      // Check the response.
      if (resp.generatedFault ()) {
        Fault fault = resp.getFault ();
        System.out.println ("Ouch, the call failed: ");
        System.out.println ("  Fault Code   = " + fault.getFaultCode ());
        System.out.println ("  Fault String = " + fault.getFaultString ());
        push( 0 );
      } else {
        Parameter result = resp.getReturnValue ();
        push( ((Double)result.getValue()).doubleValue()) ;
      }
    }
    catch( Exception e ) {
      e.printStackTrace();
      push( 0 );
    }
  }

  private double stringToDouble (String s) {
    // try as a double, float or by appending a ".0" to it
    try {
      return Double.valueOf (s).doubleValue ();
    } catch (NumberFormatException e1) {
      try {
        return Float.valueOf (s).floatValue () * 1.0;
      } catch (NumberFormatException e2) {
        if (s.indexOf (".") == -1) {
          return stringToDouble (s + '.' + '0');
        } else {
          return Double.NaN;
        }
      }
    }
  }

  public void push(double val) {
    while ( Double.isNaN(stack[0]) ) pull();
    for ( int i = 99 ; i > 0 ; i-- )
      stack[i] = stack[i-1] ;
   stack[0] = val ;
  }

  public double pull() {
    double result = stack[0] ;
    for ( int i = 0 ; i < 99 ; i++ )
      stack[i] = stack[i+1];
    stack[99] = 0 ; 
    return( result );
  }

  public void printStack() {
    int i ;
    for ( i = 99 ; i > 0 ; i-- )
      if ( stack[i] != 0 ) break ;
    System.out.println( "    Stack:" );
    for(  ; i >= 0 ; i-- )
      System.out.println( "    " + stack[i] );
    System.out.println( "" );
  }

  static String line = null ;
  static int    pos  = 0 ;

  public void setLine(String str) {
    line = str ; 
    pos  = 0 ;
  }

  public String nextToken() {
    // StringTokenizer doesn't handle "2*" nicely, it wants "2 *"
    String  res = null ;
    char    ch  ;

    if ( line == null || line.length() == 0 ) return( null );
    for( ; pos < line.length() ; pos++ ) {
      ch = line.charAt( pos );
      if ( Character.isWhitespace(ch) && res == null ) continue ;
      if ( Character.isWhitespace(ch) && res != null ) break ;
      if ( res == null ) res = "" + ch ;
      else {
        if ( Character.isDigit(ch) || ch == '.' ) {
          if ( Character.isDigit(res.charAt(0)) || 
               res.charAt(0) == '-' ||
               res.charAt(0) == '.' ) 
            res += ch ;
          else break ;
        }
        else 
          break ;
      }
    }
    return( res );
  }

  public void help() {
    System.out.println( "" );
    System.out.println( "A very simple reverse polish notation calculator" );
    System.out.println( "Operations allowed:" );
    System.out.println( "  +-/*" );
    System.out.println( "Other commands:" );
    System.out.println( "  p (print stack)" );
    System.out.println( "  q (quit)" );
    System.out.println( "  ? (print this help message)" );
    System.out.println( "" );
  }

  public void go() {
    InputStreamReader reader   = new InputStreamReader( System.in );
    BufferedReader    bReader  = new BufferedReader( reader );
    String            nextWord ;

    for ( int i = 0 ;i < stackSize ; i++ )
      stack[i] = 0 ;

    for ( ;; ) {
      while ( (nextWord = nextToken()) == null ) {
        System.out.println( ">> " + stack[0] );
        try {
          setLine( bReader.readLine() );
        }
        catch( Exception e ) {}
      }
      if ( nextWord.equals("q") ) break ;
      else if ( nextWord.equals("p") ) printStack();
      else if ( nextWord.equals("h") ) help();
      else if ( nextWord.equals("?") ) help();
      else if ( nextWord.equals("+") ) doOp( "plus" );
      else if ( nextWord.equals("-") ) doOp( "minus" );
      else if ( nextWord.equals("*") ) doOp( "times" );
      else if ( nextWord.equals("/") ) doOp( "divide" );
      else
        push( stringToDouble(nextWord) );
    }
  }

  public static void main (String[] args) throws Exception {
    int maxargs = 2;
    if (args.length != (maxargs-1)
        && (args.length != maxargs || !args[0].startsWith ("-"))) {
      System.err.println ("Usage: java " + Calc.class.getName () +
                          " [-encodingStyleURI] SOAP-router-URL");
      System.exit (1);
    }

    Calc c = new Calc();

    int offset = maxargs - args.length;
    c.encodingStyleURI = (args.length == maxargs)
                         ? args[0].substring(1)
                         : Constants.NS_URI_SOAP_ENC;
    c.url = new URL (args[1 - offset]);
    c.go();
  }
}
