/*


 ========== licence begin GPL
    Copyright (C) 2002-2003 SAP AG

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
    as published by the Free Software Foundation; either version 2
    of the License, or (at your option) any later version.

    This program 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 General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
    ========== licence end


 */

package com.sap.dbtech.jdbc.packet;

import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.sql.SQLException;

import com.sap.dbtech.jdbc.exceptions.SQLExceptionSapDB;
import com.sap.dbtech.jdbc.translators.Putval;
import com.sap.dbtech.util.MessageKey;
import com.sap.dbtech.util.MessageTranslator;
import com.sap.dbtech.util.StructuredMem;
import com.sap.dbtech.vsp00.LongDesc;
import com.sap.dbtech.vsp001.Part;
import com.sap.dbtech.vsp001.PartAttributes;

/**
 *  
 */
public abstract class DataPart extends com.sap.dbtech.util.MemIndirection {
    final private static int maxArgCount = Short.MAX_VALUE;

    protected int argCount = 0;

    protected int extent = 0;

    protected int massExtent = 0;

    private boolean isFull = false;

    StructuredMem originalMem;

    RequestPacket requestPacket;

    /**
     * DataPartBase constructor comment.
     * 
     * @param rawMem
     *            com.sap.dbtech.util.StructuredMem
     */
    DataPart(StructuredMem rawMem, RequestPacket requestPacket) {
        super(rawMem);
        this.originalMem = rawMem.getPointer(0);
        this.requestPacket = requestPacket;
    }

    /**
     * 
     * @param pos
     *            int
     * @param len
     *            int
     */
    public abstract void addArg(int pos, int len);

    /**
     *  
     */
    public void close() {
        int argCountOffs = -Part.Data_O + Part.ArgCount_O;
        this.originalMem.putInt2(this.argCount, argCountOffs);
        this.requestPacket.closePart(this.massExtent + this.extent,
                this.argCount);
    }

    /**
     *  
     */
    public void closeArrayPart(int rows) {
        int argCountOffs = -Part.Data_O + Part.ArgCount_O;
        this.originalMem.putInt2(rows, argCountOffs);
        this.requestPacket
                .closePart(this.massExtent + this.extent * rows, rows);
    }

    protected int getMaxDataSize() {
        return this.size() - this.extent - 8;
    }

    /**
     * 
     * @return boolean
     * @param recordSize
     *            int
     */
    public boolean hasRoomFor(int recordSize, int reserve) {
        return (this.argCount < maxArgCount && (this.size() - this.extent) > (recordSize + reserve));
    }

    /**
     * 
     * @return boolean
     * @param recordSize
     *            int
     */
    public boolean hasRoomFor(int recordSize) {
        return (this.argCount < maxArgCount && (this.size() - this.extent) > recordSize);
    }

    /**
     *  
     */
    public int getExtent() {
        return this.extent;
    }

    /**
     * 
     *  
     */
    public void setFirstPart() {
        this.requestPacket.addPartAttribute(PartAttributes.FirstPacket_E);
        if (false) {
            int attrOffs = -Part.Data_O + Part.Attributes_O;
            int partAttributes;

            partAttributes = this.originalMem.getInt1(attrOffs);
            partAttributes |= PartAttributes.FirstPacket_E;
            this.originalMem.putInt1(attrOffs, partAttributes);
        }
    }

    /**
     * 
     *  
     */
    public void setLastPart() {
        this.requestPacket.addPartAttribute(PartAttributes.LastPacket_E);
        if (false) {
            int attrOffs = -Part.Data_O + Part.Attributes_O;
            int partAttributes;

            partAttributes = this.originalMem.getInt1(attrOffs);
            partAttributes |= PartAttributes.LastPacket_E;
            this.originalMem.putInt1(attrOffs, partAttributes);
        }
    }

    /**
     *  
     */
    public void moveRecordBase() {
        this.moveBase(this.extent);
        this.massExtent += this.extent;
        this.extent = 0;
    }

    public abstract void putDefineByte(int value, int offset);

    /**
     */
    public void markEmptyStream(StructuredMem descriptorMark) {
        descriptorMark.putInt1(LongDesc.LastData_C, LongDesc.Valmode_O);
        descriptorMark.putInt4(this.massExtent + this.extent + 1,
                LongDesc.Valpos_O);
        descriptorMark.putInt4(0, LongDesc.Vallen_O);

    }

    /**
     * Fills a data part with <code>java.io.Reader</code> content.
     * 
     * @param stream
     *            The java.io.Reader instance.
     * @param rowSize
     *            The size of a single row.
     * @return <code>true</code> on EOF condition.
     * @throws SQLException
     *             when an <code>java.io.IOException</code> happens while
     *             reading from the reader.
     */
    public abstract boolean fillWithOMSReader(Reader stream, int rowSize)
            throws SQLException;

    /**
     * @param stream
     *            java.io.Reader
     * @param descriptor
     *            byte[]
     * @param descriptorMark
     *            StructuredMem
     */
//    public abstract boolean fillWithReader(java.io.Reader reader,
//            StructuredMem descriptorMark,
//            com.sap.dbtech.jdbc.translators.Putval putval) throws SQLException;

    public abstract boolean fillWithProcedureReader(Reader reader,
            short rowCount) throws SQLException;

    public abstract void addRow(int fieldCount);

    public abstract void putNull(int pos, int len);

    public abstract void putDefault(int pos, int len);

    public abstract StructuredMem putDescriptor(int pos, byte[] descriptor);

    public abstract void fillWithOMSReturnCode(int returncode) throws SQLExceptionSapDB;

    public abstract boolean fillWithOMSStream(java.io.InputStream stream,
            boolean asciiForUnicode) throws SQLException;

    public abstract boolean fillWithProcedureStream(InputStream stream,
            short rowCount) throws SQLException;

//    public abstract boolean fillWithStream(java.io.InputStream stream,
//            StructuredMem descriptorMark,
//            com.sap.dbtech.jdbc.translators.Putval putval) throws SQLException ;

    /**
     * @param stream java.io.InputStream
     * @param descriptor byte[]
     * @param descriptorMark StructuredMem
     */
    public boolean fillWithStream(java.io.InputStream stream, StructuredMem descriptorMark, com.sap.dbtech.jdbc.translators.Putval putval) throws SQLException {
    
        // not exact, but enough to prevent an overflow - adding this
        // part to the packet may at most eat up 8 bytes more, if
        // the size is weird.
        int maxDataSize = getMaxDataSize();
    
        if (maxDataSize <= 1) {
            descriptorMark.putInt1(LongDesc.NoData_C, LongDesc.Valmode_O);
            return false;
        }
        int dataStart = this.extent;
        int bytesRead;
        byte[] readBuf = new byte[4096];
        boolean streamExhausted = false;
        try {
            while (!streamExhausted && (maxDataSize > 0)) {
                bytesRead = stream.read(readBuf, 0, java.lang.Math.min(
                        maxDataSize, 4096));
                if (bytesRead == -1) {
                    streamExhausted = true;
                } else {
                    this.mem.putBytes(readBuf, this.extent, bytesRead);
                    this.extent += bytesRead;
                    maxDataSize -= bytesRead;
                }
            }
        } catch (java.io.IOException exc) {
            throw new SQLExceptionSapDB(MessageTranslator.translate(
                    MessageKey.ERROR_STREAM_IOEXCEPTION, exc.getMessage()));
            // streamExhausted = true;
        }
        /*
         * patch pos, length and kind
         */
        if (streamExhausted) {
            descriptorMark.putInt1(LongDesc.LastData_C, LongDesc.Valmode_O);
        } else {
            descriptorMark.putInt1(LongDesc.DataPart_C, LongDesc.Valmode_O);
        }
        descriptorMark.putInt4(this.massExtent + dataStart + 1,
                LongDesc.Valpos_O);
        descriptorMark.putInt4(this.extent - dataStart, LongDesc.Vallen_O);
        putval.markRequestedChunk(this.getPointer(dataStart), this.extent
                - dataStart);
        return streamExhausted;
    }

    public boolean fillWithReader(java.io.Reader reader, StructuredMem descriptorMark, com.sap.dbtech.jdbc.translators.Putval putval) throws SQLException {
        final int unicodeWidthC = 2;
        // not exact, but enough to prevent an overflow - adding this
        // part to the packet may at most eat up 8 bytes more, if
        // the size is weird.
        int maxDataSize = ((this.size() - this.extent - 8) / unicodeWidthC);
        if (maxDataSize <= 1) {
            descriptorMark.putInt1(LongDesc.NoData_C, LongDesc.Valmode_O);
            return false;
        }
        int dataStart = this.extent;
        int charsRead;
        char[] readBuf = new char[4096];
        boolean streamExhausted = false;
        try {
            while (!streamExhausted && (maxDataSize > 0)) {
                charsRead = reader.read(readBuf, 0, java.lang.Math.min(
                        maxDataSize, 4096));
                if (charsRead == -1) {
                    streamExhausted = true;
                } else {
                    this.mem.putBigUnicode(readBuf, this.extent, charsRead
                            * unicodeWidthC);
                    this.extent += charsRead * unicodeWidthC;
                    maxDataSize -= charsRead;
                }
            }
        } catch (java.io.IOException exc) {
            throw new SQLExceptionSapDB(MessageTranslator.translate(
                    MessageKey.ERROR_STREAM_IOEXCEPTION, exc.getMessage()));
    
        }
        /*
         * patch pos, length and kind
         */
        if (streamExhausted) {
            descriptorMark.putInt1(LongDesc.LastData_C, LongDesc.Valmode_O);
        } else {
            descriptorMark.putInt1(LongDesc.DataPart_C, LongDesc.Valmode_O);
        }
        descriptorMark.putInt4(this.massExtent + dataStart + 1,
                LongDesc.Valpos_O);
        descriptorMark.putInt4(this.extent - dataStart, LongDesc.Vallen_O);
        putval.markRequestedChunk(this.getPointer(dataStart), this.extent
                - dataStart);
        return streamExhausted;
    }
}