/*********************************************************************
 *
 *      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.read.biff;

import org.xbib.datastructures.xslx.jxl.WorkbookSettings;
import org.xbib.datastructures.xslx.jxl.biff.IntegerHelper;
import org.xbib.datastructures.xslx.jxl.biff.RecordData;
import org.xbib.datastructures.xslx.jxl.biff.StringHelper;
import org.xbib.datastructures.xslx.jxl.common.Logger;

/**
 * A record containing the references to the various sheets (internal and
 * external) referenced by formulas in this workbook
 */
public class SupbookRecord extends RecordData {
    public static final Type INTERNAL = new Type();
    public static final Type EXTERNAL = new Type();
    public static final Type ADDIN = new Type();
    public static final Type LINK = new Type();
    public static final Type UNKNOWN = new Type();
    /**
     * The logger
     */
    private static final Logger logger = Logger.getLogger(SupbookRecord.class);

    /**
     * The type of this supbook record
     */
    private final Type type;
    /**
     * The number of sheets - internal & external supbooks only
     */
    private int numSheets;
    /**
     * The name of the external file
     */
    private String fileName;
    /**
     * The names of the external sheets
     */
    private String[] sheetNames;
    /**
     * Constructs this object from the raw data
     *
     * @param t  the raw data
     * @param ws the workbook settings
     */
    SupbookRecord(Record t, WorkbookSettings ws) {
        super(t);
        byte[] data = getRecord().getData();

        // First deduce the type
        if (data.length == 4) {
            if (data[2] == 0x01 && data[3] == 0x04) {
                type = INTERNAL;
            } else if (data[2] == 0x01 && data[3] == 0x3a) {
                type = ADDIN;
            } else {
                type = UNKNOWN;
            }
        } else if (data[0] == 0 && data[1] == 0) {
            type = LINK;
        } else {
            type = EXTERNAL;
        }

        if (type == INTERNAL) {
            numSheets = IntegerHelper.getInt(data[0], data[1]);
        }

        if (type == EXTERNAL) {
            readExternal(data, ws);
        }
    }

    /**
     * Reads the external data records
     *
     * @param data the data
     * @param ws   the workbook settings
     */
    private void readExternal(byte[] data, WorkbookSettings ws) {
        numSheets = IntegerHelper.getInt(data[0], data[1]);

        // subtract file name encoding from the length
        int ln = IntegerHelper.getInt(data[2], data[3]) - 1;
        int pos = 0;

        if (data[4] == 0) {
            // non-unicode string
            int encoding = data[5];
            pos = 6;
            if (encoding == 0) {
                fileName = StringHelper.getString(data, ln, pos, ws);
                pos += ln;
            } else {
                fileName = getEncodedFilename(data, ln, pos);
                pos += ln;
            }
        } else {
            // unicode string
            int encoding = IntegerHelper.getInt(data[5], data[6]);
            pos = 7;
            if (encoding == 0) {
                fileName = StringHelper.getUnicodeString(data, ln, pos);
                pos += ln * 2;
            } else {
                fileName = getUnicodeEncodedFilename(data, ln, pos);
                pos += ln * 2;
            }
        }

        sheetNames = new String[numSheets];

        for (int i = 0; i < sheetNames.length; i++) {
            ln = IntegerHelper.getInt(data[pos], data[pos + 1]);

            if (data[pos + 2] == 0x0) {
                sheetNames[i] = StringHelper.getString(data, ln, pos + 3, ws);
                pos += ln + 3;
            } else if (data[pos + 2] == 0x1) {
                sheetNames[i] = StringHelper.getUnicodeString(data, ln, pos + 3);
                pos += ln * 2 + 3;
            }
        }
    }

    /**
     * Gets the type of this supbook record
     *
     * @return the type of this supbook
     */
    public Type getType() {
        return type;
    }

    /**
     * Gets the number of sheets.  This will only be non-zero for internal
     * and external supbooks
     *
     * @return the number of sheets
     */
    public int getNumberOfSheets() {
        return numSheets;
    }

    /**
     * Gets the name of the external file
     *
     * @return the name of the external file
     */
    public String getFileName() {
        return fileName;
    }

    /**
     * Gets the name of the external sheet
     *
     * @param i the index of the external sheet
     * @return the name of the sheet
     */
    public String getSheetName(int i) {
        return sheetNames[i];
    }

    /**
     * Gets the data - used when copying a spreadsheet
     *
     * @return the raw external sheet data
     */
    public byte[] getData() {
        return getRecord().getData();
    }

    /**
     * Gets the encoded string from the data array
     *
     * @param data the data
     * @param ln   length of the string
     * @param pos  the position in the array
     * @return the string
     */
    private String getEncodedFilename(byte[] data, int ln, int pos) {
        StringBuffer buf = new StringBuffer();
        int endpos = pos + ln;
        while (pos < endpos) {
            char c = (char) data[pos];

            if (c == '\u0001') {
                // next character is a volume letter
                pos++;
                c = (char) data[pos];
                buf.append(c);
                buf.append(":\\\\");
            } else if (c == '\u0002') {
                // file is on the same volume
                buf.append('\\');
            } else if (c == '\u0003') {
                // down directory
                buf.append('\\');
            } else if (c == '\u0004') {
                // up directory
                buf.append("..\\");
            } else {
                // just add on the character
                buf.append(c);
            }

            pos++;
        }

        return buf.toString();
    }

    /**
     * Gets the encoded string from the data array
     *
     * @param data the data
     * @param ln   length of the string
     * @param pos  the position in the array
     * @return the string
     */
    private String getUnicodeEncodedFilename(byte[] data, int ln, int pos) {
        StringBuffer buf = new StringBuffer();
        int endpos = pos + ln * 2;
        while (pos < endpos) {
            char c = (char) IntegerHelper.getInt(data[pos], data[pos + 1]);

            if (c == '\u0001') {
                // next character is a volume letter
                pos += 2;
                c = (char) IntegerHelper.getInt(data[pos], data[pos + 1]);
                buf.append(c);
                buf.append(":\\\\");
            } else if (c == '\u0002') {
                // file is on the same volume
                buf.append('\\');
            } else if (c == '\u0003') {
                // down directory
                buf.append('\\');
            } else if (c == '\u0004') {
                // up directory
                buf.append("..\\");
            } else {
                // just add on the character
                buf.append(c);
            }

            pos += 2;
        }

        return buf.toString();
    }

    /**
     * The type of supbook this refers to
     */
    private static class Type {
    }
}
