/*
 *  ClientCallBuilder.java v0.11 02-FEB-2000
 *  Copyright (c) TKK/TLM/Calypso
 *  Author: Alexey Mednonogov
 */

package codec.build;

import java.io.*;
import java.util.*;

import codec.*;
import codec.adapt.*;
import codec.convert.*;
import codec.debug.*;
import codec.dyntree.*;
import codec.export.*;
import codec.orb.*;
import codec.pco.*;
import codec.server.*;
import codec.client.*;
import codec.visit.*;
import codec.build.*;

/** Class for constructing Call PDU, based on introspection of CORBA
 *  invocation initiated by CORBA object. */
final public class ClientCallBuilder extends ServerReplyBuilder {

	// ------------------------- OUTPUT PARAMETERS -----------------------
   	
	private boolean isOneway;
	/* Defines whether operation has been declared as oneway or not. */
	public boolean getIsOneway() { return isOneway; }

	// ------------------------ INTERNAL VARIABLES -----------------------

    private static final int DEF_OPERATION = 0;
    private static final int DEF_ATTRIBUTE = 1;

    private static final int ATTR_OPTIONS_GET = 0;
    private static final int ATTR_OPTIONS_SET = 1;

	// ------------------------- CLASS OPERATIONS ------------------------

	/** Construct PDU based on information from operation call. */
	public void construct(ClientRequest clientRequest) {

        org.omg.CORBA.Contained opAttrDef = null;
        org.omg.CORBA.Any any = null;
        int opAttrDefClass = 0;
        int attrOptions = 0;
        int i;

		// Extract essential content from "ClientRequest":
        String aspName = clientRequest.getAspName();
        long callID = clientRequest.getCallID();

        org.omg.CORBA.ServerRequest request = clientRequest.getRequest();
        org.omg.CORBA.NVList nvList = null;
        org.omg.CORBA.Context ctx = null;

        // Extract name scope of the operation
        // and check validity of PDU name:

		String[] opNameScope = AspConverter.extractNameScope(aspName);

		if (opNameScope == null) {

			System.err.println("ClientCallBuilder::construct(): Internal " + 
                "error: Failed to extract name scope from PDU name.");
            System.exit(0);
		}

        String opName = opNameScope[opNameScope.length - 1];

		// Discover whether we are dealing with normal operation or
		// we are dealing with get/set method of an attribute:

		if ((opName.length() > 5) && 
			(opName.substring(0, 5).equals("_set_") ||
			 opName.substring(0, 5).equals("_get_"))) {

			opAttrDefClass = DEF_ATTRIBUTE;

			if (opName.substring(0, 5).equals("_set_"))
				 attrOptions = ATTR_OPTIONS_SET;
			else attrOptions = ATTR_OPTIONS_GET;
		}
		else {
			opAttrDefClass = DEF_OPERATION;
		}

        if (opAttrDefClass == DEF_OPERATION) {

			opAttrDef = Repository.getOperationDef(opNameScope);
			if (opAttrDef == null) {

				System.err.println("ClientCallBuilder::construct(): " + 
				    "Operation definition could not be retrieved from " +
                    "the Interface Repository.");
				throw new org.omg.CORBA.UNKNOWN();
			}

			// Obtain NVList from Interface Repository:
			nvList = orb.create_operation_list(
				(org.omg.CORBA.OperationDef) opAttrDef);

			if (((org.omg.CORBA.OperationDef) opAttrDef).mode() ==
				org.omg.CORBA.OperationMode.OP_ONEWAY)
			{ isOneway = true; } else { isOneway = false; }

			request.params(nvList);
			clientRequest.nvList = nvList;

            if (((org.omg.CORBA.OperationDef) opAttrDef).
				contexts().length != 0) { ctx = request.ctx(); }

			// Obtain result from Interface Repository:
			any = orb.create_any();
			any.type(((org.omg.CORBA.OperationDef) opAttrDef).result());

			request.result(any);
			clientRequest.result = any;
		}
		else {

			opAttrDef = Repository.getAttributeDef(opNameScope);
			if (opAttrDef == null) {

				System.err.println("ClientCallBuilder::construct(): " + 
				    "Attribute definition could not be retrieved from " +
                    "the Interface Repository.");
				throw new org.omg.CORBA.UNKNOWN();
			}
            isOneway = false;
            nvList = orb.create_list(1);

			if (attrOptions == ATTR_OPTIONS_SET) {

				any = orb.create_any();
				any.type(((org.omg.CORBA.AttributeDef) opAttrDef).type());

				nvList.add_value(opName.substring(5, 6), any,
					org.omg.CORBA.ARG_IN.value);

                request.params(nvList);
                clientRequest.nvList = nvList;

                any = orb.create_any();
                any.type(orb.get_primitive_tc(org.omg.CORBA.TCKind.tk_void));

                request.result(any);
				clientRequest.result = any;
			}
			else {
                request.params(nvList);
                clientRequest.nvList = nvList;

				any = orb.create_any();
				any.type(((org.omg.CORBA.AttributeDef) opAttrDef).type());

				request.result(any);
				clientRequest.result = any;
			}
		}

		// Initialize Call PDU:
        asp = new com.t3.ot.pco.ASPimpl(aspName);

		// Add Begin of TTCN table tag:
        asp.beginTable(aspName);

        // Add CALL_ID field:
        asp.addInteger("CALL_ID", (int) callID); // Modify.

        // Build PDU "CONTEXT" field if context clause is present in
        // IDL declaration of operation:

        if (ctx != null) {

            asp.beginSequenceOf("CONTEXT");
            org.omg.CORBA.NVList ctxNvList = null;

            try { ctxNvList = ctx.get_values("", 0, "*"); }
            catch (org.omg.CORBA.BAD_CONTEXT bcex) {

				// Here we are if context property list were empty:
				ctxNvList = orb.create_list(1);
			}

            for (i = 0; i < ctxNvList.count(); i++) {

				org.omg.CORBA.NamedValue namedValue = null;
				try {
					namedValue = ctxNvList.item(i);
				}
				catch (org.omg.CORBA.Bounds ex) {

					System.err.println("ClientCallBuilder::" +
					    "construct(): Index out of bounds.");
					System.exit(0);
				}

                asp.addCharString("$unnamed", namedValue.name());
                asp.addCharString("$unnamed", namedValue.value().
                    extract_string());
			}
            asp.endSequenceOf();
		}

        // Serialize operation call into "DynIterator":
		BuilderAdapter adapter = new BuilderAdapter();
		adapter.create(nvList, null, BuilderAdapter.OPTIONS_CLIENT_CALL);
		DynIterator dynIterator = adapter.iterator();

        // Set initial state of Builder automaton:
		lastStructType = new int[MAX_INTROSPECTION_LEVELS];
		lastStructType[0] = STRUCT_TYPE_NONE;
		isFirstMemberProcessed = false;
        isRetValuePresent = false;
        introspectionLevel = 0;
		isExcBody = false;

		// Convert "DynIterator" into Call PDU, recalling that:

		// -- prefix "PAR_" shall be added to all items of level 0;
		// -- prefix "seq_" shall be added to all members of "struct";
		// -- prefix "ch_" shall be added to all members of "union";
        // -- item name "" usually indicates sequence/array membership.

		if (dynIterator.hasNext()) { addComponent(dynIterator.next()); }
		isFirstMemberProcessed = true;
		while (dynIterator.hasNext()) { addComponent(dynIterator.next()); }

		if (introspectionLevel != 0) {

			System.err.println("ClientCallBuilder::construct(): " +
                "Internal error -- incorrect Builder state " +
                "(INTROSPECTION_LEVEL).");
		    System.exit(0);
		}

        asp.endTable();
	}
}
