/*
 *  AspConverter.java v0.10 20-DEC-1999
 *  Copyright (c) TKK/TLM/Calypso
 *  Author: Alexey Mednonogov
 */

package codec.convert;

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 containing various static conversion methods between ATS ASPs
 *  and SUT packets. */
final public class AspConverter {

	private static Long uniqueID = new Long(1);

	/** Return a value which is guaranteed to be unique within current
	 *  session of Gateway execution. */
	public static long getUniqueID() {

        long result = 0;
		synchronized (uniqueID) {

            result = uniqueID.longValue();
			uniqueID = new Long(result + 1);
		}
		return result;
	}

    /** Extract sequence representing interface (exception, operation) name
     *  scope from TTCN-style PCO (exception, operation) identifier. Prefix
     *  of identifier is stripped out. This is a reverse action to operation
     *  createFromNameScope().
     *  @return  <code>null</code> in case TTCN identifier is invalid, i.e. it
     *  contains odd number of "_" characters not terminated by "i" character;
     *  or identifier is terminated by valid "_i" separator; or identifier
     *  prefix is invalid. */
    public static String[] extractNameScope(String nameScope) {

        int i;
        int srcIndex;
        char srcItem;
        char[] srcArray = nameScope.toCharArray();
        boolean isTtcnPrefix = false;

        // Strip out TTCN identifier prefix:
        for (srcIndex = 0; srcIndex < (srcArray.length - 1); srcIndex++) {

            if ((srcArray[srcIndex] == '_') &&
                (srcArray[srcIndex + 1] == 'i')) {

                srcIndex += 2;
                isTtcnPrefix = true;
                break;
            }
        }
        if ((srcIndex >= srcArray.length) || (isTtcnPrefix == false)) {
            return null;
        }
        char[] stripArray = new char[srcArray.length - srcIndex];
        System.arraycopy(srcArray, srcIndex, stripArray, 0, stripArray.length);
        srcArray = stripArray;
        stripArray = null;

        // Count number of symbolic chains containing odd number of "_":
        boolean isChain = false;
        int isOdd = 0;
        int oddChainsCount = 0;

        for (srcIndex = 0; srcIndex < srcArray.length; srcIndex++) {

            srcItem = srcArray[srcIndex];

            if ((isChain == false) && (srcItem == '_')) {
                isChain = true;
                isOdd = 1;
                continue;
            }
            if ((isChain == true) && (srcItem == '_')) {
                isOdd = 1 - isOdd;
                continue;
            }           
            if ((isChain == true) && (srcItem != '_')) {
                isChain = false;
                if (isOdd == 1) {
                    if ((srcItem == 'i') &&
                        (srcIndex < (srcArray.length - 1))) {

                        oddChainsCount++;
                    }
                    else return null;
                }
                continue;
            }
        }
        if ((isChain == true) && (isOdd == 1)) return null;

        // Allocate space for detected amount of identifiers:
        StringBuffer[] bufArray = new StringBuffer[oddChainsCount + 1];
        for (i = 0; i < bufArray.length; i++) bufArray[i] = new StringBuffer();

        // Extract detected name scope identifiers:
        isChain = false;
        isOdd = 0;
        int bufIndex = 0;
        for (srcIndex = 0; srcIndex < srcArray.length; srcIndex++) {

            srcItem = srcArray[srcIndex];

            if ((isChain == false) && (srcItem == '_')) {
                isChain = true;
                isOdd = 1;
                continue;
            }
            if ((isChain == true) && (srcItem == '_')) {
                isOdd = 1 - isOdd;
                if (isOdd == 0) bufArray[bufIndex].append(srcItem);
                continue;
            }           
            if ((isChain == true) && (srcItem != '_')) {
                isChain = false;
                if (isOdd == 1) {
                    bufIndex++;
					continue;
                }
            }
            bufArray[bufIndex].append(srcItem);
        }
        
        String[] dstArray = new String[bufArray.length];
        for (i = 0; i < bufArray.length; i++) {
            dstArray[i] = bufArray[i].toString();
        }

        return dstArray;
    }

    /** Create TTCN-style PCO (exception, operation) identifier from sequence
     *  representing interface (exception, operation) name scope. Identifier
     *  prefix shall be appended to the return value by the calling method.
     *  This is a reverse action to operation extractNameScope().
     *  @return Operation always returns valid string. */
    public static String createFromNameScope(String[] nameScope) {

        StringBuffer dstBuffer = new StringBuffer();
        for (int i = 0; i < nameScope.length; i++) {

            String nameItem = nameScope[i];
            for (int j = 0; j < nameItem.length(); j++) {

                char ch = nameItem.charAt(j);
                if (ch != '_') dstBuffer.append(ch);
                else dstBuffer.append("__");
            }
            if (i < (nameScope.length - 1)) dstBuffer.append("_i");
        }
        return dstBuffer.toString();
    }

    /** Send ASP through PCO identified by "pcoName". In case "pcoName"
     *  refers to invalid PCO name, warning message is issued and no ASP
     *  is sent.  */
    public static void sendASP(String pcoName, com.t3.ot.pco.ASP asp) {

        OrbPco pco = CorbaServer.getCodecObject().getOrbPcoPool().
            getObject(pcoName);

        if (pco == null) {
            System.err.println("AspConverter::sendASP(): An attempt is " +
                "made to send PDU through non-existing PCO \"" + pcoName +
                "\". No PDU is sent.");
            return;
        }
        pco.sendASP(asp);
    }

    /** Convert exception thrown by SUT, Gateway, CORBA or Java environment
     *  to Exception PDU. Exception body is assumed to be void.
     *  @param <code>excBody</code> must be null if exception has no body */
    public static com.t3.ot.pco.ASP createServerException(long callID,
        String[] excScope, String excName) {

        com.t3.ot.pco.ASP asp = new com.t3.ot.pco.ASPimpl("pRAISE");
        asp.beginTable("pRAISE");

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

        asp.beginSequenceOf("EXC_SCOPE");
        for (int i = 0; i < excScope.length; i++)
        { asp.addCharString("$unnamed", excScope[i]); }
        asp.endSequenceOf();

        asp.addCharString("EXC_NAME", excName);

        // EXC_BODY is assumed to be void:
        asp.beginTable("EXC_BODY");
        asp.beginSequence("EXC_BODY");
        asp.addInteger("seq_error_code", 0);
        asp.endSequence();
        asp.endTable();

        asp.endTable();

        return asp;
    }

    /** Send <code>GatewayException.Recoverable.General</code> Exception
     *  PDU to Tester. */
    public static void throwRecoverableException(String pcoName) {

        long callID = 0;
        String[] excScope = { "GatewayException", "Recoverable" };
        String excName = "General";
        
        com.t3.ot.pco.ASP asp =
            createServerException(callID, excScope, excName);
        System.out.println("AspConverter::send(): Throwing " +
            "GatewayException.Recoverable.General exception.");

        sendASP(pcoName, asp);
    }

    /** Send <code>GatewayException.Fatal.General</code> Exception PDU to
     *  Tester. */
    public static void throwFatalException(String pcoName) {

        long callID = 0;
        String[] excScope = { "GatewayException", "Fatal" };
        String excName = "General";
        
        com.t3.ot.pco.ASP asp =
            createServerException(callID, excScope, excName);
        System.out.println("AspConverter::send(): Throwing " +
            "GatewayException.Fatal.General exception.");

        sendASP(pcoName, asp);
    }

    /** Extract field containing IOR from Registration PDU.
     *  @return In case of error, returns <code>null</code>. */
    public static String extractIOR(com.t3.ot.pco.ASP asp) {

        String result = null;
        try {
             com.t3.ot.misc.Value value = asp.element("IOR");
             result = value.getCharString();
        }
        catch (com.t3.ot.misc.OtException oe)
        {
            System.out.println("AspConverter::" +
                "extractIOR(): OtException caught.");
			return null;
        }
        return result;
    }

    /** Extract field containing IOR file location from Registration PDU.
     *  @return In case of error, returns <code>null</code>. */
    public static String extractRefFile(com.t3.ot.pco.ASP asp) {

        String result = null;
        try {
            com.t3.ot.misc.Value value = asp.element("RFILE");
            result = value.getCharString();
        }
        catch (com.t3.ot.misc.OtException oe)
        {
            System.out.println("AspConverter::" +
                "extractRefFile(): OtException caught.");
			return null;
        }
        return result;
    }

    /** Extract object location in Naming Service from Registration PDU.
     *  @return In case of error, throws Gateway Fatal exception and 
     *  returns <code>null</code>. */
    public static String[] extractNamingContext(
		String pcoName, com.t3.ot.pco.ASP asp) {

		String[] namingContext = null;

        try {
			NamingContextVisitor namingVisitor =
				new NamingContextVisitor(pcoName, asp);
			asp.accept(namingVisitor);
			namingContext = namingVisitor.getNamingContext();
		}
		catch (VisitorInvalidFormat vif) {
			System.err.println("AspConverter::extractNamingContext(): " +
                "Registration PDU has invalid format. Throwing " +
                "GatewayException.Fatal.General exception. " +
				"Reason: " + vif.getMessage());
            AspConverter.throwFatalException(pcoName);
            return null;
		}
		catch (com.t3.ot.misc.OtException oe) {
			System.err.println("AspConverter::extractNamingContext(): " +
                "OtException caught. Throwing GatewayException." +
                "Fatal.General exception.");
            AspConverter.throwFatalException(pcoName);
            return null;
		}
		return namingContext;
    }
}
