/*********************************************************************
 *
 *      Copyright (C) 2002 Andrew Khan
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 ***************************************************************************/

package org.xbib.datastructures.xslx.jxl.write.biff;

import java.util.ArrayList;
import java.util.Iterator;
import org.xbib.datastructures.xslx.jxl.biff.IntegerHelper;
import org.xbib.datastructures.xslx.jxl.biff.StringHelper;
import org.xbib.datastructures.xslx.jxl.biff.Type;
import org.xbib.datastructures.xslx.jxl.biff.WritableRecordData;

/**
 * A continuation of a shared string table record.
 */
class SSTContinueRecord extends WritableRecordData {
    /**
     * The maximum amount of bytes available for the SST record
     */
    private static final int maxBytes = 8228 - // max length
            4;  // standard biff record stuff
    /**
     * The first string is a special case
     */
    private String firstString;
    /**
     * Indicates whether or not we need to include the length information
     * for the first string
     */
    private boolean includeLength;
    /**
     * The length of the first string
     */
    private int firstStringLength;
    /**
     * The list of strings
     */
    private final ArrayList strings;
    /**
     * The list of string lengths
     */
    private final ArrayList stringLengths;
    /**
     * The binary data
     */
    private byte[] data;
    /**
     * The count of bytes needed so far to contain this record
     */
    private int byteCount;

    /**
     * Constructor
     *
     * @param numRefs the number of string references in the workbook
     * @param s       the number of strings
     */
    public SSTContinueRecord() {
        super(Type.CONTINUE);

        byteCount = 0;
        strings = new ArrayList(50);
        stringLengths = new ArrayList(50);
    }

    /**
     * Adds the first string to this SST record
     *
     * @param s the string to add
     * @param b include the length information for the first string
     * @return the number of characters not added
     */
    public int setFirstString(String s, boolean b) {
        includeLength = b;
        firstStringLength = s.length();

        int bytes = 0;

        if (!includeLength) {
            bytes = s.length() * 2 + 1;
        } else {
            bytes = s.length() * 2 + 3;
        }

        if (bytes <= maxBytes) {
            firstString = s;
            byteCount += bytes;
            return 0;
        }

        // Calculate the number of characters we can add
        // The bytes variable will always be an odd number
        int charsAvailable = includeLength ? (maxBytes - 4) / 2 :
                (maxBytes - 2) / 2;

        // Add what part of the string we can
        firstString = s.substring(0, charsAvailable);
        byteCount = maxBytes - 1;

        return s.length() - charsAvailable;
    }

    /**
     * Gets the current offset into this record, excluding the header fields
     *
     * @return the number of bytes after the header field
     */
    public int getOffset() {
        return byteCount;
    }

    /**
     * Adds a string to this  record.  It returns the number of string
     * characters not added, due to space constraints.  In the event
     * of this being non-zero, a continue record will be needed
     *
     * @param s the string to add
     * @return the number of characters not added
     */
    public int add(String s) {
        int bytes = s.length() * 2 + 3;

        // Must be able to add at least the first character of the string
        // onto the SST
        if (byteCount >= maxBytes - 5) {
            return s.length();
        }

        stringLengths.add(new Integer(s.length()));

        if (bytes + byteCount < maxBytes) {
            // add the string and return
            strings.add(s);
            byteCount += bytes;
            return 0;
        }

        // Calculate the number of characters we can add
        int bytesLeft = maxBytes - 3 - byteCount;
        int charsAvailable = bytesLeft % 2 == 0 ? bytesLeft / 2 :
                (bytesLeft - 1) / 2;

        // Add what part of the string we can
        strings.add(s.substring(0, charsAvailable));
        byteCount += charsAvailable * 2 + 3;

        return s.length() - charsAvailable;
    }

    /**
     * Gets the binary data for output to file
     *
     * @return the binary data
     */
    public byte[] getData() {
        data = new byte[byteCount];

        int pos = 0;

        // Write out the first string
        if (includeLength) {
            IntegerHelper.getTwoBytes(firstStringLength, data, 0);
            data[2] = 0x01;
            pos = 3;
        } else {
            // Just include the unicode indicator
            data[0] = 0x01;
            pos = 1;
        }

        StringHelper.getUnicodeBytes(firstString, data, pos);
        pos += firstString.length() * 2;

        // Now write out the remainder of the strings
        Iterator i = strings.iterator();
        String s = null;
        int length = 0;
        int count = 0;
        while (i.hasNext()) {
            s = (String) i.next();
            length = ((Integer) stringLengths.get(count)).intValue();
            IntegerHelper.getTwoBytes(length, data, pos);
            data[pos + 2] = 0x01;
            StringHelper.getUnicodeBytes(s, data, pos + 3);
            pos += s.length() * 2 + 3;
            count++;
        }

        return data;
    }
}
