/*
 *        JacORB - a free Java ORB
 *
 *   Copyright (C) 1997-98  Gerald Brose.
 *
 *   This library is free software; you can redistribute it and/or
 *   modify it under the terms of the GNU Library General Public
 *   License as published by the Free Software Foundation; either
 *   version 2 of the License, or (at your option) any later version.
 *
 *   This library is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *   Library General Public License for more details.
 *
 *   You should have received a copy of the GNU Library General Public
 *   License along with this library; if not, write to the Free
 *   Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

package jacorb.orb.dii;

import org.omg.CORBA.Any;
import org.omg.CORBA.TypeCode;
import org.omg.CORBA.NVList;
import org.omg.CORBA.NamedValue;
import jacorb.orb.ParsedIOR;
import jacorb.orb.giop.*;
import jacorb.orb.*;
import java.util.Enumeration;

/**
 * DII requests
 * 
 * @author Gerald Brose, FU Berlin
 * @version $Id: Request.java,v 1.8 2000/01/28 09:52:41 brose Exp $
 */

public class Request 
    extends org.omg.CORBA.Request
{
    private jacorb.orb.NamedValue result_value;
    private String op_signature;
    private org.omg.CORBA.ExceptionList exceptions;
    private org.omg.CORBA.ContextList contexts;
    private org.omg.CORBA.Context ctx;
    private Thread deferred_caller;
    private jacorb.orb.ORB orb;
    private org.omg.CORBA.portable.InputStream reply;

    public org.omg.CORBA.Object target;
    public jacorb.orb.Connection connection;
    public byte[] object_key;
    public NVList arguments;
    public String operation;
    public org.omg.CORBA.Environment env = new Environment();

    public Request( org.omg.CORBA.Object t, 
		    org.omg.CORBA.ORB _orb, 
		    jacorb.orb.Connection e, 
		    byte [] obj_key, 
		    String op )
    {
	target = t;
	orb = (jacorb.orb.ORB)_orb;
	connection = e;
	object_key = obj_key;
	operation = op;
	arguments = (NVList)orb.create_list(10);
	Any a = orb.create_any();

	/* default return type is void */
	a.type( orb.get_primitive_tc( org.omg.CORBA.TCKind.tk_void ) );
	result_value = new jacorb.orb.NamedValue(1);
	result_value.set_value(a);
    }

    public Request( org.omg.CORBA.Object t, 
		    org.omg.CORBA.ORB _orb, 
		    jacorb.orb.Connection e, 
		    byte [] obj_key, 
		    String op, 
		    org.omg.CORBA.NVList args, 
		    org.omg.CORBA.Context c, 
		    org.omg.CORBA.NamedValue result )
    {
	target = t;
	orb = (jacorb.orb.ORB)_orb;
	connection = e;
	object_key = obj_key;
	operation = op;
	arguments = (NVList)args;
	ctx = c;
	result_value = (jacorb.orb.NamedValue)result;
    }

    public org.omg.CORBA.Object target()
    {
	return target;
    }

    public java.lang.String operation()
    {
	return operation;
    }

    public org.omg.CORBA.NVList arguments()
    {
	return arguments;
    }
    
    public org.omg.CORBA.NamedValue result()
    {
	return result_value;
    }
    
    public org.omg.CORBA.Environment env()
    {
	return env;
    }

    public org.omg.CORBA.ExceptionList exceptions()
    {
	return exceptions;
    }
    
    public org.omg.CORBA.ContextList contexts()
    {
	return contexts;
    }

    public org.omg.CORBA.Context ctx()
    {
	return ctx;
    }

    public void ctx( org.omg.CORBA.Context a)
    {
	ctx = a;
    }

    public Any add_in_arg()
    {
	NamedValue nv = arguments.add(org.omg.CORBA.ARG_IN.value);
	((jacorb.orb.NamedValue)nv).set_value( orb.create_any());
	return nv.value();
    }

    public Any add_named_in_arg(java.lang.String name)
    {
	NamedValue nv = arguments.add_item(name,org.omg.CORBA.ARG_IN.value);
	((jacorb.orb.NamedValue)nv).set_value( orb.create_any());
	return nv.value();
    }

    public Any add_inout_arg()
    {
	NamedValue nv = arguments.add(org.omg.CORBA.ARG_INOUT.value);
	((jacorb.orb.NamedValue)nv).set_value( orb.create_any());
	return nv.value();
    }	

    public Any add_named_inout_arg(java.lang.String name)
    {
	NamedValue nv = arguments.add_item(name,org.omg.CORBA.ARG_INOUT.value);
	((jacorb.orb.NamedValue)nv).set_value( orb.create_any());
	return nv.value();
    }

    public Any add_out_arg()
    {
	NamedValue nv = arguments.add(org.omg.CORBA.ARG_OUT.value);
	((jacorb.orb.NamedValue)nv).set_value( orb.create_any());
	return nv.value();	
    }

    public Any add_named_out_arg(java.lang.String name)
    {
	NamedValue nv = arguments.add_item( name, org.omg.CORBA.ARG_OUT.value );
	((jacorb.orb.NamedValue)nv).set_value( orb.create_any());
	return nv.value();
    }

    /**
     * default return type is void
     */

    public void set_return_type( org.omg.CORBA.TypeCode tc)
    {
	result_value.value().type(tc);
    }

    public Any return_value()
    {
	return result_value.value();
    }

    private void _read_result()
    {
	if( result_value.value().type().kind() != org.omg.CORBA.TCKind.tk_void )
	    result_value.value().read_value( reply, result_value.value().type() );
	
	/** get out/inout parameters if any */
	for( Enumeration e = ((jacorb.orb.NVList)arguments).enumerate(); e.hasMoreElements();)
	{
	    jacorb.orb.NamedValue nv = (jacorb.orb.NamedValue)e.nextElement();
	    if( nv.flags() != org.omg.CORBA.ARG_IN.value )
		nv.receive(reply);   
	}
    }

    private void _invoke(boolean response_expected)
    {
	org.omg.CORBA.Object old_target = target;

	if( jacorb.util.Environment.clientInterceptRequests())
	    orb.client_requestIntercept_pre(this);

	// if client inteceptor changed target, rebind
	jacorb.orb.Delegate deleg = 
	    (jacorb.orb.Delegate)((org.omg.CORBA.portable.ObjectImpl)target)._get_delegate();
	if( target != old_target )
	{
	    deleg.bind();
	    
	    object_key = deleg.getObjectKey();
	    Connection c = deleg.connection;
	    if( c != connection )
		connection = c;
	}

	// devik: if connection's tcs was not negotiated yet, mark all requests
	// with codeset servicecontext.
	org.omg.IOP.ServiceContext [] ctx=new org.omg.IOP.ServiceContext[0];
	ctx=connection.addCodeSetContext(ctx,deleg.pior);
	
	RequestOutputStream ros = new RequestOutputStream( connection, orb, operation, 
					response_expected, object_key, ctx);

	for( Enumeration e = ((jacorb.orb.NVList)arguments).enumerate(); e.hasMoreElements();)
	{
	    jacorb.orb.NamedValue nv = (jacorb.orb.NamedValue)e.nextElement();
	    if( nv.flags() != org.omg.CORBA.ARG_OUT.value )
		nv.send(ros);   
	}

	ReplyInputStream rep = null;
	try 
	{ 
	    ros.close(); 
	    reply = ((ReplyInputStream)connection.sendRequest(target,(RequestOutputStream)ros)).result();
		
		// devik: if tcs was not negotiated yet, in every context we will send
		// tcs wanted. After first such request was sent (and it is here) we can
		// mark connection tcs as negotiated
		connection.markTcsNegotiated();
		
	    if( response_expected )
	    {
		_read_result();
	    }
	} 
	catch( org.omg.CORBA.portable.ApplicationException ae )
	{
	    env.exception(new RuntimeException(ae.getId()));
	}
	catch (Exception e)
	{
	    env.exception(e);
	    // throw new ApplicationException(e.getMessage(), null);
	}
	if( jacorb.util.Environment.clientInterceptRequests())
	    orb.client_requestIntercept_post(this);

    }

    public void invoke()
    {
	_invoke(true);
    }

    public void send_oneway()
    {
	_invoke(false);
    }

    class Caller
	extends Thread 
    {
	private Request r;
	public Caller( Request client )
	{
	    r = client;
	}
	
	public void run()
	{
	    r.invoke();
	}
    }

    public synchronized void send_deferred()
    {
	Caller c = new Caller(this);
	deferred_caller = c;
	deferred_caller.start();
    }

    public synchronized void get_response()
    {
	if( deferred_caller != null && deferred_caller.isAlive() )
	{
	    try 
	    {
		deferred_caller.join();
	    } 
	    catch ( InterruptedException i ){}
	    deferred_caller = null;
	}
    }

    public boolean poll_response()
    {
	if( deferred_caller != null )
	    return !deferred_caller.isAlive();
	else
	    return false;
    }

}


