
/* ====================================================================
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 2002 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Apache" and "Apache Software Foundation" and
 *    "Apache POI" must not be used to endorse or promote products
 *    derived from this software without prior written permission. For
 *    written permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache",
 *    "Apache POI", nor may "Apache" appear in their name, without
 *    prior written permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */

package org.apache.poi.hssf.record;

import org.apache.poi.util.LittleEndian;
import org.apache.poi.util.StringUtil;
import org.apache.poi.util.BitField;

/**
 * Title:        Font Record - descrbes a font in the workbook (index = 0-3,5-infinity - skip 4)<P>
 * Description:  An element in the Font Table<P>
 * REFERENCE:  PG 315 Microsoft Excel 97 Developer's Kit (ISBN: 1-57231-498-2)<P>
 * @author Andrew C. Oliver (acoliver at apache dot org)
 * @version 2.0-pre
 */

public class FontRecord
    extends Record
{
    public final static short     sid                 =
        0x31;                                                 // docs are wrong (0x231 Microsoft Support site article Q184647)
    public final static short     SS_NONE             = 0;
    public final static short     SS_SUPER            = 1;
    public final static short     SS_SUB              = 2;
    public final static byte      U_NONE              = 0;
    public final static byte      U_SINGLE            = 1;
    public final static byte      U_DOUBLE            = 2;
    public final static byte      U_SINGLE_ACCOUNTING = 0x21;
    public final static byte      U_DOUBLE_ACCOUNTING = 0x22;
    private short                 field_1_font_height;        // in units of .05 of a point
    private short                 field_2_attributes;

    // 0 0x01 - Reserved bit must be 0
    static final private BitField italic     =
        new BitField(0x02);                                   // is this font in italics

    // 2 0x04 - reserved bit must be 0
    static final private BitField strikeout  =
        new BitField(0x08);                                   // is this font has a line through the center
    static final private BitField macoutline = new BitField(
        0x10);                                                // some weird macintosh thing....but who understands those mac people anyhow
    static final private BitField macshadow  = new BitField(
        0x20);                                                // some weird macintosh thing....but who understands those mac people anyhow

    // 7-6 - reserved bits must be 0
    // the rest is unused
    private short                 field_3_color_palette_index;
    private short                 field_4_bold_weight;
    private short                 field_5_super_sub_script;   // 00none/01super/02sub
    private byte                  field_6_underline;          // 00none/01single/02double/21singleaccounting/22doubleaccounting
    private byte                  field_7_family;             // ?? defined by windows api logfont structure?
    private byte                  field_8_charset;            // ?? defined by windows api logfont structure?
    private byte                  field_9_zero = 0;           // must be 0
    private byte                  field_10_font_name_len;     // length of the font name
    private String                field_11_font_name;         // whoa...the font name

    public FontRecord()
    {
    }

    /**
     * Constructs a Font record and sets its fields appropriately.
     *
     * @param id     id must be 0x31 (NOT 0x231 see MSKB #Q184647 for an "explanation of
     * this bug in the documentation) or an exception will be throw upon validation
     * @param size  the size of the data area of the record
     * @param data  data of the record (should not contain sid/len)
     */

    public FontRecord(short id, short size, byte [] data)
    {
        super(id, size, data);
    }

    /**
     * Constructs a Font record and sets its fields appropriately.
     *
     * @param id     id must be 0x31 (NOT 0x231 see MSKB #Q184647 for an "explanation of
     * this bug in the documentation) or an exception will be throw upon validation
     * @param size  the size of the data area of the record
     * @param data  data of the record (should not contain sid/len)
     * @param offset of the record's data
     */

    public FontRecord(short id, short size, byte [] data, int offset)
    {
        super(id, size, data, offset);
    }

    protected void validateSid(short id)
    {
        if (id != sid)
        {
            throw new RecordFormatException("NOT A FONT RECORD");
        }
    }

    protected void fillFields(byte [] data, short size, int offset)
    {
        field_1_font_height         = LittleEndian.getShort(data, 0 + offset);
        field_2_attributes          = LittleEndian.getShort(data, 2 + offset);
        field_3_color_palette_index = LittleEndian.getShort(data, 4 + offset);
        field_4_bold_weight         = LittleEndian.getShort(data, 6 + offset);
        field_5_super_sub_script    = LittleEndian.getShort(data, 8 + offset);
        field_6_underline           = data[ 10 + offset ];
        field_7_family              = data[ 11 + offset ];
        field_8_charset             = data[ 12 + offset ];
        field_9_zero                = data[ 13 + offset ];
        field_10_font_name_len      = data[ 14 + offset ];
        if (field_10_font_name_len > 0)
        {
            if (data[ 15 ] == 0)
            {   // is compressed unicode
                field_11_font_name = new String(data, 16,
                                                LittleEndian.ubyteToInt(field_10_font_name_len));
            }
            else
            {   // is not compressed unicode
                field_11_font_name = StringUtil.getFromUnicode(data, 15,
                        field_10_font_name_len);
            }
        }
    }

    /**
     * sets the height of the font in 1/20th point units
     *
     * @param height  fontheight (in points/20)
     */

    public void setFontHeight(short height)
    {
        field_1_font_height = height;
    }

    /**
     * set the font attributes (see individual bit setters that reference this method)
     *
     * @param attributes    the bitmask to set
     */

    public void setAttributes(short attributes)
    {
        field_2_attributes = attributes;
    }

    // attributes bitfields

    /**
     * set the font to be italics or not
     *
     * @param italics - whether the font is italics or not
     * @see #setAttributes(short)
     */

    public void setItalic(boolean italics)
    {
        field_2_attributes = italic.setShortBoolean(field_2_attributes, italics);
    }

    /**
     * set the font to be stricken out or not
     *
     * @param strike - whether the font is stricken out or not
     * @see #setAttributes(short)
     */

    public void setStrikeout(boolean strike)
    {
        field_2_attributes = strikeout.setShortBoolean(field_2_attributes, strike);
    }

    /**
     * whether to use the mac outline font style thing (mac only) - Some mac person
     * should comment this instead of me doing it (since I have no idea)
     *
     * @param mac - whether to do that mac font outline thing or not
     * @see #setAttributes(short)
     */

    public void setMacoutline(boolean mac)
    {
        field_2_attributes = macoutline.setShortBoolean(field_2_attributes, mac);
    }

    /**
     * whether to use the mac shado font style thing (mac only) - Some mac person
     * should comment this instead of me doing it (since I have no idea)
     *
     * @param mac - whether to do that mac font shadow thing or not
     * @see #setAttributes(short)
     */

    public void setMacshadow(boolean mac)
    {
        field_2_attributes = macshadow.setShortBoolean(field_2_attributes, mac);
    }

    /**
     * set the font's color palette index
     *
     * @param cpi - font color index
     */

    public void setColorPaletteIndex(short cpi)
    {
        field_3_color_palette_index = cpi;
    }

    /**
     * set the bold weight for this font (100-1000dec or 0x64-0x3e8).  Default is
     * 0x190 for normal and 0x2bc for bold
     *
     * @param bw - a number between 100-1000 for the fonts "boldness"
     */

    public void setBoldWeight(short bw)
    {
        field_4_bold_weight = bw;
    }

    /**
     * set the type of super or subscript for the font
     *
     * @param sss  super or subscript option
     * @see #SS_NONE
     * @see #SS_SUPER
     * @see #SS_SUB
     */

    public void setSuperSubScript(short sss)
    {
        field_5_super_sub_script = sss;
    }

    /**
     * set the type of underlining for the font
     *
     * @param u  super or subscript option
     *
     * @see #U_NONE
     * @see #U_SINGLE
     * @see #U_DOUBLE
     * @see #U_SINGLE_ACCOUNTING
     * @see #U_DOUBLE_ACCOUNTING
     */

    public void setUnderline(byte u)
    {
        field_6_underline = u;
    }

    /**
     * set the font family (TODO)
     *
     * @param f family
     */

    public void setFamily(byte f)
    {
        field_7_family = f;
    }

    /**
     * set the character set
     *
     * @param charset - characterset
     */

    public void setCharset(byte charset)
    {
        field_8_charset = charset;
    }

    /**
     * set the length of the fontname string
     *
     * @param len  length of the font name
     * @see #setFontName(String)
     */

    public void setFontNameLength(byte len)
    {
        field_10_font_name_len = len;
    }

    /**
     * set the name of the font
     *
     * @param fn - name of the font (i.e. "Arial")
     */

    public void setFontName(String fn)
    {
        field_11_font_name = fn;
    }

    /**
     * gets the height of the font in 1/20th point units
     *
     * @return fontheight (in points/20)
     */

    public short getFontHeight()
    {
        return field_1_font_height;
    }

    /**
     * get the font attributes (see individual bit getters that reference this method)
     *
     * @return attribute - the bitmask
     */

    public short getAttributes()
    {
        return field_2_attributes;
    }

    /**
     * get whether the font is to be italics or not
     *
     * @return italics - whether the font is italics or not
     * @see #getAttributes()
     */

    public boolean isItalic()
    {
        return italic.isSet(field_2_attributes);
    }

    /**
     * get whether the font is to be stricken out or not
     *
     * @return strike - whether the font is stricken out or not
     * @see #getAttributes()
     */

    public boolean isStruckout()
    {
        return strikeout.isSet(field_2_attributes);
    }

    /**
     * whether to use the mac outline font style thing (mac only) - Some mac person
     * should comment this instead of me doing it (since I have no idea)
     *
     * @return mac - whether to do that mac font outline thing or not
     * @see #getAttributes()
     */

    public boolean isMacoutlined()
    {
        return macoutline.isSet(field_2_attributes);
    }

    /**
     * whether to use the mac shado font style thing (mac only) - Some mac person
     * should comment this instead of me doing it (since I have no idea)
     *
     * @return mac - whether to do that mac font shadow thing or not
     * @see #getAttributes()
     */

    public boolean isMacshadowed()
    {
        return macshadow.isSet(field_2_attributes);
    }

    /**
     * get the font's color palette index
     *
     * @return cpi - font color index
     */

    public short getColorPaletteIndex()
    {
        return field_3_color_palette_index;
    }

    /**
     * get the bold weight for this font (100-1000dec or 0x64-0x3e8).  Default is
     * 0x190 for normal and 0x2bc for bold
     *
     * @return bw - a number between 100-1000 for the fonts "boldness"
     */

    public short getBoldWeight()
    {
        return field_4_bold_weight;
    }

    /**
     * get the type of super or subscript for the font
     *
     * @return super or subscript option
     * @see #SS_NONE
     * @see #SS_SUPER
     * @see #SS_SUB
     */

    public short getSuperSubScript()
    {
        return field_5_super_sub_script;
    }

    /**
     * get the type of underlining for the font
     *
     * @return super or subscript option
     *
     * @see #U_NONE
     * @see #U_SINGLE
     * @see #U_DOUBLE
     * @see #U_SINGLE_ACCOUNTING
     * @see #U_DOUBLE_ACCOUNTING
     */

    public byte getUnderline()
    {
        return field_6_underline;
    }

    /**
     * get the font family (TODO)
     *
     * @return family
     */

    public byte getFamily()
    {
        return field_7_family;
    }

    /**
     * get the character set
     *
     * @return charset - characterset
     */

    public byte getCharset()
    {
        return field_8_charset;
    }

    /**
     * get the length of the fontname string
     *
     * @return length of the font name
     * @see #getFontName()
     */

    public byte getFontNameLength()
    {
        return field_10_font_name_len;
    }

    /**
     * get the name of the font
     *
     * @return fn - name of the font (i.e. "Arial")
     */

    public String getFontName()
    {
        return field_11_font_name;
    }

    public String toString()
    {
        StringBuffer buffer = new StringBuffer();

        buffer.append("[FONT]\n");
        buffer.append("    .fontheight      = ")
            .append(Integer.toHexString(getFontHeight())).append("\n");
        buffer.append("    .attributes      = ")
            .append(Integer.toHexString(getAttributes())).append("\n");
        buffer.append("         .italic     = ").append(isItalic())
            .append("\n");
        buffer.append("         .strikout   = ").append(isStruckout())
            .append("\n");
        buffer.append("         .macoutlined= ").append(isMacoutlined())
            .append("\n");
        buffer.append("         .macshadowed= ").append(isMacshadowed())
            .append("\n");
        buffer.append("    .colorpalette    = ")
            .append(Integer.toHexString(getColorPaletteIndex())).append("\n");
        buffer.append("    .boldweight      = ")
            .append(Integer.toHexString(getBoldWeight())).append("\n");
        buffer.append("    .supersubscript  = ")
            .append(Integer.toHexString(getSuperSubScript())).append("\n");
        buffer.append("    .underline       = ")
            .append(Integer.toHexString(getUnderline())).append("\n");
        buffer.append("    .family          = ")
            .append(Integer.toHexString(getFamily())).append("\n");
        buffer.append("    .charset         = ")
            .append(Integer.toHexString(getCharset())).append("\n");
        buffer.append("    .namelength      = ")
            .append(Integer.toHexString(getFontNameLength())).append("\n");
        buffer.append("    .fontname        = ").append(getFontName())
            .append("\n");
        buffer.append("[/FONT]\n");
        return buffer.toString();
    }

    public int serialize(int offset, byte [] data)
    {
        int realflen = getFontNameLength() * 2;

        LittleEndian.putShort(data, 0 + offset, sid);
        LittleEndian.putShort(
            data, 2 + offset,
            ( short ) (15 + realflen
                       + 1));   // 19 - 4 (sid/len) + font name length = datasize

        // undocumented single byte (1)
        LittleEndian.putShort(data, 4 + offset, getFontHeight());
        LittleEndian.putShort(data, 6 + offset, getAttributes());
        LittleEndian.putShort(data, 8 + offset, getColorPaletteIndex());
        LittleEndian.putShort(data, 10 + offset, getBoldWeight());
        LittleEndian.putShort(data, 12 + offset, getSuperSubScript());
        data[ 14 + offset ] = getUnderline();
        data[ 15 + offset ] = getFamily();
        data[ 16 + offset ] = getCharset();
        data[ 17 + offset ] = (( byte ) 0);
        data[ 18 + offset ] = getFontNameLength();
        data[ 19 + offset ] = ( byte ) 1;
        StringUtil.putUncompressedUnicode(getFontName(), data, 20 + offset);
        return getRecordSize();
    }

    public int getRecordSize()
    {
        return (getFontNameLength() * 2) + 20;
    }

    public short getSid()
    {
        return this.sid;
    }
}
