/*
 * Typecast - The Font Development Environment
 *
 * Copyright (c) 2004 David Schweinsberg
 *
 * Licensed 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 jogamp.graph.font.typecast.tt.engine;

import jogamp.graph.font.typecast.ot.Mnemonic;
import jogamp.graph.font.typecast.ot.Point;

/**
 * The interpreter shall remain ignorant of the table structure - the table
 * data will be extracted by supporting classes, whether it be the Parser
 * or some other.
 * @author <a href="mailto:david.schweinsberg@gmail.com">David Schweinsberg</a>
 */
class Interpreter {

    private Parser parser = null;
    private final GraphicsState gs = new GraphicsState();
    private final Point[][] zone = new Point[2][];
    private int[] stack;
    private int[] store;
    private final int[] cvt = new int[256];
    private int[] functionMap;
    private int stackIndex = 0;
    private boolean inFuncDef = false;

    public Interpreter(int stackMax, int storeMax, int funcMax) {
        zone[0] = new Point[256];
        zone[1] = new Point[256];
        stack = new int[stackMax];
        store = new int[storeMax];
        functionMap = new int[funcMax];
    }

    /**
     * ABSolute value
     */
    private void _abs() {
        int n = pop();
        if (n >= 0) {
            push(n);
        } else {
            push(-n);
        }
    }

    /**
     * ADD
     */
    private void _add() {
        int n1 = pop();
        int n2 = pop();
        push(n2 + n1);
    }

    private void _alignpts() {
        pop();
        pop();
    }

    /**
     *
     *
     * USES: loop
     */
    private void _alignrp() {
        while (gs.loop-- > 0) {
            pop();
        }
        gs.loop = 1;
    }

    /**
     * logical AND
     */
    private void _and() {
        int e2 = pop();
        int e1 = pop();
        push(((e1 != 0) && (e2 != 0)) ? 1 : 0);
    }

    /**
     * CALL function
     */
    private void _call() {
        execute(functionMap[pop()]);
    }

    /**
     * CEILING
     */
    private void _ceiling() {
        int n = pop();
        if (n >= 0) {
            push((n & 0xffc0) + (((n & 0x3f) != 0) ? 0x40 : 0));
        } else {
            push(n & 0xffc0);
        }
    }

    /**
     * Copy the INDEXed element to the top of the stack
     */
    private void _cindex() {
        push(stack[stackIndex - pop()]);
    }

    /**
     * CLEAR the entire stack
     */
    private void _clear() {
        stackIndex = 0;
    }

    private void _debug() {
        pop();
    }

    /**
     * DELTA exception C1
     */
    private void _deltac1() {
        int n = pop();
        for (int i = 0; i < n; i++) {
            pop();	// pn
            pop();	// argn
        }
    }

    /**
     * DELTA exception C2
     */
    private void _deltac2() {
        int n = pop();
        for (int i = 0; i < n; i++) {
            pop();	// pn
            pop();	// argn
        }
    }

    /**
     * DELTA exception C3
     */
    private void _deltac3() {
        int n = pop();
        for (int i = 0; i < n; i++) {
            pop();	// pn
            pop();	// argn
        }
    }

    /**
     * DELTA exception P1
     */
    private void _deltap1() {
        int n = pop();
        for (int i = 0; i < n; i++) {
            pop();	// pn
            pop();	// argn
        }
    }

    /**
     * DELTA exception P2
     */
    private void _deltap2() {
        int n = pop();
        for (int i = 0; i < n; i++) {
            pop();	// pn
            pop();	// argn
        }
    }

    /**
     * DELTA exception P3
     */
    private void _deltap3() {
        int n = pop();
        for (int i = 0; i < n; i++) {
            pop();	// pn
            pop();	// argn
        }
    }

    /**
     * Returns the DEPTH of the stack
     */
    private void _depth() {
        push(stackIndex);
    }

    /**
     * DIVide
     */
    private void _div() {
        int n1 = pop();
        int n2 = pop();
        push((n2 / n1) >> 6);
    }

    /**
     * DUPlicate top stack element
     */
    private void _dup() {
        int n = pop();
        push(n);
        push(n);
    }

    /**
     * ELSE
     */
    private int _else(int instructionIndex) {
        return parser.handleElse(instructionIndex);
    }

    /**
     * EQual
     */
    private void _eq() {
        int e2 = pop();
        int e1 = pop();
        push((e1 == e2) ? 1 : 0);
    }

    private void _even() {
        pop();
        push(0);
    }

    /**
     * Function DEFinition
     */
    private void _fdef(int instructionIndex) {
        functionMap[pop()] = instructionIndex;
        inFuncDef = true;
    }

    /**
     * Set the auto_FLIP boolean to OFF
     */
    private void _flipoff() {
        gs.auto_flip = false;
    }

    /**
     * Set the auto_FLIP boolean to ON
     */
    private void _flipon() {
        gs.auto_flip = true;
    }

    /**
     * FLIP PoinT
     *
     * USES: loop
     */
    private void _flippt() {
        while(gs.loop-- > 0) {
            int index = pop();
            zone[gs.zp0][index].onCurve = !zone[gs.zp0][index].onCurve;
        }
        gs.loop = 1;
    }

    /**
     * FLIP RanGe OFF
     */
    private void _fliprgoff() {
        int end = pop();
        int start = pop();
        for (int i = start; i <= end; i++) {
            zone[1][i].onCurve = false;
        }
    }

    /**
     * FLIP RanGe ON
     */
    private void _fliprgon() {
        int end = pop();
        int start = pop();
        for (int i = start; i <= end; i++) {
            zone[1][i].onCurve = true;
        }
    }

    /**
     * FLOOR
     */
    private void _floor() {
        int n = pop();
        if (n >= 0) {
            push(n & 0xffc0);
        } else {
            push((n & 0xffc0) - (((n & 0x3f) != 0) ? 0x40 : 0));
        }
    }

    private void _gc(short param) {
        pop();
        push(0);
    }

    private void _getinfo() {
        pop();
        push(0);
    }

    /**
     * Get Freedom_Vector
     */
    private void _gfv() {
        push(gs.freedom_vector[0]);
        push(gs.freedom_vector[1]);
    }

    /**
     * Get Projection_Vector
     */
    private void _gpv() {
        push(gs.projection_vector[0]);
        push(gs.projection_vector[1]);
    }

    /**
     * Greater Than
     */
    private void _gt() {
        int e2 = pop();
        int e1 = pop();
        push((e1 > e2) ? 1 : 0);
    }

    /**
     * Greater Than or EQual
     */
    private void _gteq() {
        int e2 = pop();
        int e1 = pop();
        push((e1 >= e2) ? 1 : 0);
    }

    /**
     * Instruction DEFinition
     */
    private void _idef() {
        pop();
        inFuncDef = true;
    }

    /**
     * IF test
     */
    private int _if(int instructionIndex) {
        return parser.handleIf(pop() != 0, instructionIndex);
    }

    /**
     * INSTruction Execution ConTRol
     *
     * INSTCTRL[]
     *
     * Code Range
     * 0x8E
     *
     * Pops
     * s: selector flag (int32)
     * value: USHORT (padded to 32 bits) used to set value of instruction_control.
     *
     * Pushes
     * -
     *
     * Sets
     * instruction_control
     *
     * Sets the instruction control state variable making it possible to turn on or off
     * the execution of instructions and to regulate use of parameters set in the CVT
     * program. INSTCTRL[ ] can only be executed in the CVT program.
     *
     * This instruction clears and sets various control flags in the rasterizer. The
     * selector flag determines valid values for the value argument. The value determines
     * the new setting of the raterizer control flag. In version 1.0 there are only two
     * flags in use:
     *
     * Selector flag 1 is used to inhibit grid-fitting. If s=1, valid values for the
     * value argument are 0 (FALSE) and 1 (TRUE). If the value argument is set to TRUE
     * (v=1), any instructions associated with glyphs will not be executed. For example,
     * to inhibit grid-fitting when a glyph is being rotated or stretched, use the
     * following sequence on the preprogram:
     *
     * PUSHB[000] 6	; ask GETINFO to check for stretching or rotation
     * GETINFO[]		; will push TRUE if glyph is stretched or rotated
     * IF[]				; tests value at top of stack
     * PUSHB[000] 1	; value for INSTCTRL
     * PUSHB[000] 1	; selector for INSTCTRL
     * INSTRCTRL[]		; based on selector and value will turn grid-fitting off
     * EIF[]
     *
     * Selector flag 2 is used to establish that any parameters set in the CVT program
     * should be ignored when instructions associated with glyphs are executed. These
     * include, for example, the values for scantype and the CVT cut-in. If s=1, valid
     * values for the value argument are 0 (FALSE) and 2 (TRUE). If the value argument is
     * set to TRUE (v=2), the default values of those parameters will be used regardless
     * of any changes that may have been made in those values by the preprogram. If the
     * value argument is set to FALSE (v=0), parameter values changed by the CVT program
     * will be used in glyph instructions.
     */
    private void _instctrl() {
        int s = pop();
        int v = pop();
        if (s == 1) {
            gs.instruction_control |= v;
        } else if (s == 2) {
            gs.instruction_control |= v;
        }
    }

    private void _ip() {
        pop();
    }

    private void _isect() {
        pop();
        pop();
        pop();
        pop();
        pop();
    }

    private void _iup(short param) {
    }

    /**
     * JuMP Relative
     */
    private int _jmpr(int instructionIndex) {
        instructionIndex += pop() - 1;
        return instructionIndex;
    }

    /**
     * Jump Relative On False
     */
    private int _jrof(int instructionIndex) {
        boolean test = pop() != 0;
        int offset = pop();
        if (!test) {
            instructionIndex += offset - 1;
        }
        return instructionIndex;
    }

    /**
     * Jump Relative On True
     */
    private int _jrot(int instructionIndex) {
        boolean test = pop() != 0;
        int offset = pop();
        if (test) {
            instructionIndex += offset - 1;
        }
        return instructionIndex;
    }

    /**
     * LOOP and CALL function
     */
    private void _loopcall() {
        int index = pop();
        int count = pop();
        for (int i = 0; i < count; i++) {
            execute(functionMap[i]);
        }
    }

    /**
     * Less Than
     */
    private void _lt() {
        int e2 = pop();
        int e1 = pop();
        push((e1 < e2) ? 1 : 0);
    }

    /**
     * Less Than or EQual
     */
    private void _lteq() {
        int e2 = pop();
        int e1 = pop();
        push((e1 <= e2) ? 1 : 0);
    }

    /**
     * MAXimum of top two stack elements
     */
    private void _max() {
        int n1 = pop();
        int n2 = pop();
        push(Math.max(n1, n2));
    }

    private void _md(short param) {
        pop();
        pop();
        push(0);
    }

    private void _mdap(short param) {
        pop();
    }

    private void _mdrp(short param) {
        pop();
    }

    private void _miap(short param) {
        pop();
        pop();
    }
    /**
     * MINimum of top two stack elements
     */
    private void _min() {
        int n1 = pop();
        int n2 = pop();
        push(Math.min(n1, n2));
    }

    /**
     * Move the INDEXed element to the top of the stack
     */
    private void _mindex() {
        // Move the indexed element to stackIndex, and shift the others down
        int k = pop();
        int e = stack[stackIndex - k];
        for (int i = stackIndex - k; i < stackIndex - 1; i++) {
            stack[i] = stack[i+1];
        }
        stack[stackIndex - 1] = e;
    }

    private void _mirp(short param) {
        pop();
        pop();
    }

    private void _mppem() {
        push(0);
    }

    private void _mps() {
        push(0);
    }
    
    private void _msirp(short param) {
        pop();
        pop();
    }

    /**
     * MULtiply
     */
    private void _mul() {
        int n1 = pop();
        int n2 = pop();
        push((n1 * n2) >> 6);
    }

    /**
     * NEGate
     */
    private void _neg() {
        push(-pop());
    }

    /**
     * Not EQual
     */
    private void _neq() {
        int e2 = pop();
        int e1 = pop();
        push((e1 != e2) ? 1 : 0);
    }

    /**
     * logical NOT
     */
    private void _not() {
        push((pop() != 0) ? 0 : 1);
    }

    private void _nround(short param) {
        pop();
        push(0);
    }

    private void _odd() {
        pop();
        push(0);
    }

    /**
     * logical OR
     */
    private void _or() {
        int e2 = pop();
        int e1 = pop();
        push(((e1 != 0) || (e2 != 0)) ? 1 : 0);
    }

    /**
     * PUSH N Bytes
     * PUSH N Words
     * PUSH Bytes
     * PUSH Words
     */
    private void _push(int[] data) {
        for (int datum : data) {
            push(datum);
        }
    }

    /**
     * Read Control Value Table
     */
    private void _rcvt() {
        push(cvt[pop()]);
    }

    /**
     * Round Down To Grid
     */
    private void _rdtg() {
        gs.round_state = 3;
    }

    /**
     * Round OFF
     */
    private void _roff() {
        gs.round_state = 5;
    }

    /**
     * ROLL the top three stack elements
     */
    private void _roll() {
        int a = pop();
        int b = pop();
        int c = pop();
        push(b);
        push(a);
        push(c);
    }

    private void _round(short param) {
        pop();
        push(0);
    }

    /**
     * Read Store
     */
    private void _rs() {
        push(store[pop()]);
    }

    /**
     * Round To Double Grid
     */
    private void _rtdg() {
        gs.round_state = 2;
    }

    /**
     * Round To Grid
     */
    private void _rtg() {
        gs.round_state = 1;
    }

    /**
     * Round To Half Grid
     */
    private void _rthg() {
        gs.round_state = 0;
    }

    /**
     * Round Up To Grid
     */
    private void _rutg() {
        gs.round_state = 4;
    }

    private void _s45round() {
        pop();
    }

    /**
     * SCAN conversion ConTRoL
     *
     * SCANCTRL[ ]
     *
     * Code Range
     * 0x85
     *
     * Pops
     * n: flags indicating when to turn on dropout control mode (16 bit word padded
     * to 32 bits)
     *
     * Pushes
     * -
     *
     * Sets
     * scan_control
     *
     * SCANCTRL is used to set the value of the Graphics State variable scan_control
     * which in turn determines whether the scan converter will activate dropout
     * control for this glyph. Use of the dropout control mode is determined by three
     * conditions:
     *
     * Is the glyph rotated?
     *
     * Is the glyph stretched?
     *
     * Is the current setting for ppem less than a specified threshold?
     *
     * The interpreter pops a word from the stack and looks at the lower 16 bits.
     *
     * Bits 0-7 represent the threshold value for ppem. A value of FF in bits 0-7
     * means invoke dropout_control for all sizes. A value of 0 in bits 0-7 means
     * never invoke dropout_control.
     *
     * Bits 8-13 are used to turn on dropout_control in cases where the specified
     * conditions are met. Bits 8, 9 and 10 are used to turn on the dropout_control
     * mode (assuming other conditions do not block it). Bits 11, 12, and 13 are
     * used to turn off the dropout mode unless other conditions force it. Bits 14
     * and 15 are reserved for future use.
     *
     * Bit   Meaning if set
     * ---   --------------
     *   8   Set dropout_control to TRUE if other conditions do not block and ppem
     *       is less than or equal to the threshold value.
     *
     *   9   Set dropout_control to TRUE if other conditions do not block and the
     *       glyph is rotated.
     *
     *  10   Set dropout_control to TRUE if other conditions do not block and the
     *       glyph is stretched.
     *
     *  11   Set dropout_control to FALSE unless ppem is less than or equal to the
     *       threshold value.
     *
     *  12   Set dropout_control to FALSE unless the glyph is rotated.
     *
     *  13   Set dropout_control to FALSE unless the glyph is stretched.
     *
     *  14   Reserved for future use.
     *
     *  15   Reserved for future use.
     *
     * For example
     * 0x0000 No dropout control is invoked
     * 0x01FF Always do dropout control
     * 0x0A10 Do dropout control if the glyph is rotated and has less than 16
     *        pixels per-em
     *
     * The scan converter can operate in either a "normal" mode or in a "fix dropout"
     * mode depending on the value of a set of enabling and disabling flags.
     */
    private void _scanctrl() {
        gs.scan_control = pop();
    }

    /**
     * SCANTYPE
     *
     * SCANTYPE[]
     *
     * Code Range
     * 0x8D
     *
     * Pops
     * n: 16 bit integer
     *
     * Pushes
     * -
     *
     * Sets
     * scan_control
     *
     * Pops a 16-bit integer whose value is used to determine which rules the scan
     * converter will use. If the value of the argument is 0, the fast scan converter
     * will be used. If the value of the integer is 1 or 2, simple dropout control will
     * be used. If the value of the integer is 4 or 5, smart dropout control will be
     * used. More specifically,
     *
     *   if n=0 rules 1, 2, and 3 are invoked (simple dropout control scan conversion
     *   including stubs)
     *
     *   if n=1 rules 1, 2, and 4 are invoked (simple dropout control scan conversion
     *   excluding stubs)
     *
     *   if n=2 rules 1 and 2 only are invoked (fast scan conversion; dropout control
     *   turned off)
     *
     *   if n=3 same as n = 2
     *
     *   if n = 4 rules 1, 2, and 5 are invoked (smart dropout control scan conversion
     *   including stubs)
     *
     *   if n = 5 rules 1, 2, and 6 are invoked (smart dropout control scan conversion
     *   excluding stubs)
     *
     *   if n = 6 same as n = 2
     *
     *   if n = 7 same as n = 2
     *
     * The scan conversion rules are shown here:
     *
     * Rule 1
     * If a pixel's center falls within the glyph outline, that pixel is turned on.
     *
     * Rule 2
     * If a contour falls exactly on a pixel's center, that pixel is turned on.
     *
     * Rule 3
     * If a scan line between two adjacent pixel centers (either vertical or
     * horizontal) is intersected by both an on-Transition contour and an off-Transition
     * contour and neither of the pixels was already turned on by rules 1 and 2, turn on
     * the left-most pixel (horizontal scan line) or the bottom-most pixel (vertical scan
     * line). This is "Simple" dropout control.
     *
     * Rule 4
     * Apply Rule 3 only if the two contours continue to intersect other scan lines in
     * both directions. That is, do not turn on pixels for 'stubs.' The scanline segments
     * that form a square with the intersected scan line segment are examined to verify
     * that they are intersected by two contours. It is possible that these could be
     * different contours than the ones intersecting the dropout scan line segment. This
     * is very unlikely but may have to be controlled with grid-fitting in some exotic
     * glyphs.
     *
     * Rule 5
     * If a scan line between two adjacent pixel centers (either vertical or horizontal)
     * is intersected by both an on-Transition contour and an off-Transition contour and
     * neither of the pixels was already turned on by rules 1 and 2, turn on the pixel
     * which is closer to the midpoint between the on-Transition contour and off-
     * Transition contour. This is "Smart" dropout control.
     *
     * Rule 6
     * Apply Rule 5 only if the two contours continue to intersect other scan lines in
     * both directions. That is, do not turn on pixels for 'stubs.'
     *
     * New fonts wishing to use the new modes of the ScanType instruction, but still
     * wishing to work correctly on old rasterizers that don't recognize the new modes
     * should:
     *
     * First execute a ScanType instruction using an old mode which will give the best
     * approximation to the desired new mode (e.g. Simple Stubs for Smart Stubs), and
     * then
     *
     * Immediately execute another ScanType instruction with the desired new mode.
     */
    private void _scantype() {
        pop();
    }

    private void _scfs() {
        pop();
        pop();
    }

    /**
     * Set Control Value Table Cut In
     */
    private void _scvtci() {
        gs.control_value_cut_in = pop();
    }

    /**
     * Set Delta_Base in the graphics state
     */
    private void _sdb() {
        gs.delta_base = pop();
    }

    /**
     * Set Dual Projection_Vector To Line
     */
    private void _sdpvtl(short param) {
        pop();
        pop();
    }

    /**
     * Set Delta_Shift in the graphics state
     */
    private void _sds() {
        gs.delta_shift = pop();
    }

    /**
     * Set Freedom_Vector From Stack
     */
    private void _sfvfs() {
        gs.freedom_vector[1] = pop();	// y
        gs.freedom_vector[0] = pop();	// x
    }

    /*
     * Set Freedom_Vector to Coordinate Axis
     */
    private void _sfvtca(short param) {
        if (param == 1) {
            gs.freedom_vector[0] = 0x4000;
            gs.freedom_vector[1] = 0x0000;
        } else {
            gs.freedom_vector[0] = 0x0000;
            gs.freedom_vector[1] = 0x4000;
        }
    }

    /*
     * Set Freedom_Vector To Line
     */
    private void _sfvtl(short param) {
        pop();
        pop();
        if (param == 1) {
            gs.freedom_vector[0] = 0x0000;
            gs.freedom_vector[1] = 0x0000;
        } else {
            gs.freedom_vector[0] = 0x0000;
            gs.freedom_vector[1] = 0x0000;
        }
    }

    /**
     * Set Freedom_Vector To Projection Vector
     */
    private void _sfvtpv() {
        gs.freedom_vector[0] = gs.projection_vector[0];
        gs.freedom_vector[1] = gs.projection_vector[1];
    }

    private void _shc(short param) {
        pop();
    }

    /**
     * SHift Point by the last point
     *
     * USES: loop
     */
    private void _shp(short param) {
        while(gs.loop-- > 0) {
            pop();
            if(param == 0) {
            } else {
            }
        }
        gs.loop = 1;
    }

    /**
     * SHift Point by a PIXel amount
     *
     * USES: loop
     */
    private void _shpix() {
        pop();	// amount
        while (gs.loop-- > 0) {
            pop();	// p
        }
        gs.loop = 1;
    }

    private void _shz(short param) {
        pop();
    }

    /**
     * Set LOOP variable
     */
    private void _sloop() {
        gs.loop = pop();
    }

    /**
     * Set Minimum_Distance
     */
    private void _smd() {
        gs.minimum_distance = pop();
    }

    /**
     * Set Projection_Vector From Stack
     */
    private void _spvfs() {
        gs.projection_vector[1] = pop();	// y
        gs.projection_vector[0] = pop();	// x
    }

    /*
     * Set Projection_Vector To Coordinate Axis
     */
    private void _spvtca(short param) {
        if (param == 1) {
            gs.projection_vector[0] = 0x4000;
            gs.projection_vector[1] = 0x0000;
        } else {
            gs.projection_vector[0] = 0x0000;
            gs.projection_vector[1] = 0x4000;
        }
    }

    /**
     * Set Projection_Vector To Line
     */
    private void _spvtl(short param) {

        // We'll get a copy of the line and normalize it -
        // divide the x- and y-coords by the vector's dot product.
        Point p1 = zone[gs.zp2][pop()];
        Point p2 = zone[gs.zp1][pop()];
        int x = p2.x - p1.x;
        int y = p2.y - p1.y;
        if(param == 1) {
            gs.projection_vector[0] = 0x0000;
            gs.projection_vector[1] = 0x0000;
        } else {
            gs.projection_vector[0] = 0x0000;
            gs.projection_vector[1] = 0x0000;
        }
    }

    private void _sround() {
        pop();
    }

    /**
     * Set Reference Point 0
     */
    private void _srp0() {
        gs.rp0 = pop();
    }

    /**
     * Set Reference Point 1
     */
    private void _srp1() {
        gs.rp1 = pop();
    }

    /**
     * Set Reference Point 2
     */
    private void _srp2() {
        gs.rp2 = pop();
    }

    /**
     * Set Single-Width
     */
    private void _ssw() {
        gs.single_width_value = pop();
    }

    /**
     * Set Single_Width_Cut_In
     */
    private void _sswci() {
        gs.single_width_cut_in = pop();
    }

    /**
     * SUBtract
     */
    private void _sub() {
        int n1 = pop();
        int n2 = pop();
        push(n2 - n1);
    }

    /**
     * Set freedom and projection Vectors To Coordinate Axis
     */
    private void _svtca(short param) {
        if (param == 1) {
            gs.projection_vector[0] = 0x4000;
            gs.projection_vector[1] = 0x0000;
            gs.freedom_vector[0] = 0x4000;
            gs.freedom_vector[1] = 0x0000;
        } else {
            gs.projection_vector[0] = 0x0000;
            gs.projection_vector[1] = 0x4000;
            gs.freedom_vector[0] = 0x0000;
            gs.freedom_vector[1] = 0x4000;
        }
    }

    /**
     * SWAP the top two elements on the stack
     */
    private void _swap() {
        int n1 = pop();
        int n2 = pop();
        push(n1);
        push(n2);
    }

    /**
     * Set Zone Pointer 0
     */
    private void _szp0() {
        gs.zp0 = pop();
    }

    /**
     * Set Zone Pointer 1
     */
    private void _szp1() {
        gs.zp1 = pop();
    }

    /**
     * Set Zone Pointer 2
     */
    private void _szp2() {
        gs.zp2 = pop();
    }

    /**
     * Set Zone PointerS
     */
    private void _szps() {
        gs.zp0 = gs.zp1 = gs.zp2 = pop();
    }

    private void _utp() {
        pop();
    }

    /**
     * Write Control Value Table in FUnits
     */
    private void _wcvtf() {
        int value = pop();
        // Conversion of value goes here
        cvt[pop()] = value;
    }

    /**
     * Write Control Value Table in Pixel units
     */
    private void _wcvtp() {
        int value = pop();
        // Conversion of value goes here
        cvt[pop()] = value;
    }

    /**
     * Write Store
     */
    private void _ws() {
        store[pop()] = pop();
    }

    private void execute(int ip) {
        while (ip < ((ip & 0xffff0000) | parser.getISLength(ip >> 16))) {
            short opcode = parser.getOpcode(ip);
            if (inFuncDef) {
                
                // We're within a function definition, so don't execute the code
                if (opcode == Mnemonic.ENDF) {
                    inFuncDef = false;
                }
                ip = parser.advanceIP(ip);
                continue;
            }
            if (opcode >= Mnemonic.MIRP) _mirp((short)(opcode & 31));
            else if (opcode >= Mnemonic.MDRP) _mdrp((short)(opcode & 31));
            else if (opcode >= Mnemonic.PUSHW) _push(parser.getPushData(ip));
            else if (opcode >= Mnemonic.PUSHB) _push(parser.getPushData(ip));
            else if (opcode >= Mnemonic.INSTCTRL) _instctrl();
            else if (opcode >= Mnemonic.SCANTYPE) _scantype();
            else if (opcode >= Mnemonic.MIN) _min();
            else if (opcode >= Mnemonic.MAX) _max();
            else if (opcode >= Mnemonic.ROLL) _roll();
            else if (opcode >= Mnemonic.IDEF) _idef();
            else if (opcode >= Mnemonic.GETINFO) _getinfo();
            else if (opcode >= Mnemonic.SDPVTL) _sdpvtl((short)(opcode & 1));
            else if (opcode >= Mnemonic.SCANCTRL) _scanctrl();
            else if (opcode >= Mnemonic.FLIPRGOFF) _fliprgoff();
            else if (opcode >= Mnemonic.FLIPRGON) _fliprgon();
            else if (opcode >= Mnemonic.FLIPPT) _flippt();
            else if (opcode >= Mnemonic.AA); // AA (ignored)
            else if (opcode >= Mnemonic.SANGW); // SANGW (ignored)
            else if (opcode >= Mnemonic.RDTG) _rdtg();
            else if (opcode >= Mnemonic.RUTG) _rutg();
            else if (opcode >= Mnemonic.ROFF) _roff();
            else if (opcode >= Mnemonic.JROF) ip = _jrof(ip);
            else if (opcode >= Mnemonic.JROT) ip = _jrot(ip);
            else if (opcode >= Mnemonic.S45ROUND) _s45round();
            else if (opcode >= Mnemonic.SROUND) _sround();
            else if (opcode >= Mnemonic.DELTAC3) _deltac3();
            else if (opcode >= Mnemonic.DELTAC2) _deltac2();
            else if (opcode >= Mnemonic.DELTAC1) _deltac1();
            else if (opcode >= Mnemonic.DELTAP3) _deltap3();
            else if (opcode >= Mnemonic.DELTAP2) _deltap2();
            else if (opcode >= Mnemonic.WCVTF) _wcvtf();
            else if (opcode >= Mnemonic.NROUND) _nround((short)(opcode & 3));
            else if (opcode >= Mnemonic.ROUND) _round((short)(opcode & 3));
            else if (opcode >= Mnemonic.CEILING) _ceiling();
            else if (opcode >= Mnemonic.FLOOR) _floor();
            else if (opcode >= Mnemonic.NEG) _neg();
            else if (opcode >= Mnemonic.ABS) _abs();
            else if (opcode >= Mnemonic.MUL) _mul();
            else if (opcode >= Mnemonic.DIV) _div();
            else if (opcode >= Mnemonic.SUB) _sub();
            else if (opcode >= Mnemonic.ADD) _add();
            else if (opcode >= Mnemonic.SDS) _sds();
            else if (opcode >= Mnemonic.SDB) _sdb();
            else if (opcode >= Mnemonic.DELTAP1) _deltap1();
            else if (opcode >= Mnemonic.NOT) _not();
            else if (opcode >= Mnemonic.OR) _or();
            else if (opcode >= Mnemonic.AND) _and();
            else if (opcode >= Mnemonic.EIF); // EIF
            else if (opcode >= Mnemonic.IF) ip = _if(ip);
            else if (opcode >= Mnemonic.EVEN) _even();
            else if (opcode >= Mnemonic.ODD) _odd();
            else if (opcode >= Mnemonic.NEQ) _neq();
            else if (opcode >= Mnemonic.EQ) _eq();
            else if (opcode >= Mnemonic.GTEQ) _gteq();
            else if (opcode >= Mnemonic.GT) _gt();
            else if (opcode >= Mnemonic.LTEQ) _lteq();
            else if (opcode >= Mnemonic.LT) _lt();
            else if (opcode >= Mnemonic.DEBUG) _debug();
            else if (opcode >= Mnemonic.FLIPOFF) _flipoff();
            else if (opcode >= Mnemonic.FLIPON) _flipon();
            else if (opcode >= Mnemonic.MPS) _mps();
            else if (opcode >= Mnemonic.MPPEM) _mppem();
            else if (opcode >= Mnemonic.MD) _md((short)(opcode & 1));
            else if (opcode >= Mnemonic.SCFS) _scfs();
            else if (opcode >= Mnemonic.GC) _gc((short)(opcode & 1));
            else if (opcode >= Mnemonic.RCVT) _rcvt();
            else if (opcode >= Mnemonic.WCVTP) _wcvtp();
            else if (opcode >= Mnemonic.RS) _rs();
            else if (opcode >= Mnemonic.WS) _ws();
            else if (opcode >= Mnemonic.NPUSHW) _push(parser.getPushData(ip));
            else if (opcode >= Mnemonic.NPUSHB) _push(parser.getPushData(ip));
            else if (opcode >= Mnemonic.MIAP) _miap((short)(opcode & 1));
            else if (opcode >= Mnemonic.RTDG) _rtdg();
            else if (opcode >= Mnemonic.ALIGNRP) _alignrp();
            else if (opcode >= Mnemonic.IP) _ip();
            else if (opcode >= Mnemonic.MSIRP) _msirp((short)(opcode & 1));
            else if (opcode >= Mnemonic.SHPIX) _shpix();
            else if (opcode >= Mnemonic.SHZ) _shz((short)(opcode & 1));
            else if (opcode >= Mnemonic.SHC) _shc((short)(opcode & 1));
            else if (opcode >= Mnemonic.SHP) _shp((short)(opcode & 1));
            else if (opcode >= Mnemonic.IUP) _iup((short)(opcode & 1));
            else if (opcode >= Mnemonic.MDAP) _mdap((short)(opcode & 1));
            else if (opcode >= Mnemonic.ENDF) return;
            else if (opcode >= Mnemonic.FDEF) _fdef(ip + 1);
            else if (opcode >= Mnemonic.CALL) _call();
            else if (opcode >= Mnemonic.LOOPCALL) _loopcall();
            else if (opcode >= Mnemonic.UTP) _utp();
            else if (opcode >= Mnemonic.ALIGNPTS) _alignpts();
            else if (opcode >= Mnemonic.MINDEX) _mindex();
            else if (opcode >= Mnemonic.CINDEX) _cindex();
            else if (opcode >= Mnemonic.DEPTH) _depth();
            else if (opcode >= Mnemonic.SWAP) _swap();
            else if (opcode >= Mnemonic.CLEAR) _clear();
            else if (opcode >= Mnemonic.POP) pop();
            else if (opcode >= Mnemonic.DUP) _dup();
            else if (opcode >= Mnemonic.SSW) _ssw();
            else if (opcode >= Mnemonic.SSWCI) _sswci();
            else if (opcode >= Mnemonic.SCVTCI) _scvtci();
            else if (opcode >= Mnemonic.JMPR) ip = _jmpr(ip);
            else if (opcode >= Mnemonic.ELSE) ip = _else(ip);
            else if (opcode >= Mnemonic.SMD) _smd();
            else if (opcode >= Mnemonic.RTHG) _rthg();
            else if (opcode >= Mnemonic.RTG) _rtg();
            else if (opcode >= Mnemonic.SLOOP) _sloop();
            else if (opcode >= Mnemonic.SZPS) _szps();
            else if (opcode >= Mnemonic.SZP2) _szp2();
            else if (opcode >= Mnemonic.SZP1) _szp1();
            else if (opcode >= Mnemonic.SZP0) _szp0();
            else if (opcode >= Mnemonic.SRP2) _srp2();
            else if (opcode >= Mnemonic.SRP1) _srp1();
            else if (opcode >= Mnemonic.SRP0) _srp0();
            else if (opcode >= Mnemonic.ISECT) _isect();
            else if (opcode >= Mnemonic.SFVTPV) _sfvtpv();
            else if (opcode >= Mnemonic.GFV) _gfv();
            else if (opcode >= Mnemonic.GPV) _gpv();
            else if (opcode >= Mnemonic.SFVFS) _sfvfs();
            else if (opcode >= Mnemonic.SPVFS) _spvfs();
            else if (opcode >= Mnemonic.SFVTL) _sfvtl((short)(opcode & 1));
            else if (opcode >= Mnemonic.SPVTL) _spvtl((short)(opcode & 1));
            else if (opcode >= Mnemonic.SFVTCA) _sfvtca((short)(opcode & 1));
            else if (opcode >= Mnemonic.SPVTCA) _spvtca((short)(opcode & 1));
            else if (opcode >= Mnemonic.SVTCA) _svtca((short)(opcode & 1));
            ip = parser.advanceIP(ip);
        }
    }

    public Point[][] getZones() {
        return zone;
    }

    private int pop() {
        return stack[--stackIndex];
    }

    private void push(int i) {
        stack[stackIndex++] = i;
    }

    public void runCvtProgram() {
        execute(0x00010000);
    }

    public void runFontProgram() {
        execute(0);
    }

    public void runGlyphProgram() {
        // instruction_control can be set to stop glyphs grid-fitting
        if ((gs.instruction_control & 1) == 0) {
            execute(0x00020000);
        }
    }

    public void setParser(Parser p) {
        parser = p;
    }
}
