package com.sap.dbtech.jdbc.packet;

/*


 ========== 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


 */
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.util.MessageKey;
import com.sap.dbtech.util.MessageTranslator;
import com.sap.dbtech.util.StructuredMem;
import com.sap.dbtech.vsp00.LongDesc;

public class DataPartFixed extends DataPart {
    /**
     * DataPart constructor comment.
     * 
     * @param rawMem
     *            com.sap.dbtech.util.StructuredMem
     */
    DataPartFixed(StructuredMem rawMem, RequestPacket requestPacket) {
        super(rawMem, requestPacket);
    }

    public void putDefineByte(int value, int offset) {
        this.putInt1(value, offset);
    }

    public void addRow(int fieldCount) {
        // nothing to do with fixed Datapart
    }

    public void addArg(int pos, int len) {
        ++this.argCount;
        this.extent = Math.max(this.extent, pos + len);
    }

    /**
     * @param pos
     *            int
     * @param len
     *            int
     */
    public void putNull(int pos, int len) {
        this.putInt1(-1, pos - 1);
        this.putBytes(new byte[len], pos);
        this.addArg(pos, len);
    }

    public void putDefault(int pos, int len) {
        this.putInt1(-3, pos - 1);
        this.putBytes(new byte[len], pos);
        this.addArg(pos, len);
    }

    public StructuredMem putDescriptor(int pos, byte[] descriptor) {
        this.putDefineByte(0, pos++);
        this.putBytes(descriptor, pos);
        return this.getPointer(pos);
    }

    public boolean fillWithOMSReader(Reader stream, int rowSize)
            throws SQLException {
        // We have to:
        // - read and write only multiples of 'rowSize' (which should be 2)
        // - but up to maxReadLength

        boolean streamExhausted = false;
        int maxDataSize = (getMaxDataSize() / rowSize) * rowSize;

        int readBufSize = (4096 / rowSize) * rowSize;
        if (readBufSize == 0) {
            readBufSize = rowSize;
        }
        char[] readBuf = new char[readBufSize];
        int charsRead = 0;
        int bytesWritten = 0;
        while (!streamExhausted && maxDataSize > 0) {
            charsRead = 0;
            int startPos = 0;
            int charsToRead = Math.min(maxDataSize / 2, readBufSize);
            int currCharsRead = 0;
            while (charsToRead != 0) {
                try {
                    currCharsRead = stream.read(readBuf, startPos, charsToRead);
                } catch (IOException ioex) {
                    throw new SQLExceptionSapDB(MessageTranslator.translate(
                            MessageKey.ERROR_STREAM_IOEXCEPTION, ioex
                                    .getMessage()));
                }
                // if the stream is exhausted, we have to look
                // whether it is wholly written.
                if (currCharsRead == -1) {
                    if ((charsRead * 2) % rowSize != 0) {
                        throw new SQLExceptionSapDB(MessageTranslator
                                .translate(MessageKey.ERROR_STREAM_ODDSIZE));
                    } else {
                        charsToRead = 0;
                        streamExhausted = true;
                    }
                } else {
                    charsRead += currCharsRead;
                    // does it fit, then it is ok.
                    if (charsRead > 0 && (charsRead * 2) % rowSize == 0) {
                        charsToRead = 0;
                    } else {
                        // else advance in the buffer
                        charsToRead -= currCharsRead;
                        startPos += currCharsRead;
                    }
                }
            }

            putBigUnicode(readBuf, this.extent, charsRead * 2);
            this.extent += charsRead * 2;
            maxDataSize -= charsRead * 2;
            bytesWritten += charsRead * 2;
        }
        // The number of arguments is the number of rows
        this.argCount = bytesWritten / rowSize;
        // the data must be marked as 'last part' in case it is a last
        // part.
        if (streamExhausted) {
            setLastPart();
        }
        return streamExhausted;
    }

    public boolean fillWithProcedureReader(Reader reader, short rowCount)
            throws SQLException {
        boolean streamExhausted = false;
        int maxDataSize = (getMaxDataSize() / 2) * 2;
    
        int readBufSize = (4096 / 2) * 2;
        if (readBufSize == 0) {
            readBufSize = 2;
        }
        char[] readBuf = new char[readBufSize];
        int charsRead = 0;
        int bytesWritten = 0;
        while (!streamExhausted && maxDataSize > 0) {
            charsRead = 0;
            int startPos = 0;
            int charsToRead = Math.min(maxDataSize / 2, readBufSize);
            int currCharsRead = 0;
            while (charsToRead != 0) {
                try {
                    currCharsRead = reader.read(readBuf, startPos, charsToRead);
                } catch (IOException ioex) {
                    throw new SQLExceptionSapDB(MessageTranslator.translate(
                            MessageKey.ERROR_STREAM_IOEXCEPTION, ioex
                                    .getMessage()));
                }
                // if the stream is exhausted, we have to look
                // whether it is wholly written.
                if (currCharsRead == -1) {
                    charsToRead = 0;
                    streamExhausted = true;
                } else {
                    charsRead += currCharsRead;
                    // does it fit, then it is ok.
                    if (charsRead > 0) {
                        charsToRead = 0;
                    } else {
                        // else advance in the buffer
                        charsToRead -= currCharsRead;
                        startPos += currCharsRead;
                    }
                }
            }
    
            putBigUnicode(readBuf, this.extent, charsRead * 2);
            this.extent += charsRead * 2;
            maxDataSize -= charsRead * 2;
            bytesWritten += charsRead * 2;
        }
        // The number of arguments is the number of rows
        this.argCount = bytesWritten / 2;
        // the data must be marked as 'last part' in case it is a last
        // part.
        if (streamExhausted) {
            setLastPart();
        }
        return streamExhausted;
    }

    public void fillWithOMSReturnCode(int returncode) throws SQLExceptionSapDB {
        putInt4(returncode, this.extent);
        this.extent += 4;
        ++this.argCount;
    }

    public boolean fillWithOMSStream(java.io.InputStream stream, boolean asciiForUnicode)
            throws SQLException {
    
        // We have to:
        // - read and write only multiples of 'rowSize'
        // - but up to maxReadLength
    
        boolean streamExhausted = false;
        int maxDataSize = getMaxDataSize();
        int readBufSize = 4096;
        byte[] readBuf = new byte[readBufSize];
        byte[] expandbuf = null;
        if (asciiForUnicode) {
            expandbuf = new byte[readBufSize * 2];
        }
        int bytesRead = 0;
        int bytesWritten = 0;
        while (!streamExhausted && maxDataSize > (asciiForUnicode ? 1 : 0)) {
            bytesRead = 0;
            int startPos = 0;
            int bytesToRead = Math.min(maxDataSize / (asciiForUnicode ? 2 : 1),
                    readBufSize);
            int currBytesRead = 0;
            while (bytesToRead != 0) {
                try {
                    currBytesRead = stream.read(readBuf, startPos, bytesToRead);
                } catch (IOException ioex) {
                    throw new SQLExceptionSapDB(MessageTranslator.translate(
                            MessageKey.ERROR_STREAM_IOEXCEPTION, ioex
                                    .getMessage()));
                }
                // if the stream is exhausted, we have to look
                // whether it is wholly written.
                if (currBytesRead == -1) {
                    bytesToRead = 0;
                    streamExhausted = true;
                } else {
                    bytesRead += currBytesRead;
                    bytesToRead = 0;
                }
            }
            if (asciiForUnicode) {
                for (int i = 0; i < bytesRead; ++i) {
                    expandbuf[i * 2] = 0;
                    expandbuf[i * 2 + 1] = readBuf[i];
                }
                putBytes(expandbuf, this.extent, bytesRead * 2);
                this.extent += bytesRead * 2;
                maxDataSize -= bytesRead * 2;
                bytesWritten += bytesRead * 2;
            } else {
                putBytes(readBuf, this.extent, bytesRead);
                this.extent += bytesRead;
                maxDataSize -= bytesRead;
                bytesWritten += bytesRead;
            }
        }
        // The number of arguments is the number of rows
        this.argCount = bytesWritten / (asciiForUnicode ? 2 : 1);
        // the data must be marked as 'last part' in case it is a last
        // part.
        if (streamExhausted) {
            setLastPart();
        }
        return streamExhausted;
    }

    public boolean fillWithProcedureStream(InputStream stream, short rowCount)
            throws SQLException {
        boolean streamExhausted = false;
        int maxDataSize = getMaxDataSize();
        if (maxDataSize > Short.MAX_VALUE) {
            maxDataSize = Short.MAX_VALUE;
        }
        int rowsize = 1;
        int bytesRead = 0;
        int bytesWritten = 0;
        int readBufferSize = 4096;
        byte[] readBuffer = new byte[4096];
        while (!streamExhausted & maxDataSize > rowsize) {
            bytesRead = 0;
            int startPos = 0;
            int currBytesRead = 0;
            int bytesToRead = Math.min(maxDataSize / rowsize, readBufferSize);
            while (bytesToRead != 0) {
                try {
                    currBytesRead = stream.read(readBuffer, startPos,
                            bytesToRead);
                } catch (IOException ioEx) {
                    throw new SQLExceptionSapDB(MessageTranslator.translate(
                            MessageKey.ERROR_STREAM_IOEXCEPTION, ioEx
                                    .getMessage()));
                }
                if (currBytesRead == -1) {
                    streamExhausted = true;
                    bytesToRead = 0;
                } else {
                    bytesRead += currBytesRead;
                    bytesToRead = 0;
                }
            }
            putBytes(readBuffer, this.extent, bytesRead);
            this.extent += bytesRead;
            maxDataSize -= bytesRead;
            bytesWritten += bytesRead;
        }
    
        this.argCount = bytesWritten / rowsize;
    
        if (streamExhausted) {
            setLastPart();
        }
    
        return streamExhausted;
    }
    
    
}
