/*
 * @(#)CDROutputObject.java	1.55 05/11/17
 * 
 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */

package com.sun.corba.se.impl.encoding;

import java.io.IOException;
import java.nio.ByteBuffer;

import org.omg.CORBA.Any;
import org.omg.CORBA.Principal;
import org.omg.CORBA.TypeCode;
import org.omg.CORBA.portable.InputStream;

import com.sun.corba.se.pept.encoding.OutputObject;
import com.sun.corba.se.pept.protocol.MessageMediator;

import com.sun.corba.se.spi.encoding.CorbaOutputObject ;
import com.sun.corba.se.spi.ior.iiop.GIOPVersion;
import com.sun.corba.se.spi.orb.ORB;
import com.sun.corba.se.spi.protocol.CorbaMessageMediator;
import com.sun.corba.se.pept.transport.ByteBufferPool;
import com.sun.corba.se.spi.transport.CorbaConnection;
import com.sun.corba.se.spi.logging.CORBALogDomains;

import com.sun.corba.se.spi.servicecontext.ServiceContexts;
import com.sun.corba.se.impl.encoding.BufferManagerFactory;
import com.sun.corba.se.impl.encoding.ByteBufferWithInfo;
import com.sun.corba.se.impl.encoding.CDROutputStream;
import com.sun.corba.se.impl.encoding.CDROutputStream_1_0;
import com.sun.corba.se.impl.encoding.CodeSetConversion;
import com.sun.corba.se.impl.encoding.CodeSetComponentInfo;
import com.sun.corba.se.impl.encoding.OSFCodeSetRegistry;
import com.sun.corba.se.impl.orbutil.ORBUtility;
import com.sun.corba.se.impl.protocol.giopmsgheaders.Message;
import com.sun.corba.se.impl.protocol.giopmsgheaders.MessageBase;
import com.sun.corba.se.impl.logging.ORBUtilSystemException;
import com.sun.corba.se.impl.logging.OMGSystemException;

/**
 * @author Harold Carr
 */
public class CDROutputObject extends CorbaOutputObject
{
    private Message header;
    private ORB orb;
    private ORBUtilSystemException wrapper;
    private OMGSystemException omgWrapper;

    // REVISIT - only used on sendCancelRequest.
    private CorbaConnection connection;

    private CDROutputObject(
        ORB orb, GIOPVersion giopVersion, Message header,
	BufferManagerWrite manager, byte streamFormatVersion,
	CorbaMessageMediator mediator)
    {
	super(orb, giopVersion, header.getEncodingVersion(),
	      false, manager, streamFormatVersion,
	      ((mediator != null && mediator.getConnection() != null) ?
	       ((CorbaConnection)mediator.getConnection()).
	             shouldUseDirectByteBuffers() : false));

	this.header = header;
        this.orb = orb;
	this.wrapper = ORBUtilSystemException.get( orb, CORBALogDomains.RPC_ENCODING ) ;
	this.omgWrapper = OMGSystemException.get( orb, CORBALogDomains.RPC_ENCODING ) ;

        getBufferManager().setOutputObject(this);
	this.corbaMessageMediator = mediator;
    }

    public CDROutputObject(ORB orb,
			   MessageMediator messageMediator,
			   Message header,
			   byte streamFormatVersion) 
    {
        this(
	    orb, 
	    ((CorbaMessageMediator)messageMediator).getGIOPVersion(), 
	    header, 
	    BufferManagerFactory.newBufferManagerWrite(
		((CorbaMessageMediator)messageMediator).getGIOPVersion(),
		header.getEncodingVersion(),
		orb),
	    streamFormatVersion,
	    (CorbaMessageMediator)messageMediator);
    }

    // NOTE: 
    // Used in SharedCDR (i.e., must be grow).
    // Used in msgtypes test.
    public CDROutputObject(ORB orb,
			   MessageMediator messageMediator,
			   Message header,
			   byte streamFormatVersion,
			   int strategy) 
    {
        this( 
	    orb, 
	    ((CorbaMessageMediator)messageMediator).getGIOPVersion(), 
	    header, 
            BufferManagerFactory.
	        newBufferManagerWrite(strategy,
				      header.getEncodingVersion(),
				      orb),
	    streamFormatVersion,
	    (CorbaMessageMediator)messageMediator);
    }

    // REVISIT 
    // Used on sendCancelRequest.
    // Used for needs addressing mode.
    public CDROutputObject(ORB orb, CorbaMessageMediator mediator,
			   GIOPVersion giopVersion,
			   CorbaConnection connection, Message header,
			   byte streamFormatVersion)
    {
        this(
	    orb, 
	    giopVersion, 
	    header, 
            BufferManagerFactory.
	    newBufferManagerWrite(giopVersion,
				  header.getEncodingVersion(),
				  orb),
	    streamFormatVersion,
	    mediator);
	this.connection = connection ;
    }

    // XREVISIT
    // Header should only be in message mediator.
    // Another possibility: merge header and message mediator.
    // REVISIT - make protected once all encoding together
    public Message getMessageHeader() {
        return header;
    }

    public final void finishSendingMessage() {
        getBufferManager().sendMessage();
    }

    /**
     * Write the contents of the CDROutputStream to the specified
     * output stream.  Has the side-effect of pushing any current
     * Message onto the Message list.
     * @param s The output stream to write to.
     */
    public void writeTo(CorbaConnection connection)
	throws java.io.IOException 
    {

        //
        // Update the GIOP MessageHeader size field.
        //

        ByteBufferWithInfo bbwi = getByteBufferWithInfo();

        getMessageHeader().setSize(bbwi.byteBuffer, bbwi.getSize());

        if (orb() != null) {
	    if (((ORB)orb()).transportDebugFlag) {
		dprint(".writeTo: " + connection);
	    }
	    if (((ORB)orb()).giopDebugFlag) {
		CDROutputStream_1_0.printBuffer(bbwi);
	    }
        }
	bbwi.byteBuffer.position(0).limit(bbwi.getSize());
	connection.write(bbwi.byteBuffer);
    }

    /** overrides create_input_stream from CDROutputStream */
    public org.omg.CORBA.portable.InputStream create_input_stream()
    {
        // XREVISIT
	return null;
        //return new XIIOPInputStream(orb(), getByteBuffer(), getIndex(), 
	    //isLittleEndian(), getMessageHeader(), conn);
    }

    public CorbaConnection getConnection() 
    {
	// REVISIT - only set when doing sendCancelRequest.
	if (connection != null) {
	    return connection;
	}
	return (CorbaConnection) corbaMessageMediator.getConnection();
    }

    // XREVISIT - If CDROutputObject doesn't live in the iiop
    // package, it will need this, here, to give package access
    // to xgiop.
    // REVISIT - make protected once all encoding together
    public final ByteBufferWithInfo getByteBufferWithInfo() {
        return super.getByteBufferWithInfo();
    }

    // REVISIT - make protected once all encoding together
    public final void setByteBufferWithInfo(ByteBufferWithInfo bbwi) {
        super.setByteBufferWithInfo(bbwi);
    }

    /**
     * Override the default CDR factory behavior to get the
     * negotiated code sets from the connection.
     *
     * These are only called once per message, the first time needed.
     *
     * In the local case, there is no Connection, so use the
     * local code sets.
     */
    protected CodeSetConversion.CTBConverter createCharCTBConverter() {
        CodeSetComponentInfo.CodeSetContext codesets = getCodeSets();

        // If the connection doesn't have its negotiated
        // code sets by now, fall back on the defaults defined
        // in CDRInputStream.
        if (codesets == null)
            return super.createCharCTBConverter();
        
        OSFCodeSetRegistry.Entry charSet
            = OSFCodeSetRegistry.lookupEntry(codesets.getCharCodeSet());

        if (charSet == null)
	    throw wrapper.unknownCodeset( charSet ) ;

        return CodeSetConversion.impl().getCTBConverter(charSet, 
                                                        isLittleEndian(), 
                                                        false);
    }

    protected CodeSetConversion.CTBConverter createWCharCTBConverter() {

        CodeSetComponentInfo.CodeSetContext codesets = getCodeSets();

        // If the connection doesn't have its negotiated
        // code sets by now, we have to throw an exception.
        // See CORBA formal 00-11-03 13.9.2.6.
        if (codesets == null) {
            if (getConnection().isServer())
		throw omgWrapper.noClientWcharCodesetCtx() ;
            else
		throw omgWrapper.noServerWcharCodesetCmp() ;
        }

        OSFCodeSetRegistry.Entry wcharSet
            = OSFCodeSetRegistry.lookupEntry(codesets.getWCharCodeSet());

        if (wcharSet == null)
	    throw wrapper.unknownCodeset( wcharSet ) ;

        boolean useByteOrderMarkers
            = ((ORB)orb()).getORBData().useByteOrderMarkers();

        // With UTF-16:
        //
        // For GIOP 1.2, we can put byte order markers if we want to, and
        // use the default of big endian otherwise.  (See issue 3405b)
        //
        // For GIOP 1.1, we don't use BOMs and use the endianness of
        // the stream.
        if (wcharSet == OSFCodeSetRegistry.UTF_16) {
            if (getGIOPVersion().equals(GIOPVersion.V1_2)) {
                return CodeSetConversion.impl().getCTBConverter(wcharSet, 
                                                                false, 
                                                                useByteOrderMarkers);
            }

            if (getGIOPVersion().equals(GIOPVersion.V1_1)) {
                return CodeSetConversion.impl().getCTBConverter(wcharSet,
                                                                isLittleEndian(),
                                                                false);
            }
        }

        // In the normal case, let the converter system handle it
        return CodeSetConversion.impl().getCTBConverter(wcharSet, 
                                                        isLittleEndian(),
                                                        useByteOrderMarkers);
    }

    // If we're local and don't have a Connection, use the
    // local code sets, otherwise get them from the connection.
    // If the connection doesn't have negotiated code sets
    // yet, then we use ISO8859-1 for char/string and wchar/wstring
    // are illegal.
    private CodeSetComponentInfo.CodeSetContext getCodeSets() {
        if (getConnection() == null)
            return CodeSetComponentInfo.LOCAL_CODE_SETS;
        else
            return getConnection().getCodeSetContext();
    }

    protected void dprint(String msg)
    {
	ORBUtility.dprint("CDROutputObject", msg);
    }
}

// End of file.
