/*
 * @(#)CDROutputStream_1_1.java	1.17 03/12/19
 *
 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
package com.sun.corba.se.impl.encoding;

import org.omg.CORBA.CompletionStatus;
import com.sun.corba.se.spi.ior.iiop.GIOPVersion;
import com.sun.corba.se.impl.encoding.CodeSetConversion;

public class CDROutputStream_1_1 extends CDROutputStream_1_0
{
    // This is used to keep indirections working across fragments.  When added
    // to the current bbwi.position(), the result is the current position
    // in the byte stream without any fragment headers.
    // 
    // It is equal to the following:
    //
    // n = number of buffers (0 is original buffer, 1 is first fragment, etc)
    // 
    // n == 0, fragmentOffset = 0
    //
    // n > 0, fragmentOffset
    //          = sum i=[1,n] { bbwi_i-1_.size - buffer i header length }
    //
    protected int fragmentOffset = 0;

    protected void alignAndReserve(int align, int n) {

        // Notice that in 1.1, we won't end a fragment with
        // alignment padding.  We also won't guarantee that
        // our fragments end on evenly divisible 8 byte
        // boundaries.  There may be alignment
        // necessary with the header of the next fragment
        // since the header isn't aligned on an 8 byte
        // boundary, so we have to calculate it twice.

        int alignment = computeAlignment(align);

        if (bbwi.position() + n + alignment > bbwi.buflen) {
            grow(align, n);

            // Must recompute the alignment after a grow.
            // In the case of fragmentation, the alignment
            // calculation may no longer be correct.

            // People shouldn't be able to set their fragment
            // sizes so small that the fragment header plus
            // this alignment fills the entire buffer.
            alignment = computeAlignment(align);
        }

        bbwi.position(bbwi.position() + alignment);
    }

    protected void grow(int align, int n) {
        // Save the current size for possible post-fragmentation calculation
        int oldSize = bbwi.position();

        super.grow(align, n);
   
        // At this point, if we fragmented, we should have a ByteBufferWithInfo
        // with the fragment header already marshalled.  The size and length fields
        // should be updated accordingly, and the fragmented flag should be set.
        if (bbwi.fragmented) {

            // Clear the flag
            bbwi.fragmented = false;

            // Update fragmentOffset so indirections work properly.
            // At this point, oldSize is the entire length of the
            // previous buffer.  bbwi.position() is the length of the
            // fragment header of this buffer.
            fragmentOffset += (oldSize - bbwi.position());
        }
    }

    public int get_offset() {
	return bbwi.position() + fragmentOffset;
    }

    public GIOPVersion getGIOPVersion() {
        return GIOPVersion.V1_1;
    }

    public void write_wchar(char x)
    {
        // In GIOP 1.1, interoperability with wchar is limited
        // to 2 byte fixed width encodings.  CORBA formal 99-10-07 15.3.1.6.
        // Note that the following code prohibits UTF-16 with a byte
        // order marker (which would result in 4 bytes).
        CodeSetConversion.CTBConverter converter = getWCharConverter();

        converter.convert(x);

        if (converter.getNumBytes() != 2)
	    throw wrapper.badGiop11Ctb(CompletionStatus.COMPLETED_MAYBE);

        alignAndReserve(converter.getAlignment(),
                        converter.getNumBytes());

        parent.write_octet_array(converter.getBytes(),
                                 0,
                                 converter.getNumBytes());
    }

    public void write_wstring(String value)
    {
        if (value == null) {
	    throw wrapper.nullParam(CompletionStatus.COMPLETED_MAYBE);
        }

        // The length is the number of code points (which are 2 bytes each)
        // including the 2 byte null.  See CORBA formal 99-10-07 15.3.2.7.

    	int len = value.length() + 1;

        write_long(len);

        CodeSetConversion.CTBConverter converter = getWCharConverter();

        converter.convert(value);

        internalWriteOctetArray(converter.getBytes(), 0, converter.getNumBytes());

        // Write the 2 byte null ending
        write_short((short)0);
    }
}

