/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.fontbox.ttf;

import java.io.IOException;

/**
 * This class is based on code from Apache Batik, a subproject of Apache XMLGraphics. See
 * <a href="http://xmlgraphics.apache.org/batik/">The Apache™ Batik Project</a> for further details.
 */
public class GlyfCompositeComp
{

    // Flags for composite glyphs.

    /**
     * If set, the arguments are words; otherwise, they are bytes.
     */
    protected static final short ARG_1_AND_2_ARE_WORDS = 0x0001;
    /**
     * If set, the arguments are xy values; otherwise they are points.
     */
    protected static final short ARGS_ARE_XY_VALUES = 0x0002;
    /**
     * If set, xy values are rounded to those of the closest grid lines.
     */
    protected static final short ROUND_XY_TO_GRID = 0x0004;
    /**
     * If set, there is a simple scale; otherwise, scale = 1.0.
     */
    protected static final short WE_HAVE_A_SCALE = 0x0008;
    /**
     * Indicates at least one more glyph after this one.
     */
    protected static final short MORE_COMPONENTS = 0x0020;
    /**
     * The x direction will use a different scale from the y direction.
     */
    protected static final short WE_HAVE_AN_X_AND_Y_SCALE = 0x0040;
    /**
     * There is a 2 by2 transformation that will be used to scale the component.
     */
    protected static final short WE_HAVE_A_TWO_BY_TWO = 0x0080;
    /**
     * Following the last component are instructions for the composite character.
     */
    protected static final short WE_HAVE_INSTRUCTIONS = 0x0100;
    /**
     * If set, this forces the aw and lsb (and rsb) for the composite to be equal to those from this original glyph.
     */
    protected static final short USE_MY_METRICS = 0x0200;

    private int firstIndex;
    private int firstContour;
    private final short argument1;
    private final short argument2;
    private final short flags;
    private final int glyphIndex;
    private double xscale = 1.0;
    private double yscale = 1.0;
    private double scale01 = 0.0;
    private double scale10 = 0.0;
    private int xtranslate = 0;
    private int ytranslate = 0;
    private int point1 = 0;
    private int point2 = 0;

    /**
     * Constructor.
     * 
     * @param bais the stream to be read
     * @throws IOException is thrown if something went wrong
     */
    GlyfCompositeComp(TTFDataStream bais) throws IOException
    {
        flags = bais.readSignedShort();
        glyphIndex = bais.readUnsignedShort();// number of glyph in a font is uint16

        // Get the arguments as just their raw values
        if ((flags & ARG_1_AND_2_ARE_WORDS) != 0)
        {
            // If this is set, the arguments are 16-bit (uint16 or int16)
            argument1 = bais.readSignedShort();
            argument2 = bais.readSignedShort();
        }
        else
        {
            // otherwise, they are bytes (uint8 or int8).
            argument1 = (short) bais.readSignedByte();
            argument2 = (short) bais.readSignedByte();
        }

        // Assign the arguments according to the flags
        if ((flags & ARGS_ARE_XY_VALUES) != 0)
        {
            // If this is set, the arguments are signed xy values
            xtranslate = argument1;
            ytranslate = argument2;
        }
        else
        {
            // otherwise, they are unsigned point numbers.
            //TODO why unused?
            // https://docs.microsoft.com/en-us/typography/opentype/spec/glyf
            // "In the latter case, the first point number indicates the point that is to be matched
            // to the new glyph. The second number indicates the new glyph’s “matched” point.
            // Once a glyph is added, its point numbers begin directly after the last glyphs
            // (endpoint of first glyph + 1).
            point1 = argument1;
            point2 = argument2;
        }

        // Get the scale values (if any)
        if ((flags & WE_HAVE_A_SCALE) != 0)
        {
            int i = bais.readSignedShort();
            xscale = yscale = i / (double) 0x4000;
        }
        else if ((flags & WE_HAVE_AN_X_AND_Y_SCALE) != 0)
        {
            short i = bais.readSignedShort();
            xscale = i / (double) 0x4000;
            i = bais.readSignedShort();
            yscale = i / (double) 0x4000;
        }
        else if ((flags & WE_HAVE_A_TWO_BY_TWO) != 0)
        {
            int i = bais.readSignedShort();
            xscale = i / (double) 0x4000;
            i = bais.readSignedShort();
            scale01 = i / (double) 0x4000;
            i = bais.readSignedShort();
            scale10 = i / (double) 0x4000;
            i = bais.readSignedShort();
            yscale = i / (double) 0x4000;
        }
    }

    /**
     * Sets the first index.
     * 
     * @param idx the first index
     */
    protected void setFirstIndex(int idx)
    {
        firstIndex = idx;
    }

    /**
     * Returns the first index.
     * 
     * @return the first index.
     */
    public int getFirstIndex()
    {
        return firstIndex;
    }

    /**
     * Sets the index for the first contour.
     * 
     * @param idx the index of the first contour
     */
    protected void setFirstContour(int idx)
    {
        firstContour = idx;
    }

    /**
     * Returns the index of the first contour.
     * 
     * @return the index of the first contour.
     */
    public int getFirstContour()
    {
        return firstContour;
    }

    /**
     * Returns argument 1.
     * 
     * @return argument 1.
     */
    public short getArgument1()
    {
        return argument1;
    }

    /**
     * Returns argument 2.
     * 
     * @return argument 2.
     */
    public short getArgument2()
    {
        return argument2;
    }

    /**
     * Returns the flags of the glyph.
     * 
     * @return the flags.
     */
    public short getFlags()
    {
        return flags;
    }

    /**
     * Returns the index of the first contour.
     * 
     * @return index of the first contour.
     */
    public int getGlyphIndex()
    {
        return glyphIndex;
    }

    /**
     * Returns the scale-01 value.
     * 
     * @return the scale-01 value.
     */
    public double getScale01()
    {
        return scale01;
    }

    /**
     * Returns the scale-10 value.
     * 
     * @return the scale-10 value.
     */
    public double getScale10()
    {
        return scale10;
    }

    /**
     * Returns the x-scaling value.
     * 
     * @return the x-scaling value.
     */
    public double getXScale()
    {
        return xscale;
    }

    /**
     * Returns the y-scaling value.
     * 
     * @return the y-scaling value.
     */
    public double getYScale()
    {
        return yscale;
    }

    /**
     * Returns the x-translation value.
     * 
     * @return the x-translation value.
     */
    public int getXTranslate()
    {
        return xtranslate;
    }

    /**
     * Returns the y-translation value.
     * 
     * @return the y-translation value.
     */
    public int getYTranslate()
    {
        return ytranslate;
    }

    /**
     * Transforms an x-coordinate of a point for this component.
     * 
     * @param x The x-coordinate of the point to transform
     * @param y The y-coordinate of the point to transform
     * @return The transformed x-coordinate
     */
    public int scaleX(int x, int y)
    {
        return Math.round((float) (x * xscale + y * scale10));
    }

    /**
     * Transforms a y-coordinate of a point for this component.
     * 
     * @param x The x-coordinate of the point to transform
     * @param y The y-coordinate of the point to transform
     * @return The transformed y-coordinate
     */
    public int scaleY(int x, int y)
    {
        return Math.round((float) (x * scale01 + y * yscale));
    }
}
