/*
 * $Id: GlyfCompound.java,v 1.3 2009-02-12 13:53:57 tomoke Exp $
 *
 * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
 * Santa Clara, California 95054, U.S.A. All rights reserved.
 *
 * 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

package com.sun.pdfview.font.ttf;

import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;

/**
 * A single simple glyph in a pdf font.
 */
public class GlyfCompound extends Glyf {
    /** flags */
    private static final int ARG_1_AND_2_ARE_WORDS    = 0x1;
    private static final int ARGS_ARE_XY_VALUES       = 0x2;
    private static final int ROUND_XY_TO_GRID         = 0x4;
    private static final int WE_HAVE_A_SCALE          = 0x8;
    private static final int MORE_COMPONENTS          = 0x20;
    private static final int WE_HAVE_AN_X_AND_Y_SCALE = 0x40; 
    private static final int WE_HAVE_A_TWO_BY_TWO     = 0x80;
    private static final int WE_HAVE_INSTRUCTIONS     = 0x100;
    private static final int USE_MY_METRICS 	      = 0x200;
    private static final int OVERLAP_COMPOUND         = 0x400;

    /** the flags for each compound glyph */
    private GlyfComponent[] components;
    
    /** the instructions for the compound as a whole */
    private byte[] instructions;
    
    /**
     * Creates a new instance of a simple glyf
     */
    protected GlyfCompound() {
    }
    
    /**
     * Set the data for this glyf.
     */
    @Override public void setData(ByteBuffer data) {
        // int pos = data.position();
        // byte[] prdata = new byte[data.remaining()];
        // data.get(prdata);
        // HexDump.printData(prdata);
        // data.position(pos);
              
        // read the contour end points
        List<GlyfComponent> comps = new ArrayList<GlyfComponent>();
        GlyfComponent cur = null;
        boolean hasInstructions = false;
        
        do {
            cur = new GlyfComponent();
            cur.flags = data.getShort();
            cur.glyphIndex = data.getShort();
          
            // read either e/f or matching points, as shorts or bytes...
            if (((cur.flags & ARG_1_AND_2_ARE_WORDS) != 0) &&
                ((cur.flags & ARGS_ARE_XY_VALUES) != 0)) {
                cur.e = data.getShort();
                cur.f = data.getShort();
            } else if (!((cur.flags & ARG_1_AND_2_ARE_WORDS) != 0) &&
                        ((cur.flags & ARGS_ARE_XY_VALUES) != 0)) {
                cur.e = (float) data.get();
                cur.f = (float) data.get();
            } else if ( ((cur.flags & ARG_1_AND_2_ARE_WORDS) != 0) &&
                       !((cur.flags & ARGS_ARE_XY_VALUES) != 0)) {
                cur.compoundPoint = data.getShort();
                cur.componentPoint = data.getShort();
            } else {
                cur.compoundPoint = data.get();
                cur.componentPoint = data.get();
            }
         
            // read the linear transform
            if ((cur.flags & WE_HAVE_A_SCALE) != 0) {
                cur.a = (float) data.getShort() / (float) (1 << 14);
                cur.d = cur.a;
            } else if ((cur.flags & WE_HAVE_AN_X_AND_Y_SCALE) != 0) {
                cur.a = (float) data.getShort() / (float) (1 << 14);
                cur.d = (float) data.getShort() / (float) (1 << 14);
            } else if ((cur.flags & WE_HAVE_A_TWO_BY_TWO) != 0) {
                cur.a = (float) data.getShort() / (float) (1 << 14);
                cur.b = (float) data.getShort() / (float) (1 << 14);
                cur.c = (float) data.getShort() / (float) (1 << 14);
                cur.d = (float) data.getShort() / (float) (1 << 14);
            }
        
            if ((cur.flags & WE_HAVE_INSTRUCTIONS) != 0) {
  	        hasInstructions = true;
            }

            comps.add(cur);
        } while ((cur.flags & MORE_COMPONENTS) != 0);

        GlyfComponent[] componentArray = new GlyfComponent[comps.size()];
        comps.toArray(componentArray);
        setComponents(componentArray);
        
        byte[] instr = null;
        if (hasInstructions) {
            // read the instructions
            short numInstructions = data.getShort();
            instr = new byte[numInstructions];
            for (int i = 0; i < instr.length; i++) {
                instr[i] = data.get();
            }
        } else {
            instr = new byte[0];
        }
        setInstructions(instr);
    }
    
    /**
     * Get the data in this glyf as a byte buffer.  Not implemented.
     */
    @Override public ByteBuffer getData() {
        ByteBuffer buf = super.getData();
        
        // don't flip the buffer, since it may be used by subclasses
        return buf;
    }
    
    /**
     * Get the length of this glyf.  Not implemented.
     */
    @Override public short getLength() {
        
        // start with the length of the superclass
        short length = super.getLength();
        return length;
    }
    
    /**
     * Get the number of components in this compound
     */
    public int getNumComponents() {
        return components.length;
    }
    
    /**
     * Get a given flag
     */
    public short getFlag(int index) {
        return components[index].flags;
    }
    
    /**
     * Get the glyf index for a given glyf
     */
    public short getGlyphIndex(int index) {
        return components[index].glyphIndex;
    }
    
    /**
     * Get the base affine transform.  This is based on a whacy formula
     * defined in the true type font spec.
     */
    public double[] getTransform(int index) {
        GlyfComponent gc = components[index];

        float m = (float) Math.max(Math.abs(gc.a), Math.abs(gc.b));
        if (Math.abs(Math.abs(gc.a) - Math.abs(gc.c)) < (33 / 65536)) {
            m *= 2;
        }

	float n = (float) Math.max(Math.abs(gc.c), Math.abs(gc.d));
        if (Math.abs(Math.abs(gc.c) - Math.abs(gc.d)) < (33 / 65536)) {
            n *= 2;
        }
        
        float e = m * gc.e;
        float f = n * gc.f;
        
        return new double[] { gc.a, gc.b, gc.c, gc.d, e, f }; 
    }
  
    /**
     * Get the point in the compound glyph to match
     */
    public int getCompoundPoint(int index) {
        return components[index].compoundPoint;
    }
    
    /**
     * Get the point in the component glyph to match
     */
    public int getComponentPoint(int index) {
        return components[index].componentPoint;
    }
 
    /**
     * Determine whether args 1 and 2 are words or bytes
     */
    public boolean argsAreWords(int index) {
        return ((getFlag(index) & ARG_1_AND_2_ARE_WORDS) != 0);
    }
    
    /**
     * Determine whether args 1 and 2 are xy values or point indices
     */
    public boolean argsAreXYValues(int index) {
        return ((getFlag(index) & ARGS_ARE_XY_VALUES) != 0);
    }
    
    /**
     * Determine whether to round XY values to the grid
     */
    public boolean roundXYToGrid(int index) {
        return ((getFlag(index) & ROUND_XY_TO_GRID) != 0);
    }
    
    /**
     * Determine whether there is a simple scale
     */
    public boolean hasAScale(int index) {
        return ((getFlag(index) & WE_HAVE_A_SCALE) != 0);
    }
    
    /**
     * Determine whether there are more components left to read
     */
    protected boolean moreComponents(int index) {
        return ((getFlag(index) & MORE_COMPONENTS) != 0);
    }
    
    /**
     * Determine whether there are separate scales on X and Y
     */
    protected boolean hasXYScale(int index) {
        return ((getFlag(index) & WE_HAVE_AN_X_AND_Y_SCALE) != 0);
    }
    
    /**
     * Determine whether there is a 2x2 transform
     */
    protected boolean hasTwoByTwo(int index) {
        return ((getFlag(index) & WE_HAVE_A_TWO_BY_TWO) != 0);
    }
    
    /**
     * Determine whether there are instructions
     */
    protected boolean hasInstructions(int index) {
        return ((getFlag(index) & WE_HAVE_INSTRUCTIONS) != 0);
    }
    
    /**
     * Use the metrics of this component for the compound
     */
    public boolean useMetrics(int index) {
        return ((getFlag(index) & USE_MY_METRICS) != 0);
    }
    
    /**
     * This component overlaps the existing compound
     */
    public boolean overlapCompound(int index) {
        return ((getFlag(index) & OVERLAP_COMPOUND) != 0);
    }
    
    /**
     * Set the components
     */
    void setComponents(GlyfComponent[] components) {
        this.components = components;
    }
    
   /**
    * Get the number of instructions
    */
    public short getNumInstructions() {
        return (short) instructions.length;
    }
    
    /**
     * Get a given instruction
     */
    public byte getInstruction(int index) {
        return instructions[index];
    }
    
    /**
     * Set the instructions
     */
    protected void setInstructions(byte[] instructions) {
        this.instructions = instructions;
    }
    
    /**
     * The record for a single component of this compound glyph
     */
    class GlyfComponent {
        /** flags */
        short flags;
        
        /** the index of the component glyf */
        short glyphIndex;
        
        /** the points to match */
        int compoundPoint;
        int componentPoint;
        
        /** affine transform of this component */
        float a = 1.0f;
        float b = 0.0f;
        float c = 0.0f;
        float d = 1.0f;
        float e = 0.0f;
        float f = 0.0f;
    }
}
