text:
package com.gk969.gallery.gallery3d.glrenderer;

import android.graphics.Bitmap;
import android.graphics.Rect;
import android.graphics.RectF;
import android.opengl.GLU;
import android.opengl.GLUtils;
import android.opengl.Matrix;
import android.util.Log;

import com.gk969.gallery.gallery3d.common.Utils;
import com.gk969.gallery.gallery3d.util.IntArray;

import junit.framework.Assert;

import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.util.ArrayList;

import javax.microedition.khronos.opengles.GL10;
import javax.microedition.khronos.opengles.GL11;
import javax.microedition.khronos.opengles.GL11Ext;
import javax.microedition.khronos.opengles.GL11ExtensionPack;

public class GLES11Canvas implements GLCanvas {
    @SuppressWarnings("unused")
    private static final String TAG = "GLCanvasImp";

    private static final float OPAQUE_ALPHA = 0.95f;

    private static final int OFFSET_FILL_RECT = 0;
    private static final int OFFSET_DRAW_LINE = 4;
    private static final int OFFSET_DRAW_RECT = 6;
    private static final float[] BOX_COORDINATES = {
            0, 0, 1, 0, 0, 1, 1, 1,  // used for filling a rectangle
            0, 0, 1, 1,              // used for drawing a line
            0, 0, 0, 1, 1, 1, 1, 0}; // used for drawing the outline of a rectangle

    private GL11 mGL;

    private final float mMatrixValues[] = new float[16];
    private final float mTextureMatrixValues[] = new float[16];

    // The results of mapPoints are stored in this buffer, and the order is
    // x1, y1, x2, y2.
    private final float mMapPointsBuffer[] = new float[4];

    private final float mTextureColor[] = new float[4];

    private int mBoxCoords;

    private GLState mGLState;
    private final ArrayList<RawTexture> mTargetStack = new ArrayList<RawTexture>();

    private float mAlpha;
    private final ArrayList<ConfigState> mRestoreStack = new ArrayList<ConfigState>();
    private ConfigState mRecycledRestoreAction;

    private final RectF mDrawTextureSourceRect = new RectF();
    private final RectF mDrawTextureTargetRect = new RectF();
    private final float[] mTempMatrix = new float[32];
    private final IntArray mUnboundTextures = new IntArray();
    private final IntArray mDeleteBuffers = new IntArray();
    private int mScreenWidth;
    private int mScreenHeight;
    private boolean mBlendEnabled = true;
    private int mFrameBuffer[] = new int[1];
    private static float[] sCropRect = new float[4];

    private RawTexture mTargetTexture;

    // Drawing statistics
    int mCountDrawLine;
    int mCountFillRect;
    int mCountDrawMesh;
    int mCountTextureRect;
    int mCountTextureOES;

    private static GLId mGLId = new GLES11IdImpl();

    public GLES11Canvas(GL11 gl) {
        mGL = gl;
        mGLState = new GLState(gl);
        // First create an nio buffer, then create a VBO from it.
        int size = BOX_COORDINATES.length * Float.SIZE / Byte.SIZE;
        FloatBuffer xyBuffer = allocateDirectNativeOrderBuffer(size).asFloatBuffer();
        xyBuffer.put(BOX_COORDINATES, 0, BOX_COORDINATES.length).position(0);

        int[] name = new int[1];
        mGLId.glGenBuffers(1, name, 0);
        mBoxCoords = name[0];

        gl.glBindBuffer(GL11.GL_ARRAY_BUFFER, mBoxCoords);
        gl.glBufferData(GL11.GL_ARRAY_BUFFER, xyBuffer.capacity() * (Float.SIZE / Byte.SIZE),
                xyBuffer, GL11.GL_STATIC_DRAW);

        gl.glVertexPointer(2, GL11.GL_FLOAT, 0, 0);
        gl.glTexCoordPointer(2, GL11.GL_FLOAT, 0, 0);

        // Enable the texture coordinate array for Texture 1
        gl.glClientActiveTexture(GL11.GL_TEXTURE1);
        gl.glTexCoordPointer(2, GL11.GL_FLOAT, 0, 0);
        gl.glClientActiveTexture(GL11.GL_TEXTURE0);
        gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);

        // mMatrixValues and mAlpha will be initialized in setSize()
    }

    @Override
    public void setSize(int width, int height) {
        Assert.assertTrue(width >= 0 && height >= 0);

        if (mTargetTexture == null) {
            mScreenWidth = width;
            mScreenHeight = height;
        }
        mAlpha = 1.0f;

        GL11 gl = mGL;
        gl.glViewport(0, 0, width, height);
        gl.glMatrixMode(GL11.GL_PROJECTION);
        gl.glLoadIdentity();
        GLU.gluOrtho2D(gl, 0, width, 0, height);

        gl.glMatrixMode(GL11.GL_MODELVIEW);
        gl.glLoadIdentity();

        float matrix[] = mMatrixValues;
        Matrix.setIdentityM(matrix, 0);
        // to match the graphic coordinate system in android, we flip it vertically.
        if (mTargetTexture == null) {
            Matrix.translateM(matrix, 0, 0, height, 0);
            Matrix.scaleM(matrix, 0, 1, -1, 1);
        }
    }

    @Override
    public void setAlpha(float alpha) {
        Assert.assertTrue(alpha >= 0 && alpha <= 1);
        mAlpha = alpha;
    }

    @Override
    public float getAlpha() {
        return mAlpha;
    }

    @Override
    public void multiplyAlpha(float alpha) {
        Assert.assertTrue(alpha >= 0 && alpha <= 1);
        mAlpha *= alpha;
    }

    private static ByteBuffer allocateDirectNativeOrderBuffer(int size) {
        return ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder());
    }

    @Override
    public void drawRect(float x, float y, float width, float height, GLPaint paint) {
        GL11 gl = mGL;

        mGLState.setColorMode(paint.getColor(), mAlpha);
        mGLState.setLineWidth(paint.getLineWidth());

        saveTransform();
        translate(x, y);
        scale(width, height, 1);

        gl.glLoadMatrixf(mMatrixValues, 0);
        gl.glDrawArrays(GL11.GL_LINE_LOOP, OFFSET_DRAW_RECT, 4);

        restoreTransform();
        mCountDrawLine++;
    }

    @Override
    public void drawLine(float x1, float y1, float x2, float y2, GLPaint paint) {
        GL11 gl = mGL;

        mGLState.setColorMode(paint.getColor(), mAlpha);
        mGLState.setLineWidth(paint.getLineWidth());

        saveTransform();
        translate(x1, y1);
        scale(x2 - x1, y2 - y1, 1);

        gl.glLoadMatrixf(mMatrixValues, 0);
        gl.glDrawArrays(GL11.GL_LINE_STRIP, OFFSET_DRAW_LINE, 2);

        restoreTransform();
        mCountDrawLine++;
    }

    @Override
    public void fillRect(float x, float y, float width, float height, int color) {
        mGLState.setColorMode(color, mAlpha);
        GL11 gl = mGL;

        saveTransform();
        translate(x, y);
        scale(width, height, 1);

        gl.glLoadMatrixf(mMatrixValues, 0);
        gl.glDrawArrays(GL11.GL_TRIANGLE_STRIP, OFFSET_FILL_RECT, 4);

        restoreTransform();
        mCountFillRect++;
    }

    @Override
    public void translate(float x, float y, float z) {
        Matrix.translateM(mMatrixValues, 0, x, y, z);
    }

    // This is a faster version of translate(x, y, z) because
    // (1) we knows z = 0, (2) we inline the Matrix.translateM call,
    // (3) we unroll the loop
    @Override
    public void translate(float x, float y) {
        float[] m = mMatrixValues;
        m[12] += m[0] * x + m[4] * y;
        m[13] += m[1] * x + m[5] * y;
        m[14] += m[2] * x + m[6] * y;
        m[15] += m[3] * x + m[7] * y;
    }

    @Override
    public void scale(float sx, float sy, float sz) {
        Matrix.scaleM(mMatrixValues, 0, sx, sy, sz);
    }

    @Override
    public void rotate(float angle, float x, float y, float z) {
        if (angle == 0) return;
        float[] temp = mTempMatrix;
        Matrix.setRotateM(temp, 0, angle, x, y, z);
        Matrix.multiplyMM(temp, 16, mMatrixValues, 0, temp, 0);
        System.arraycopy(temp, 16, mMatrixValues, 0, 16);
    }

    @Override
    public void multiplyMatrix(float matrix[], int offset) {
        float[] temp = mTempMatrix;
        Matrix.multiplyMM(temp, 0, mMatrixValues, 0, matrix, offset);
        System.arraycopy(temp, 0, mMatrixValues, 0, 16);
    }

    private void textureRect(float x, float y, float width, float height) {
        GL11 gl = mGL;

        saveTransform();
        translate(x, y);
        scale(width, height, 1);

        gl.glLoadMatrixf(mMatrixValues, 0);
        gl.glDrawArrays(GL11.GL_TRIANGLE_STRIP, OFFSET_FILL_RECT, 4);

        restoreTransform();
        mCountTextureRect++;
    }

    @Override
    public void drawMesh(BasicTexture tex, int x, int y, int xyBuffer,
            int uvBuffer, int indexBuffer, int indexCount) {
        float alpha = mAlpha;
        if (!bindTexture(tex)) return;

        mGLState.setBlendEnabled(mBlendEnabled
                && (!tex.isOpaque() || alpha < OPAQUE_ALPHA));
        mGLState.setTextureAlpha(alpha);

        // Reset the texture matrix. We will set our own texture coordinates
        // below.
        setTextureCoords(0, 0, 1, 1);

        saveTransform();
        translate(x, y);

        mGL.glLoadMatrixf(mMatrixValues, 0);

        mGL.glBindBuffer(GL11.GL_ARRAY_BUFFER, xyBuffer);
        mGL.glVertexPointer(2, GL11.GL_FLOAT, 0, 0);

        mGL.glBindBuffer(GL11.GL_ARRAY_BUFFER, uvBuffer);
        mGL.glTexCoordPointer(2, GL11.GL_FLOAT, 0, 0);

        mGL.glBindBuffer(GL11.GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
        mGL.glDrawElements(GL11.GL_TRIANGLE_STRIP,
                indexCount, GL11.GL_UNSIGNED_BYTE, 0);

        mGL.glBindBuffer(GL11.GL_ARRAY_BUFFER, mBoxCoords);
        mGL.glVertexPointer(2, GL11.GL_FLOAT, 0, 0);
        mGL.glTexCoordPointer(2, GL11.GL_FLOAT, 0, 0);

        restoreTransform();
        mCountDrawMesh++;
    }

    // Transforms two points by the given matrix m. The result
    // {x1', y1', x2', y2'} are stored in mMapPointsBuffer and also returned.
    private float[] mapPoints(float m[], int x1, int y1, int x2, int y2) {
        float[] r = mMapPointsBuffer;

        // Multiply m and (x1 y1 0 1) to produce (x3 y3 z3 w3). z3 is unused.
        float x3 = m[0] * x1 + m[4] * y1 + m[12];
        float y3 = m[1] * x1 + m[5] * y1 + m[13];
        float w3 = m[3] * x1 + m[7] * y1 + m[15];
        r[0] = x3 / w3;
        r[1] = y3 / w3;

        // Same for x2 y2.
        float x4 = m[0] * x2 + m[4] * y2 + m[12];
        float y4 = m[1] * x2 + m[5] * y2 + m[13];
        float w4 = m[3] * x2 + m[7] * y2 + m[15];
        r[2] = x4 / w4;
        r[3] = y4 / w4;

        return r;
    }

    private void drawBoundTexture(
            BasicTexture texture, int x, int y, int width, int height) {
        // Test whether it has been rotated or flipped, if so, glDrawTexiOES
        // won't work
        if (isMatrixRotatedOrFlipped(mMatrixValues)) {
            if (texture.hasBorder()) {
                setTextureCoords(
                        1.0f / texture.getTextureWidth(),
                        1.0f / texture.getTextureHeight(),
                        (texture.getWidth() - 1.0f) / texture.getTextureWidth(),
                        (texture.getHeight() - 1.0f) / texture.getTextureHeight());
            } else {
                setTextureCoords(0, 0,
                        (float) texture.getWidth() / texture.getTextureWidth(),
                        (float) texture.getHeight() / texture.getTextureHeight());
            }
            textureRect(x, y, width, height);
        } else {
            // draw the rect from bottom-left to top-right
            float points[] = mapPoints(
                    mMatrixValues, x, y + height, x + width, y);
            x = (int) (points[0] + 0.5f);
            y = (int) (points[1] + 0.5f);
            width = (int) (points[2] + 0.5f) - x;
            height = (int) (points[3] + 0.5f) - y;
            if (width > 0 && height > 0) {
                ((GL11Ext) mGL).glDrawTexiOES(x, y, 0, width, height);
                mCountTextureOES++;
            }
        }
    }

    @Override
    public void drawTexture(
            BasicTexture texture, int x, int y, int width, int height) {
        drawTexture(texture, x, y, width, height, mAlpha);
    }

    private void drawTexture(BasicTexture texture,
            int x, int y, int width, int height, float alpha) {
        if (width <= 0 || height <= 0) return;

        mGLState.setBlendEnabled(mBlendEnabled
                && (!texture.isOpaque() || alpha < OPAQUE_ALPHA));
        if (!bindTexture(texture)) return;
        mGLState.setTextureAlpha(alpha);
        drawBoundTexture(texture, x, y, width, height);
    }

    @Override
    public void drawTexture(BasicTexture texture, RectF source, RectF target) {
        if (target.width() <= 0 || target.height() <= 0) return;

        // Copy the input to avoid changing it.
        mDrawTextureSourceRect.set(source);
        mDrawTextureTargetRect.set(target);
        source = mDrawTextureSourceRect;
        target = mDrawTextureTargetRect;

        mGLState.setBlendEnabled(mBlendEnabled
                && (!texture.isOpaque() || mAlpha < OPAQUE_ALPHA));
        if (!bindTexture(texture)) return;
        convertCoordinate(source, target, texture);
        setTextureCoords(source);
        mGLState.setTextureAlpha(mAlpha);
        textureRect(target.left, target.top, target.width(), target.height());
    }

    @Override
    public void drawTexture(BasicTexture texture, float[] mTextureTransform,
            int x, int y, int w, int h) {
        mGLState.setBlendEnabled(mBlendEnabled
                && (!texture.isOpaque() || mAlpha < OPAQUE_ALPHA));
        if (!bindTexture(texture)) return;
        setTextureCoords(mTextureTransform);
        mGLState.setTextureAlpha(mAlpha);
        textureRect(x, y, w, h);
    }

    // This function changes the source coordinate to the texture coordinates.
    // It also clips the source and target coordinates if it is beyond the
    // bound of the texture.
    private static void convertCoordinate(RectF source, RectF target,
            BasicTexture texture) {

        int width = texture.getWidth();
        int height = texture.getHeight();
        int texWidth = texture.getTextureWidth();
        int texHeight = texture.getTextureHeight();
        // Convert to texture coordinates
        source.left /= texWidth;
        source.right /= texWidth;
        source.top /= texHeight;
        source.bottom /= texHeight;

        // Clip if the rendering range is beyond the bound of the texture.
        float xBound = (float) width / texWidth;
        if (source.right > xBound) {
            target.right = target.left + target.width() *
                    (xBound - source.left) / source.width();
            source.right = xBound;
        }
        float yBound = (float) height / texHeight;
        if (source.bottom > yBound) {
            target.bottom = target.top + target.height() *
                    (yBound - source.top) / source.height();
            source.bottom = yBound;
        }
    }

    @Override
    public void drawMixed(BasicTexture from,
            int toColor, float ratio, int x, int y, int w, int h) {
        drawMixed(from, toColor, ratio, x, y, w, h, mAlpha);
    }

    private boolean bindTexture(BasicTexture texture) {
        if (!texture.onBind(this)) return false;
        int target = texture.getTarget();
        mGLState.setTextureTarget(target);
        mGL.glBindTexture(target, texture.getId());
        return true;
    }

    private void setTextureColor(float r, float g, float b, float alpha) {
        float[] color = mTextureColor;
        color[0] = r;
        color[1] = g;
        color[2] = b;
        color[3] = alpha;
    }

    private void setMixedColor(int toColor, float ratio, float alpha) {
        //
        // The formula we want:
        //     alpha * ((1 - ratio) * from + ratio * to)
        //
        // The formula that GL supports is in the form of:
        //     combo * from + (1 - combo) * to * scale
        //
        // So, we have combo = alpha * (1 - ratio)
        //     and     scale = alpha * ratio / (1 - combo)
        //
        float combo = alpha * (1 - ratio);
        float scale = alpha * ratio / (1 - combo);

        // Specify the interpolation factor via the alpha component of
        // GL_TEXTURE_ENV_COLORs.
        // RGB component are get from toColor and will used as SRC1
        float colorScale = scale * (toColor >>> 24) / (0xff * 0xff);
        setTextureColor(((toColor >>> 16) & 0xff) * colorScale,
                ((toColor >>> 8) & 0xff) * colorScale,
                (toColor & 0xff) * colorScale, combo);
        GL11 gl = mGL;
        gl.glTexEnvfv(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_COLOR, mTextureColor, 0);

        gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_COMBINE_RGB, GL11.GL_INTERPOLATE);
        gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_COMBINE_ALPHA, GL11.GL_INTERPOLATE);
        gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_SRC1_RGB, GL11.GL_CONSTANT);
        gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_OPERAND1_RGB, GL11.GL_SRC_COLOR);
        gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_SRC1_ALPHA, GL11.GL_CONSTANT);
        gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_OPERAND1_ALPHA, GL11.GL_SRC_ALPHA);

        // Wire up the interpolation factor for RGB.
        gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_SRC2_RGB, GL11.GL_CONSTANT);
        gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_OPERAND2_RGB, GL11.GL_SRC_ALPHA);

        // Wire up the interpolation factor for alpha.
        gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_SRC2_ALPHA, GL11.GL_CONSTANT);
        gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_OPERAND2_ALPHA, GL11.GL_SRC_ALPHA);

    }

    @Override
    public void drawMixed(BasicTexture from, int toColor, float ratio,
            RectF source, RectF target) {
        if (target.width() <= 0 || target.height() <= 0) return;

        if (ratio <= 0.01f) {
            drawTexture(from, source, target);
            return;
        } else if (ratio >= 1) {
            fillRect(target.left, target.top, target.width(), target.height(), toColor);
            return;
        }

        float alpha = mAlpha;

        // Copy the input to avoid changing it.
        mDrawTextureSourceRect.set(source);
        mDrawTextureTargetRect.set(target);
        source = mDrawTextureSourceRect;
        target = mDrawTextureTargetRect;

        mGLState.setBlendEnabled(mBlendEnabled && (!from.isOpaque()
                || !Utils.isOpaque(toColor) || alpha < OPAQUE_ALPHA));

        if (!bindTexture(from)) return;

        // Interpolate the RGB and alpha values between both textures.
        mGLState.setTexEnvMode(GL11.GL_COMBINE);
        setMixedColor(toColor, ratio, alpha);
        convertCoordinate(source, target, from);
        setTextureCoords(source);
        textureRect(target.left, target.top, target.width(), target.height());
        mGLState.setTexEnvMode(GL11.GL_REPLACE);
    }

    private void drawMixed(BasicTexture from, int toColor,
            float ratio, int x, int y, int width, int height, float alpha) {
        // change from 0 to 0.01f to prevent getting divided by zero below
        if (ratio <= 0.01f) {
            drawTexture(from, x, y, width, height, alpha);
            return;
        } else if (ratio >= 1) {
            fillRect(x, y, width, height, toColor);
            return;
        }

        mGLState.setBlendEnabled(mBlendEnabled && (!from.isOpaque()
                || !Utils.isOpaque(toColor) || alpha < OPAQUE_ALPHA));

        final GL11 gl = mGL;
        if (!bindTexture(from)) return;

        // Interpolate the RGB and alpha values between both textures.
        mGLState.setTexEnvMode(GL11.GL_COMBINE);
        setMixedColor(toColor, ratio, alpha);

        drawBoundTexture(from, x, y, width, height);
        mGLState.setTexEnvMode(GL11.GL_REPLACE);
    }

    // TODO: the code only work for 2D should get fixed for 3D or removed
    private static final int MSKEW_X = 4;
    private static final int MSKEW_Y = 1;
    private static final int MSCALE_X = 0;
    private static final int MSCALE_Y = 5;

    private static boolean isMatrixRotatedOrFlipped(float matrix[]) {
        final float eps = 1e-5f;
        return Math.abs(matrix[MSKEW_X]) > eps
                || Math.abs(matrix[MSKEW_Y]) > eps
                || matrix[MSCALE_X] < -eps
                || matrix[MSCALE_Y] > eps;
    }

    private static class GLState {

        private final GL11 mGL;

        private int mTexEnvMode = GL11.GL_REPLACE;
        private float mTextureAlpha = 1.0f;
        private int mTextureTarget = GL11.GL_TEXTURE_2D;
        private boolean mBlendEnabled = true;
        private float mLineWidth = 1.0f;
        private boolean mLineSmooth = false;

        public GLState(GL11 gl) {
            mGL = gl;

            // Disable unused state
            gl.glDisable(GL11.GL_LIGHTING);

            // Enable used features
            gl.glEnable(GL11.GL_DITHER);

            gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
            gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
            gl.glEnable(GL11.GL_TEXTURE_2D);

            gl.glTexEnvf(GL11.GL_TEXTURE_ENV,
                    GL11.GL_TEXTURE_ENV_MODE, GL11.GL_REPLACE);

            // Set the background color
            gl.glClearColor(0f, 0f, 0f, 0f);

            gl.glEnable(GL11.GL_BLEND);
            gl.glBlendFunc(GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);

            // We use 565 or 8888 format, so set the alignment to 2 bytes/pixel.
            gl.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 2);
        }

        public void setTexEnvMode(int mode) {
            if (mTexEnvMode == mode) return;
            mTexEnvMode = mode;
            mGL.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, mode);
        }

        public void setLineWidth(float width) {
            if (mLineWidth == width) return;
            mLineWidth = width;
            mGL.glLineWidth(width);
        }

        public void setTextureAlpha(float alpha) {
            if (mTextureAlpha == alpha) return;
            mTextureAlpha = alpha;
            if (alpha >= OPAQUE_ALPHA) {
                // The alpha is need for those texture without alpha channel
                mGL.glColor4f(1, 1, 1, 1);
                setTexEnvMode(GL11.GL_REPLACE);
            } else {
                mGL.glColor4f(alpha, alpha, alpha, alpha);
                setTexEnvMode(GL11.GL_MODULATE);
            }
        }

        public void setColorMode(int color, float alpha) {
            setBlendEnabled(!Utils.isOpaque(color) || alpha < OPAQUE_ALPHA);

            // Set mTextureAlpha to an invalid value, so that it will reset
            // again in setTextureAlpha(float) later.
            mTextureAlpha = -1.0f;

            setTextureTarget(0);

            float prealpha = (color >>> 24) * alpha * 65535f / 255f / 255f;
            mGL.glColor4x(
                    Math.round(((color >> 16) & 0xFF) * prealpha),
                    Math.round(((color >> 8) & 0xFF) * prealpha),
                    Math.round((color & 0xFF) * prealpha),
                    Math.round(255 * prealpha));
        }

        // target is a value like GL_TEXTURE_2D. If target = 0, texturing is disabled.
        public void setTextureTarget(int target) {
            if (mTextureTarget == target) return;
            if (mTextureTarget != 0) {
                mGL.glDisable(mTextureTarget);
            }
            mTextureTarget = target;
            if (mTextureTarget != 0) {
                mGL.glEnable(mTextureTarget);
            }
        }

        public void setBlendEnabled(boolean enabled) {
            if (mBlendEnabled == enabled) return;
            mBlendEnabled = enabled;
            if (enabled) {
                mGL.glEnable(GL11.GL_BLEND);
            } else {
                mGL.glDisable(GL11.GL_BLEND);
            }
        }
    }

    @Override
    public void clearBuffer(float[] argb) {
        if(argb != null && argb.length == 4) {
            mGL.glClearColor(argb[1], argb[2], argb[3], argb[0]);
        } else {
            mGL.glClearColor(0, 0, 0, 1);
        }
        mGL.glClear(GL10.GL_COLOR_BUFFER_BIT);
    }

    @Override
    public void clearBuffer() {
        clearBuffer(null);
    }

    private void setTextureCoords(RectF source) {
        setTextureCoords(source.left, source.top, source.right, source.bottom);
    }

    private void setTextureCoords(float left, float top,
            float right, float bottom) {
        mGL.glMatrixMode(GL11.GL_TEXTURE);
        mTextureMatrixValues[0] = right - left;
        mTextureMatrixValues[5] = bottom - top;
        mTextureMatrixValues[10] = 1;
        mTextureMatrixValues[12] = left;
        mTextureMatrixValues[13] = top;
        mTextureMatrixValues[15] = 1;
        mGL.glLoadMatrixf(mTextureMatrixValues, 0);
        mGL.glMatrixMode(GL11.GL_MODELVIEW);
    }

    private void setTextureCoords(float[] mTextureTransform) {
        mGL.glMatrixMode(GL11.GL_TEXTURE);
        mGL.glLoadMatrixf(mTextureTransform, 0);
        mGL.glMatrixMode(GL11.GL_MODELVIEW);
    }

    // unloadTexture and deleteBuffer can be called from the finalizer thread,
    // so we synchronized on the mUnboundTextures object.
    @Override
    public boolean unloadTexture(BasicTexture t) {
        synchronized (mUnboundTextures) {
            if (!t.isLoaded()) return false;
            mUnboundTextures.add(t.mId);
            return true;
        }
    }

    @Override
    public void deleteBuffer(int bufferId) {
        synchronized (mUnboundTextures) {
            mDeleteBuffers.add(bufferId);
        }
    }

    @Override
    public void deleteRecycledResources() {
        synchronized (mUnboundTextures) {
            IntArray ids = mUnboundTextures;
            if (ids.size() > 0) {
                mGLId.glDeleteTextures(mGL, ids.size(), ids.getInternalArray(), 0);
                ids.clear();
            }

            ids = mDeleteBuffers;
            if (ids.size() > 0) {
                mGLId.glDeleteBuffers(mGL, ids.size(), ids.getInternalArray(), 0);
                ids.clear();
            }
        }
    }

    @Override
    public void save() {
        save(SAVE_FLAG_ALL);
    }

    @Override
    public void save(int saveFlags) {
        ConfigState config = obtainRestoreConfig();

        if ((saveFlags & SAVE_FLAG_ALPHA) != 0) {
            config.mAlpha = mAlpha;
        } else {
            config.mAlpha = -1;
        }

        if ((saveFlags & SAVE_FLAG_MATRIX) != 0) {
            System.arraycopy(mMatrixValues, 0, config.mMatrix, 0, 16);
        } else {
            config.mMatrix[0] = Float.NEGATIVE_INFINITY;
        }

        mRestoreStack.add(config);
    }

    @Override
    public void restore() {
        if (mRestoreStack.isEmpty()) throw new IllegalStateException();
        ConfigState config = mRestoreStack.remove(mRestoreStack.size() - 1);
        config.restore(this);
        freeRestoreConfig(config);
    }

    private void freeRestoreConfig(ConfigState action) {
        action.mNextFree = mRecycledRestoreAction;
        mRecycledRestoreAction = action;
    }

    private ConfigState obtainRestoreConfig() {
        if (mRecycledRestoreAction != null) {
            ConfigState result = mRecycledRestoreAction;
            mRecycledRestoreAction = result.mNextFree;
            return result;
        }
        return new ConfigState();
    }

    private static class ConfigState {
        float mAlpha;
        float mMatrix[] = new float[16];
        ConfigState mNextFree;

        public void restore(GLES11Canvas canvas) {
            if (mAlpha >= 0) canvas.setAlpha(mAlpha);
            if (mMatrix[0] != Float.NEGATIVE_INFINITY) {
                System.arraycopy(mMatrix, 0, canvas.mMatrixValues, 0, 16);
            }
        }
    }

    @Override
    public void dumpStatisticsAndClear() {
        String line = String.format(
                "MESH:%d, TEX_OES:%d, TEX_RECT:%d, FILL_RECT:%d, LINE:%d",
                mCountDrawMesh, mCountTextureRect, mCountTextureOES,
                mCountFillRect, mCountDrawLine);
        mCountDrawMesh = 0;
        mCountTextureRect = 0;
        mCountTextureOES = 0;
        mCountFillRect = 0;
        mCountDrawLine = 0;
        Log.d(TAG, line);
    }

    private void saveTransform() {
        System.arraycopy(mMatrixValues, 0, mTempMatrix, 0, 16);
    }

    private void restoreTransform() {
        System.arraycopy(mTempMatrix, 0, mMatrixValues, 0, 16);
    }

    private void setRenderTarget(RawTexture texture) {
        GL11ExtensionPack gl11ep = (GL11ExtensionPack) mGL;

        if (mTargetTexture == null && texture != null) {
            mGLId.glGenBuffers(1, mFrameBuffer, 0);
            gl11ep.glBindFramebufferOES(
                    GL11ExtensionPack.GL_FRAMEBUFFER_OES, mFrameBuffer[0]);
        }
        if (mTargetTexture != null && texture  == null) {
            gl11ep.glBindFramebufferOES(GL11ExtensionPack.GL_FRAMEBUFFER_OES, 0);
            gl11ep.glDeleteFramebuffersOES(1, mFrameBuffer, 0);
        }

        mTargetTexture = texture;
        if (texture == null) {
            setSize(mScreenWidth, mScreenHeight);
        } else {
            setSize(texture.getWidth(), texture.getHeight());

            if (!texture.isLoaded()) texture.prepare(this);

            gl11ep.glFramebufferTexture2DOES(
                    GL11ExtensionPack.GL_FRAMEBUFFER_OES,
                    GL11ExtensionPack.GL_COLOR_ATTACHMENT0_OES,
                    GL11.GL_TEXTURE_2D, texture.getId(), 0);

            checkFramebufferStatus(gl11ep);
        }
    }

    @Override
    public void endRenderTarget() {
        RawTexture texture = mTargetStack.remove(mTargetStack.size() - 1);
        setRenderTarget(texture);
        restore(); // restore matrix and alpha
    }

    @Override
    public void beginRenderTarget(RawTexture texture) {
        save(); // save matrix and alpha
        mTargetStack.add(mTargetTexture);
        setRenderTarget(texture);
    }

    private static void checkFramebufferStatus(GL11ExtensionPack gl11ep) {
        int status = gl11ep.glCheckFramebufferStatusOES(GL11ExtensionPack.GL_FRAMEBUFFER_OES);
        if (status != GL11ExtensionPack.GL_FRAMEBUFFER_COMPLETE_OES) {
            String msg = "";
            switch (status) {
                case GL11ExtensionPack.GL_FRAMEBUFFER_INCOMPLETE_FORMATS_OES:
                    msg = "FRAMEBUFFER_FORMATS";
                    break;
                case GL11ExtensionPack.GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_OES:
                    msg = "FRAMEBUFFER_ATTACHMENT";
                    break;
                case GL11ExtensionPack.GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_OES:
                    msg = "FRAMEBUFFER_MISSING_ATTACHMENT";
                    break;
                case GL11ExtensionPack.GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_OES:
                    msg = "FRAMEBUFFER_DRAW_BUFFER";
                    break;
                case GL11ExtensionPack.GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_OES:
                    msg = "FRAMEBUFFER_READ_BUFFER";
                    break;
                case GL11ExtensionPack.GL_FRAMEBUFFER_UNSUPPORTED_OES:
                    msg = "FRAMEBUFFER_UNSUPPORTED";
                    break;
                case GL11ExtensionPack.GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_OES:
                    msg = "FRAMEBUFFER_INCOMPLETE_DIMENSIONS";
                    break;
            }
            throw new RuntimeException(msg + ":" + Integer.toHexString(status));
        }
    }

    @Override
    public void setTextureParameters(BasicTexture texture) {
        int width = texture.getWidth();
        int height = texture.getHeight();
        // Define a vertically flipped crop rectangle for OES_draw_texture.
        // The four values in sCropRect are: left, bottom, width, and
        // height. Negative value of width or height means flip.
        sCropRect[0] = 0;
        sCropRect[1] = height;
        sCropRect[2] = width;
        sCropRect[3] = -height;

        // Set texture parameters.
        int target = texture.getTarget();
        mGL.glBindTexture(target, texture.getId());
        mGL.glTexParameterfv(target, GL11Ext.GL_TEXTURE_CROP_RECT_OES, sCropRect, 0);
        mGL.glTexParameteri(target, GL11.GL_TEXTURE_WRAP_S, GL11.GL_CLAMP_TO_EDGE);
        mGL.glTexParameteri(target, GL11.GL_TEXTURE_WRAP_T, GL11.GL_CLAMP_TO_EDGE);
        mGL.glTexParameterf(target, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
        mGL.glTexParameterf(target, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
    }

    @Override
    public void initializeTextureSize(BasicTexture texture, int format, int type) {
        int target = texture.getTarget();
        mGL.glBindTexture(target, texture.getId());
        int width = texture.getTextureWidth();
        int height = texture.getTextureHeight();
        mGL.glTexImage2D(target, 0, format, width, height, 0, format, type, null);
    }

    @Override
    public void initializeTexture(BasicTexture texture, Bitmap bitmap) {
        int target = texture.getTarget();
        mGL.glBindTexture(target, texture.getId());
        GLUtils.texImage2D(target, 0, bitmap, 0);
    }

    @Override
    public void texSubImage2D(BasicTexture texture, int xOffset, int yOffset, Bitmap bitmap,
            int format, int type) {
        int target = texture.getTarget();
        mGL.glBindTexture(target, texture.getId());
        GLUtils.texSubImage2D(target, 0, xOffset, yOffset, bitmap, format, type);
    }

    @Override
    public int uploadBuffer(FloatBuffer buf) {
        return uploadBuffer(buf, Float.SIZE / Byte.SIZE);
    }

    @Override
    public int uploadBuffer(ByteBuffer buf) {
        return uploadBuffer(buf, 1);
    }

    private int uploadBuffer(Buffer buf, int elementSize) {
        int[] bufferIds = new int[1];
        mGLId.glGenBuffers(bufferIds.length, bufferIds, 0);
        int bufferId = bufferIds[0];
        mGL.glBindBuffer(GL11.GL_ARRAY_BUFFER, bufferId);
        mGL.glBufferData(GL11.GL_ARRAY_BUFFER, buf.capacity() * elementSize, buf,
                GL11.GL_STATIC_DRAW);
        return bufferId;
    }

    @Override
    public void recoverFromLightCycle() {
        // This is only required for GLES20
    }

    @Override
    public void getBounds(Rect bounds, int x, int y, int width, int height) {
        // This is only required for GLES20
    }

    @Override
    public GLId getGLId() {
        return mGLId;
    }
}


text:
_Your issue may already be reported. Please search existing `issues` before creating one._

## Your Environment

<!--- Include relevant details about your environment -->

* PowerShell Version:
* PoShPACLI Version:
* PACLI Version:
* CyberArk Version:

## Expected Behaviour

<!--- If you're describing a bug, tell us what should happen -->
<!--- If you're suggesting a change/improvement, tell us how it should work -->

## Current Behaviour

<!--- If describing a bug, tell us what happens instead of the expected behaviour -->
<!--- If suggesting a change/improvement, explain the difference from current behaviour -->

## Possible Solution

<!--- Not obligatory, but suggest a fix/reason for the bug, -->
<!--- or ideas how to implement the addition or change -->

## Steps to Reproduce (for bug reports)

<!--- Provide unambiguous set of steps to -->
<!--- reproduce this bug. Include code to reproduce, if relevant -->
1 .

2 .

3 .

4 .

## Sample Output

<!--- Provide any output you see -->
<!--- Include Verbose & Debug output where possible -->

## Context

<!--- What are you trying to accomplish? -->
<!--- Providing context helps us define the best solution -->

redpajama_set_name: RedPajamaGithub


text:
package operias.diff;

import java.io.File;
import java.io.IOException;
import java.security.InvalidParameterException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.apache.commons.io.FilenameUtils;

/**
 * Contains the source diff information for a directory and all its sub directories
 * @author soosterwaal
 *
 */
public class DiffDirectory {

	/**
	 * Original directory name
	 */
	private String originalDirectoryName;

	/**
	 * Revised directory name
	 */
	private String revisedDirectoryName;

	/**
	 * List of changed files within the directory
	 */
	private List<DiffFile> files;

	/**
	 * List of changed files within the directory
	 */
	private List<DiffDirectory> directories;

	/**
	 * Diff state of the directory
	 */
	private SourceDiffState state;

	/**
	 * Construct a new direcotyr for in the diff report
	 * @param originalDirectoryName
	 */
	public DiffDirectory(String originalDirectoryName, String revisedDirectoryName, SourceDiffState state) {
		this.originalDirectoryName = originalDirectoryName;
		this.revisedDirectoryName = revisedDirectoryName;
		this.state = state;
		this.files = new ArrayList<DiffFile>();
		this.directories = new ArrayList<DiffDirectory>();
	}

	/**
	 * @return the directoryName
	 */
	public String getOriginalDirectoryName() {
		return originalDirectoryName;
	}

	public String getRevisedDirectoryName() {
		return revisedDirectoryName;
	}

	/**
	 * @return the files
	 */
	public List<DiffFile> getFiles() {
		return files;
	}

	/**
	 * @return the state
	 */
	public SourceDiffState getState() {
		return state;
	}

	/**
	 * @param state the state to set
	 */
	public void setState(SourceDiffState state) {
		this.state = state;
	}

	/**
	 * Add a new file to the files within this directory;
	 * @param file
	 */
	public void addFile(DiffFile file) {
		files.add(file);
	}

	/**
	 * Add a new directory to this directory
	 * @param directory
	 */
	public void addDirectory(DiffDirectory directory) {
		this.directories.add(directory);
	}

	/**
	 *
	 * @param originalDirectory
	 * @param revisedDirectory
	 * @throws IOException
	 */
	public static DiffDirectory compareDirectory(String originalDirectory, String revisedDirectory) throws IOException {

		// Short cuts
		if (originalDirectory == null) {
			return fillDirectoryDiff(revisedDirectory, SourceDiffState.NEW);
		} else if (revisedDirectory == null) {
			return fillDirectoryDiff(originalDirectory, SourceDiffState.DELETED);
		}

		// Both directories should exists, if not, throw exceptions
		DiffDirectory diffDirectory = new DiffDirectory(originalDirectory, revisedDirectory, SourceDiffState.SAME);

		File originalDirectoryFile = new File(originalDirectory);
		File newDirectoryFile = new File(revisedDirectory);

		if (!originalDirectoryFile.isDirectory()) {
			throw new InvalidParameterException("'" +originalDirectory + "' is not a valid directory");
		}

		if (!newDirectoryFile.isDirectory()) {
			throw new InvalidParameterException("'" +newDirectoryFile + "' is not a valid directory");
		}

		String[] newFiles = newDirectoryFile.list();
		Arrays.sort(newFiles);
		String[] originalFiles = originalDirectoryFile.list();
		Arrays.sort(originalFiles);

		List<String> filesWithinNewDirectory = Arrays.asList(newFiles);
		List<String> filesWithinOriginalDirectory = Arrays.asList(originalFiles);

		//First cmpare the original dirs with the new one to see changes and deleted file and directories
		for(String fileName : filesWithinOriginalDirectory) {
			File originalFile = new File(originalDirectoryFile, fileName);

			if (originalFile.isHidden()) {
				continue;
			}

			if (originalFile.isDirectory()) {
				if (filesWithinNewDirectory.indexOf(fileName) >= 0) {
					// Directory exists
					File newDir = new File(newDirectoryFile, fileName);
					diffDirectory.addDirectory(DiffDirectory.compareDirectory(originalFile.getAbsolutePath(), newDir.getAbsolutePath()));
				} else {
					// Directory was deleted
					diffDirectory.addDirectory(DiffDirectory.compareDirectory(originalFile.getAbsolutePath(), null));
				}
			} else {
				if (FilenameUtils.getExtension(originalFile.getName()).equals("java")) {
					if (filesWithinNewDirectory.indexOf(fileName) >= 0) {
						// File exists
						File newFile = new File(newDirectoryFile, fileName);
						diffDirectory.addFile(DiffFile.compareFile(originalFile.getAbsolutePath(), newFile.getAbsolutePath()));
					} else {
						// File was deleted
						diffDirectory.addFile(DiffFile.compareFile(originalFile.getAbsolutePath(), null));
					}
				}
			}
		}

		//Secondly check the new directory for any new files or directories
		for(String fileName : filesWithinNewDirectory) {
			File newFile = new File(revisedDirectory, fileName);

			if (newFile.isHidden()) {
				continue;
			}
			if (filesWithinOriginalDirectory.indexOf(fileName) < 0) {
				if (newFile.isDirectory()) {
					// directory is new
					diffDirectory.addDirectory(DiffDirectory.compareDirectory(null, newFile.getAbsolutePath()));
				} else {
					// file is new

					if (FilenameUtils.getExtension(newFile.getName()).equals("java")) {
						diffDirectory.addFile(DiffFile.compareFile(null, newFile.getAbsolutePath()));
					}
				}
			}
		}

		//Check if we need to changed this directory status to changed
		diffDirectory.setNewStatusIfNeeded();

		return diffDirectory;
	}

	/**
	 * Set the status to CHANGED if any directory or file within this directory is changed
	 */
	private void setNewStatusIfNeeded() {
		for(DiffDirectory dir : directories ) {
			if (dir.getState() != SourceDiffState.SAME) {
				setState(SourceDiffState.CHANGED);
				return;
			}
		}

		for(DiffFile file : files ) {
			if (file.getSourceState() != SourceDiffState.SAME) {
				setState(SourceDiffState.CHANGED);
				return;
			}
		}
	}

	/**
	 * File a deleted directory with state deleted, only states DELETED or NEW are accepted
	 * @param fileDiffDirectory
	 * @return
	 * @throws IOException
	 */
	private static DiffDirectory fillDirectoryDiff(String directory, SourceDiffState state) throws IOException {

		File dir = new File(directory);

		File[] files = dir.listFiles();
		Arrays.sort(files);

		List<File> filesWithinDirectory = Arrays.asList(files);

		// Create the instance
		DiffDirectory diffDirectory = new DiffDirectory(directory, "", state);

		// Switch the directory name is the state is NEW
		if (state.equals(SourceDiffState.NEW)) {
			diffDirectory = new DiffDirectory("", directory, state);
		}

		for(File file : filesWithinDirectory) {

			if (file.isHidden()) {
				continue;
			}

			if (file.isFile()) {
				if (state.equals(SourceDiffState.DELETED)) {
					diffDirectory.addFile(DiffFile.compareFile(file.getAbsolutePath(), null));
				} else {
					diffDirectory.addFile(DiffFile.compareFile(null, file.getAbsolutePath()));
				}
			} else {
				if (state.equals(SourceDiffState.DELETED)) {
					diffDirectory.addDirectory(DiffDirectory.compareDirectory(file.getAbsolutePath(), null));
				} else {
					diffDirectory.addDirectory(DiffDirectory.compareDirectory(null, file.getAbsolutePath()));
				}
			}
		}

		return diffDirectory;
	}

	/**
	 * Checks if the directory has no changes
	 * @return True if no changes were found in this directory, false otherwise
	 */
	public boolean isEmpty() {

		boolean isEmpty = files.size() == 0;

		for(DiffDirectory dir : directories) {
			isEmpty &= dir.isEmpty();
		}

		return isEmpty;
	}

	/**
	 * @return the directories
	 */
	public List<DiffDirectory> getDirectories() {
		return directories;
	}

	/**
	 * Get a directory by its name
	 * @param directoryName
	 * @return
	 */
	public DiffDirectory getDirectory(String directoryName) {
		for(DiffDirectory directory : directories) {
			if (directory.getOriginalDirectoryName().equals(directoryName) || directory.getRevisedDirectoryName().equals(directoryName)) {
				return directory;
			} else if (directoryName.startsWith(directory.getOriginalDirectoryName()) || directoryName.startsWith(directory.getRevisedDirectoryName())) {
				// Check sub directories

				DiffDirectory tempDir = directory.getDirectory(directoryName);

				if (tempDir != null) {
					return tempDir;
				}
			}
		}

		return null;
	}

	/**
	 * Get the file diff instance of the given file
	 * @param filename
	 * @return
	 */
	public DiffFile getFile(String filename) {

		// Search the files of this directory
		for(DiffFile file : files) {
			if (file.getOriginalFileName().equals(filename) || file.getRevisedFileName().equals(filename)) {
				return file;
			}
		}

		//Not found in file, search in directories
		for (DiffDirectory dir : directories) {
			DiffFile sFile = dir.getFile(filename);

			if (sFile != null) {
				return sFile;
			}
		}

		// file not found
		return null;
	}
}


text:
(function(document) {
  'use strict';

  var app = document.querySelector('#app');

})(document);


text:
pywebtest-gitbook is a plugin for [gitbook](http://www.gitbook.io) that allow to run python code in the gitbook with python editor.

For Editor , using [ace](http://ace.c9.io) and [brython](www.brython.info) to run python code on browser. Running brython code are inspired from  [Pierre Quentel](https://groups.google.com/d/msg/brython/xLv55qq-L1s/mcwmI6-pEhcJ)'s answer from google group discussion.

## Install

```
npm install pywebtest-gitbook
```

In your gitbook book.json

```
"plugin" : [
	"pywebtest-gitbook"
	],
 "pluginsConfig" : {
      "bookPath" : "http://www.mybook.com/"
    }
```
You need to point the **bookPath** url because brython.js need to load from gitbook home path.

## Screenshot

![image](https://i.cloudup.com/2Bbk19AXT9.png)

## How to add python code
You can add python code like following
```
Hello World

[[[helloworld.py]]]

Bubbler Sort

[[[bubble.py]]]
```

You need to put your python file path between **[[[** and **]]]**

Example:

- file.md
- code
	-  helloworld.py

In file.md , you can call like

```
This is file.md

[[[code/helloworld.py]]

```


text:
These are just a few examples of using Rockhopper Music with JavaFX. I have a metric ****ton of Swing apps that use Rockhopper Music and have not ported them all to JavaFX yet. In progress.

## Common Music Notation

I'm not interested in building a better Sibelius or Finale - or even Lilypond or MuseScore. But I would like a simple view of the results of some algorithmic generator as common music notation. Nothing fancy. Just a quick viewer. Not Sibelius.

Around 1990, I wrote a few classes on the NeXT that used the Sonata font. So I ported that work to Swing and Java2D in the late 90s. The Sonata font is a bit long in the tooth, and the licensing is not friendly. Since I use Sibelius, I tried using the Opus font. That works, but, once again, licensing.

Enter Steinberg's [Bravura](http://www.smufl.org/fonts/) font designed by Daniel Spreadbury. It uses the
[Open Font License](http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=ofl).

Here is a tl;dr.
> Bravura is made available under the SIL Open Font License, which means that the font is free to download, use, embed, redistribute with other software (including commercial software) or to create derivative versions. The only restrictions on its use are that it cannot be sold on its own, any derivative versions cannot use the reserved font name "Bravura", and any derivative versions must likewise also be licensed under the SIL Open Font License.

I'm not a lawyer, but to me, that says I can use the font for my toys. If I'm wrong, please let me know.

There is a Bravura derivative by [Andrew Moschou](http://www.amoschou.net/) named Taneyev in case you're interested.

### Design

Right now the Staff is a JavaFX Region, and the font glyphs are Text objects. I tried using a Canvas and drawing them using the graphic context. Lacking font metrics, that was awkward. You can ask a Text node for its width. The downside is that the scene graph will have lots of Text nodes. But once again, not Sibelius.

The MIDITrack class doesn't know about JavaFX properties, but it does fire plain old B flat JavaBean properties. It's not a very good fit since the FX adapter classes are awkward. So, the StaffModel creates an ObservableList from the MIDITrack.

The Bravura font provides JSON metadata files. I've had to add the JSON parser as a maven dependency.

JavaFX currently complains about loading custom fonts. You will see this complaint after you exit the program. I just ignore it.
You will see this:

> Assertion failed: (t->numPointers == 0), function tsi_DeleteMemhandler, file src/tsimem.c, line 89.

I'm undecided on whether to keep the Staff as a Region, or to turn it into a Control. The Behavior classes will still be private in JavaFX 8, and there are improvements to the Skin interface. For now, I'm using the Region and will worry about this later.

### Drawing classes

The **Staff** and **GrandStaff** classes are fairly straightforward. Simply create in instance in code of in fxml. Then give it a **MIDITrack**, and tell it to draw. These classes do not pay attention to start beats in the **MIDINotes**. So, if you have a chord, it won't be "stacked". Once again, not Sibelius.

The **MeasureCanvas** (actually a Region) attempts to map start beats to calcuated x positions. The first cut sort of works. Rests don't work yet for example.


text:
<?php

$maindir = "../../../";
include($maindir."conexion/config.inc.php");

  require_once($maindir."funciones/check_session.php");

  require_once($maindir."funciones/timeout.php");

  if(!isset( $_SESSION['user_id'] ))  {
    header('Location: '.$maindir.'login/logout.php?code=100');
    exit();
  }

  try{
    $no_cuenta = $_POST['no_cuenta'];

    $queryString = "SELECT * FROM sa_estudiantes WHERE no_cuenta='".$no_cuenta."'";
    $query = mysql_query($queryString);
    $row = mysql_fetch_assoc($query);

    $dni = $row['dni'];
    $anios_estudio = $row['anios_estudio'];
    $indice_academico = $row['indice_academico'];
    $fecha_registro = $row['fecha_registro'];
    $uv_acumulados = $row['uv_acumulados'];
    $cod_plan_estudio = $row['cod_plan_estudio'];
    $cod_ciudad_origen = $row['cod_ciudad_origen'];
    $cod_orientacion = $row['cod_orientacion'];
    $cod_residencia_actual = $row['cod_residencia_actual'];
    $correo = obtenerCorreo($dni);
    $tipo = obtenerTipo($dni);
    $mencion = obtenerMencion($dni);
    $personales = obtenerPersonales($dni);
    $telefono = obtenerTelefono($dni);

    $mensaje = $dni.",".
                $anios_estudio.",".
                $indice_academico.",".
                $fecha_registro.",".
                $uv_acumulados.",".
                $cod_plan_estudio.",".
                $cod_ciudad_origen.",".
                $cod_orientacion.",".
                $cod_residencia_actual.",".
                $tipo.",".
                $correo.",".
                $mencion.",".
                $telefono.",".
                $personales;

    echo $mensaje;
  }catch(PDOExecption $e){
    $mensaje = "<strong>¡Error! </strong> Error al actualizar la clase.";
    $codMensaje = 0;
    echo $mensaje;
    http_response_code(500);
  }

  function obtenerCorreo($dni){
    $queryString = "SELECT Correo_Electronico from persona where N_Identidad ='".$dni."'";
    $query = mysql_query($queryString);
    $row = mysql_fetch_assoc($query);
    return $row['Correo_Electronico'];
  }

  function obtenerMencion($dni){
    $queryString = "SELECT codigo FROM sa_menciones_honorificas WHERE codigo in (SELECT cod_mencion FROM sa_estudiantes_menciones_honorificas WHERE dni_estudiante ='".$dni."')";

    $query = mysql_query($queryString);
    $row = mysql_fetch_assoc($query);
    return $row['codigo'];
  }

  function obtenerTipo($dni){
    $queryString = "SELECT codigo_tipo_estudiante FROM sa_estudiantes_tipos_estudiantes WHERE dni_estudiante ='".$dni."' ORDER BY fecha_registro DESC LIMIT 0, 1";

    $query = mysql_query($queryString);
    $row = mysql_fetch_assoc($query);
    return $row['codigo_tipo_estudiante'];
  }

  function obtenerPersonales($dni){
    $queryString = "SELECT * FROM
            persona p INNER JOIN sa_estudiantes e ON p.N_Identidad = e.dni WHERE e.dni = '".$dni."'";
    $query = mysql_query($queryString);
    $row = mysql_fetch_assoc($query);

    return $row['Primer_nombre'].",".$row["Segundo_nombre"].",".$row["Primer_apellido"].",".$row["Segundo_apellido"].",".
            $row['Sexo'].",".$row['Fecha_nacimiento'].",".$row['Estado_Civil'].",".$row['Nacionalidad'].",".$row['Direccion'];
  }

  function obtenerTelefono($dni){
    $queryString = "SELECT Numero FROM telefono WHERE N_Identidad = '".$dni."'";

    $query = mysql_query($queryString);
    $row = mysql_fetch_assoc($query);
    return $row['Numero'];
  }
?>


text:
Hello {{name}}
glytch {{glytch}}
binary {{binary}}
value {{value}}
numeric {{numeric}}


text:
Ext.define('Metadata.store.Attributi.Tipo',
{
    extend: 'Ext.data.Store',
    storeId: 'AttributiTipo',
    fields: ['name', 'value'],
    autoLoad: true,
	proxy:
	{
        type: 'ajax',
        url: 'metadata/getAttributiTipo'
    }
});


text:
Gauges - SAP Lumira visualization extension
=================================================
 * By [Andrew Jabbitt](http://scn.sap.com/people/andrew.jabbitt01)

Files
-----------
* `ucs.viz.ext.gauge.zip` - SAP Lumira extension, packaged with SAP WebIDE

Resources
-----------
* Blog post - [SAP Lumira Extension - Taking D3 Gauges to the next level](http://scn.sap.com/community/lumira/blog/2014/05/05/sap-lumira-extension--taking-gauges-to-the-next-level)


text:
var driver;

if (typeof window === 'undefined') {
  driver = require('./' +
    (global.MONGOOSE_DRIVER_PATH || 'node-mongodb-native'));
} else {
  driver = require('./browser');
}

module.exports = driver;


text:
namespace v8 {
namespace internal {

// static
HType HType::FromType(Type* type) {
  if (Type::Any()->Is(type)) return HType::Any();
  if (!type->IsInhabited()) return HType::None();
  if (type->Is(Type::SignedSmall())) return HType::Smi();
  if (type->Is(Type::Number())) return HType::TaggedNumber();
  if (type->Is(Type::Null())) return HType::Null();
  if (type->Is(Type::String())) return HType::String();
  if (type->Is(Type::Boolean())) return HType::Boolean();
  if (type->Is(Type::Undefined())) return HType::Undefined();
  if (type->Is(Type::Object())) return HType::JSObject();
  if (type->Is(Type::Receiver())) return HType::JSReceiver();
  return HType::Tagged();
}

// static
HType HType::FromFieldType(Handle<FieldType> type, Zone* temp_zone) {
  return FromType(type->Convert(temp_zone));
}

// static
HType HType::FromValue(Handle<Object> value) {
  if (value->IsSmi()) return HType::Smi();
  if (value->IsNull()) return HType::Null();
  if (value->IsHeapNumber()) {
    double n = Handle<v8::internal::HeapNumber>::cast(value)->value();
    return IsSmiDouble(n) ? HType::Smi() : HType::HeapNumber();
  }
  if (value->IsString()) return HType::String();
  if (value->IsBoolean()) return HType::Boolean();
  if (value->IsUndefined()) return HType::Undefined();
  if (value->IsJSArray()) return HType::JSArray();
  if (value->IsJSObject()) return HType::JSObject();
  DCHECK(value->IsHeapObject());
  return HType::HeapObject();
}

std::ostream& operator<<(std::ostream& os, const HType& t) {
  // Note: The c1visualizer syntax for locals allows only a sequence of the
  // following characters: A-Za-z0-9_-|:
  switch (t.kind_) {
#define DEFINE_CASE(Name, mask) \
  case HType::k##Name:          \
    return os << #Name;
    HTYPE_LIST(DEFINE_CASE)
#undef DEFINE_CASE
  }
  UNREACHABLE();
  return os;
}

}  // namespace internal
}  // namespace v8


text:
package com.tkmtwo.kavro.consumer;

import org.apache.avro.generic.GenericContainer;

public class ConfessingKavroConsumer<T extends GenericContainer>
  extends KavroConsumer<T> {

	public ConfessingKavroConsumer(final Class<T> dc) {
    super(dc);
	}

  protected void doWithDatum(T t) {
    System.out.println("Confession says: " + t.toString());
  }

}


text:
// Injecting environment variables
require('dotenv').config();

var path = require('path');
var express = require('express');
var cors = require('cors');
var compression = require('compression');
var serveStatic = require('serve-static');
var passport = require('passport');
var DBS = require('./dbs');
const PORT = process.env.PORT || 8003;

const app = express();
app.use(compression());
app.use(cors());

// Define routes
app.use(require('./routes'));

// Serve tilesets
global.dataRoot = path.join(__dirname, 'data');
app.use(serveStatic(path.join(__dirname, 'data'), {
    index: false
}));

/*
* Firstly we connect to a MongoDB server,
* Then we start listening for incoming requests
*/
DBS.EstablishConnection()
    .then(function () {
        console.log("MongoDB connection established!");
        const server = app.listen(PORT, function () {
            console.log('3D backend is running on http://localhost:' + PORT);
        });
    })
    .catch(function (error) {
        console.log("Database connection failure: " + error.err);
    });


text:
<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="#43AEE6"/>

    <stroke android:width="3dp"
        android:color="#43AEE6"
        />

    <padding android:left="1dp"
        android:top="1dp"
        android:right="1dp"
        android:bottom="1dp"
        />

    <corners android:bottomRightRadius="4dp" android:bottomLeftRadius="4dp"
        android:topLeftRadius="4dp" android:topRightRadius="4dp"/>
</shape>


text:
<?php

namespace CmsCommon\Initializer;

use Zend\ServiceManager\AbstractPluginManager,
    Zend\ServiceManager\InitializerInterface,
    Zend\ServiceManager\ServiceLocatorAwareInterface,
    Zend\ServiceManager\ServiceLocatorInterface;

class ServiceManagerInitializer implements InitializerInterface
{
    /**
     * {@inheritDoc}
     */
    public function initialize($instance, ServiceLocatorInterface $serviceLocator)
    {
        if ($instance instanceof ServiceLocatorAwareInterface) {
            if ($serviceLocator instanceof AbstractPluginManager) {
                $serviceLocator = $serviceLocator->getServiceLocator();
            }

            $instance->setServiceLocator($serviceLocator);
        }
    }
}


text:
#include "include/core/SkTypes.h"

#ifdef SK_XML

#include "experimental/svg/model/SkSVGDOM.h"
#include "include/core/SkCanvas.h"
#include "include/core/SkRect.h"
#include "include/core/SkStream.h"
#include "samplecode/Sample.h"
#include "src/core/SkOSFile.h"
#include "src/utils/SkOSPath.h"
#include "src/xml/SkDOM.h"
#include "tools/Resources.h"

namespace {
class AnimatedSVGSample : public Sample {
    static constexpr auto kAnimationIterations = 5;
    enum State {
        kZoomIn,
        kScroll,
        kZoomOut
    };
    sk_sp<SkSVGDOM> fDom;
    const char*     fResource = nullptr;
    const char*     fName = nullptr;
    State           fState = kZoomIn;
    int             fAnimationLoop = kAnimationIterations;
    SkScalar        fDelta = 1;

public:
    AnimatedSVGSample(const char* r, const char* n) : fResource(r), fName(n) {}

private:
    void onOnceBeforeDraw() override {
        SkASSERT(fResource);
        auto data = GetResourceAsData(fResource);
        if (!data) {
            SkDebugf("Resource not found: \"%s\"\n", fResource);
            return;
        }
        SkMemoryStream svgStream(std::move(data));

        SkDOM xmlDom;
        if (!xmlDom.build(svgStream)) {
            SkDebugf("XML parsing failed: \"%s\"\n", fResource);
            return;
        }

        fDom = SkSVGDOM::MakeFromDOM(xmlDom);
        if (fDom) {
            fDom->setContainerSize(SkSize::Make(this->width(), this->height()));
        }
    }

    void onDrawContent(SkCanvas* canvas) override {
        if (fDom) {
            canvas->setMatrix(SkMatrix::MakeScale(3));
            canvas->clipRect(SkRect::MakeLTRB(0, 0, 400, 400));
            switch (fState) {
                case kZoomIn:
                    fDelta += 0.2f;
                    canvas->concat(SkMatrix::MakeScale(fDelta));
                    break;
                case kScroll:
                    if (fAnimationLoop > kAnimationIterations/2) {
                        fDelta += 80.f;
                    } else {
                        fDelta -= 80.f;
                    }
                    canvas->concat(SkMatrix::MakeScale(fDelta));
                    canvas->translate(fDelta, 0);
                    break;
                case kZoomOut:
                    fDelta += 0.2f;
                    canvas->concat(SkMatrix::MakeScale(fDelta));
                    break;
            }

            fDom->render(canvas);
        }
    }

    void onSizeChange() override {
        if (fDom) {
            fDom->setContainerSize(SkSize::Make(this->width(), this->height()));
        }
    }

    SkString name() override { return SkASSERT(fName), SkString(fName); }

    bool onAnimate(double nanos) override {
        if (!fDom) {
            return false;
        }

        --fAnimationLoop;
        if (fAnimationLoop == 0) {
            fAnimationLoop = kAnimationIterations;
            switch (fState) {
                case kZoomIn:
                    fState = kScroll;
                    fDelta = 0;
                    break;
                case kScroll:
                    fState = kZoomOut;
                    fDelta = 2;
                    break;
                case kZoomOut:
                    fState = kZoomIn;
                    fDelta = 1;
                    break;
            }
        }
        return true;
    }
};
} // namespace

DEF_SAMPLE( return new AnimatedSVGSample("Cowboy.svg", "SampleCowboy"); )

#endif  // SK_XML


text:
package org.apache.flink.table.planner.plan.schema

import org.apache.flink.table.functions
import org.apache.flink.table.functions.TableFunction
import org.apache.flink.table.planner.functions.utils.UserDefinedFunctionUtils
import org.apache.flink.table.runtime.types.LogicalTypeDataTypeConverter.fromDataTypeToLogicalType
import org.apache.flink.table.types.DataType
import org.apache.flink.table.types.utils.TypeConversions

import org.apache.calcite.rel.`type`.{RelDataType, RelDataTypeFactory}

/**
  * A Deferred Type is a Table Function which the result type hasn't been determined yet.
  * It will determine the result type after the arguments are passed.
  *
  * @param tableFunction The Table Function instance
  * @param implicitResultType Implicit result type.
  */
class DeferredTypeFlinkTableFunction(
    val tableFunction: TableFunction[_],
    val implicitResultType: DataType)
  extends FlinkTableFunction(tableFunction) {

  override def getExternalResultType(
      tableFunction: functions.TableFunction[_],
      arguments: Array[AnyRef],
      argTypes: Array[Class[_]]): DataType = {
    // TODO
//    val resultType = tableFunction.getResultType(arguments, argTypes)
    val resultType = tableFunction.getResultType
    if (resultType != null) {
      TypeConversions.fromLegacyInfoToDataType(resultType)
    } else {
      // if user don't specific the result type, using the implicit type
      implicitResultType
    }
  }

  override def getRowType(
      typeFactory: RelDataTypeFactory,
      arguments: Array[AnyRef],
      argTypes: Array[Class[_]]): RelDataType = {
    val resultType = getExternalResultType(tableFunction, arguments, argTypes)
    val (fieldNames, fieldIndexes, _) = UserDefinedFunctionUtils.getFieldInfo(resultType)
    UserDefinedFunctionUtils.buildRelDataType(
      typeFactory, fromDataTypeToLogicalType(resultType), fieldNames, fieldIndexes)
  }
}


text:
"use strict";
var fs_1 = require('fs');
var path_1 = require('path');
require('reflect-metadata');
var typescript_1 = require('typescript');
var compiler_cli_1 = require('@angular/compiler-cli');
var tsc_1 = require('@angular/tsc-wrapped/src/tsc');
var hybrid_file_system_factory_1 = require('../util/hybrid-file-system-factory');
var compiler_host_factory_1 = require('./compiler-host-factory');
var app_module_resolver_1 = require('./app-module-resolver');
var reflector_host_1 = require('./reflector-host');
var typescript_utils_1 = require('../util/typescript-utils');
var utils_1 = require('./utils');
var logger_1 = require('../logger/logger');
var logger_diagnostics_1 = require('../logger/logger-diagnostics');
var logger_typescript_1 = require('../logger/logger-typescript');
var errors_1 = require('../util/errors');
var helpers_1 = require('../util/helpers');
var AotCompiler = (function () {
    function AotCompiler(context, options) {
        this.context = context;
        this.options = options;
        this.tsConfig = getNgcConfig(this.context, this.options.tsConfigPath);
        this.angularCompilerOptions = Object.assign({}, this.tsConfig.ngOptions, {
            basePath: this.options.rootDir,
            entryPoint: this.options.entryPoint
        });
        this.fileSystem = hybrid_file_system_factory_1.getInstance();
        this.compilerHost = compiler_host_factory_1.getInstance(this.tsConfig.parsed.options);
        this.program = typescript_1.createProgram(this.tsConfig.parsed.fileNames, this.tsConfig.parsed.options, this.compilerHost);
        this.reflectorHost = new compiler_cli_1.ReflectorHost(this.program, this.compilerHost, this.angularCompilerOptions);
        this.reflector = new compiler_cli_1.StaticReflector(this.reflectorHost);
    }
    AotCompiler.prototype.compile = function () {
        var _this = this;
        return Promise.resolve().then(function () {
        }).then(function () {
            logger_diagnostics_1.clearDiagnostics(_this.context, logger_diagnostics_1.DiagnosticsType.TypeScript);
            var i18nOptions = {
                i18nFile: undefined,
                i18nFormat: undefined,
                locale: undefined,
                basePath: _this.options.rootDir
            };
            // Create the Code Generator.
            var codeGenerator = compiler_cli_1.CodeGenerator.create(_this.angularCompilerOptions, i18nOptions, _this.program, _this.compilerHost, new compiler_cli_1.NodeReflectorHostContext(_this.compilerHost));
            // We need to temporarily patch the CodeGenerator until either it's patched or allows us
            // to pass in our own ReflectorHost.
            reflector_host_1.patchReflectorHost(codeGenerator);
            return codeGenerator.codegen({ transitiveModules: true });
        }).then(function () {
            // Create a new Program, based on the old one. This will trigger a resolution of all
            // transitive modules, which include files that might just have been generated.
            _this.program = typescript_1.createProgram(_this.tsConfig.parsed.fileNames, _this.tsConfig.parsed.options, _this.compilerHost, _this.program);
            var globalDiagnostics = _this.program.getGlobalDiagnostics();
            var tsDiagnostics = _this.program.getSyntacticDiagnostics()
                .concat(_this.program.getSemanticDiagnostics())
                .concat(_this.program.getOptionsDiagnostics());
            if (globalDiagnostics.length) {
                var diagnostics = logger_typescript_1.runTypeScriptDiagnostics(_this.context, globalDiagnostics);
                logger_diagnostics_1.printDiagnostics(_this.context, logger_diagnostics_1.DiagnosticsType.TypeScript, diagnostics, true, false);
                throw new errors_1.BuildError(new Error('Failed to transpile TypeScript'));
            }
            if (tsDiagnostics.length) {
                var diagnostics = logger_typescript_1.runTypeScriptDiagnostics(_this.context, tsDiagnostics);
                logger_diagnostics_1.printDiagnostics(_this.context, logger_diagnostics_1.DiagnosticsType.TypeScript, diagnostics, true, false);
                throw new errors_1.BuildError(new Error('Failed to transpile TypeScript'));
            }
        })
            .then(function () {
            for (var _i = 0, _a = _this.tsConfig.parsed.fileNames; _i < _a.length; _i++) {
                var fileName = _a[_i];
                var cleanedFileName = path_1.normalize(path_1.resolve(fileName));
                var content = fs_1.readFileSync(cleanedFileName).toString();
                _this.context.fileCache.set(cleanedFileName, { path: cleanedFileName, content: content });
            }
        })
            .then(function () {
            var mainFile = _this.context.fileCache.get(_this.options.entryPoint);
            if (!mainFile) {
                throw new errors_1.BuildError(new Error("Could not find entry point (bootstrap file) " + _this.options.entryPoint));
            }
            var mainSourceFile = typescript_utils_1.getTypescriptSourceFile(mainFile.path, mainFile.content, typescript_1.ScriptTarget.Latest, false);
            var AppNgModuleStringAndClassName = app_module_resolver_1.resolveAppNgModuleFromMain(mainSourceFile, _this.context.fileCache, _this.compilerHost, _this.program);
            var AppNgModuleTokens = AppNgModuleStringAndClassName.split('#');
            var modifiedFileContent = null;
            try {
                modifiedFileContent = utils_1.replaceBootstrap(mainFile.path, mainFile.content, AppNgModuleTokens[0], AppNgModuleTokens[1]);
            }
            catch (ex) {
                logger_1.Logger.debug("Failed to parse bootstrap: ", ex.message);
                logger_1.Logger.warn("Failed to parse and update " + _this.options.entryPoint + " content for AoT compilation.\n                    For now, the default fallback content will be used instead.\n                    Please consider updating " + _this.options.entryPoint + " with the content from the following link:\n                    https://github.com/driftyco/ionic2-app-base/tree/master/src/app/main.ts");
                modifiedFileContent = utils_1.getFallbackMainContent();
            }
            _this.context.fileCache.set(_this.options.entryPoint, { path: _this.options.entryPoint, content: modifiedFileContent });
        })
            .then(function () {
            var tsFiles = _this.context.fileCache.getAll().filter(function (file) { return path_1.extname(file.path) === '.ts' && file.path.indexOf('.d.ts') === -1; });
            for (var _i = 0, tsFiles_1 = tsFiles; _i < tsFiles_1.length; _i++) {
                var tsFile = tsFiles_1[_i];
                var cleanedFileContent = typescript_utils_1.removeDecorators(tsFile.path, tsFile.content);
                tsFile.content = cleanedFileContent;
                var transpileOutput = _this.transpileFileContent(tsFile.path, cleanedFileContent, _this.tsConfig.parsed.options);
                var diagnostics = logger_typescript_1.runTypeScriptDiagnostics(_this.context, transpileOutput.diagnostics);
                if (diagnostics.length) {
                    // darn, we've got some things wrong, transpile failed :(
                    logger_diagnostics_1.printDiagnostics(_this.context, logger_diagnostics_1.DiagnosticsType.TypeScript, diagnostics, true, true);
                    throw new errors_1.BuildError();
                }
                var jsFilePath = helpers_1.changeExtension(tsFile.path, '.js');
                _this.fileSystem.addVirtualFile(jsFilePath, transpileOutput.outputText);
                _this.fileSystem.addVirtualFile(jsFilePath + '.map', transpileOutput.sourceMapText);
            }
        });
    };
    AotCompiler.prototype.transpileFileContent = function (fileName, sourceText, options) {
        var transpileOptions = {
            compilerOptions: options,
            fileName: fileName,
            reportDiagnostics: true
        };
        return typescript_1.transpileModule(sourceText, transpileOptions);
    };
    return AotCompiler;
}());
exports.AotCompiler = AotCompiler;
function getNgcConfig(context, tsConfigPath) {
    var tsConfigFile = tsc_1.tsc.readConfiguration(tsConfigPath, process.cwd());
    if (!tsConfigFile) {
        throw new errors_1.BuildError("tsconfig: invalid tsconfig file, \"" + tsConfigPath + "\"");
    }
    return tsConfigFile;
}
exports.getNgcConfig = getNgcConfig;


text:
/*
 * Test cases converted from http://testsuites.opera.com/JSON/runner.htm
 */

#include <string>
#include <sstream>
#include <fstream>
#include <iostream>
#include <iomanip>
#include "../JSON.h"
#include "functions.h"

// Set to the width of the description column
#define DESC_LENGTH	50

using namespace std;

// Helper to do a quick parse check
bool parse_check(wstring str)
{
	JSONValue *v = JSON::Parse(str.c_str());
	if (v)
	{
		delete v;
		return true;
	}
	else
		return false;
}

// Helper to get a files contents
bool get_file(string filename, wstring &description, wstring &data)
{
	wifstream in(filename.c_str());
	if (in.is_open() == false)
		return false;

	getline(in, description);
	if (description.length() > DESC_LENGTH)
		description.resize(DESC_LENGTH);

	wstring line;
	data = L"";
	while (getline(in, line))
	{
		data += line;
		if (!in.eof()) data += L"\n";
	}
	return true;
}

// Run a pass / fail test
void run_test_type(bool type)
{
	int test = 0;
	wstring data = L"", name = L"";
	ostringstream stream;
	wostringstream wstream;

	while (true)
	{
		stream.str("");
		stream << "test_cases/" << (type ? "pass" : "fail") << (++test) << ".json";
		if (get_file(stream.str(), name, data) == false) break;

		print_out(L"| ");

		wstream.str(L"");
		wstream.setf(ios_base::left, ios_base::adjustfield);
		wstream << setw(DESC_LENGTH) << name;
		print_out(wstream.str().c_str());

		print_out(L" | ");
		print_out(parse_check(data) != type ? L"failed" : L"passed");
		print_out(L" |\r\n");
	}
}

// Tests to run
void run_tests()
{
	wstring vert_sep = wstring(L"+-") + wstring(DESC_LENGTH, L'-') + wstring(L"-+--------+\r\n");

	print_out(vert_sep.c_str());

	wstring header = wstring(L"| Test case") + wstring(DESC_LENGTH - 9, L' ') + wstring(L" | Result |\r\n");
	print_out(header.c_str());

	print_out(vert_sep.c_str());

	run_test_type(true);
	run_test_type(false);

	// Static test for a very precise decimal number
	double decimal = 40.9358215191158457340974;
	JSONValue *json_value = JSON::Parse("40.9358215191158457340974");
	wstring test_output = wstring(L"| Very precise decimal number") + wstring(DESC_LENGTH - 27, L' ') + wstring(L" | ");
	if (json_value && json_value->IsNumber() && json_value->AsNumber() == decimal)
	{
		test_output += wstring(L"passed |\r\n");
		delete json_value;
	}
	else
	{
		test_output += wstring(L"failed |\r\n");
	}
	print_out(test_output.c_str());

	// Static test for a decimal number with leading zeros
	decimal = 1.00034985734000;
	json_value = JSON::Parse("1.00034985734000");
	test_output = wstring(L"| Decimal number with leading zeros") + wstring(DESC_LENGTH - 33, L' ') + wstring(L" | ");
	if (json_value && json_value->IsNumber() && json_value->AsNumber() == decimal)
	{
		test_output += wstring(L"passed |\r\n");
	}
	else
	{
		test_output += wstring(L"failed |\r\n");
	}
	print_out(test_output.c_str());

	print_out(vert_sep.c_str());
}