function ref(getFunc, setFunc, objInst) {
    var r = {
        g: getFunc,
        s: setFunc,
        $: objInst
    };
    return function(v) {
        if (v !== undefined) r.s(v);
        return r.g();
    }
}

function _stringFromCharArray(array) {
    var str = "";
    for (var i = 0; i < array.length; i++) str += array[i];
    return str;
}

function _newFilledArray(size, cb) 
{
    var array = new Array(size);
    for (var i = 0; i < size; i++) { array[i] = cb(); }
    return array;
}

Object.prototype.GetHashCode = function () {
    return 0;
}

Object.prototype.Equals = function (o) {
    return this == o;
}

Object.prototype.ToString = function () {
    return "Object";
}

Number.prototype.GetHashCode = function() {
    return this | 0;
}

Number.prototype.ToString = function() {
    return "" + this;
}

String.prototype.GetHashCode = function() {
    var hash = 0;
    for (i = 0; i < this.length; i++) {
        hash = (hash << 5) - hash + this.charCodeAt(i);
    }
    return hash | 0;
}

String.prototype.ToString = function() {
    return this;
}

String.prototype.Split = function (splitChars) {
    if (splitChars.length == 0) return this;
    var copy = this;
    for (var i = 0; i < splitChars.length; i++) splitChars[i] = String.fromCharCode(splitChars[i]);
    for (var i = 1; i < splitChars.length; i++) copy.replace(splitChars[i], splitChars[0]);
    return copy.split(splitChars[0]);
}

function uglCompileShader(type, name, src) {
	src = "precision mediump float;"+src;
	var shader = gl.createShader(type);
	gl.shaderSource(shader, src);
	gl.compileShader(shader);
	if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) alert("Error compiling " + name + ":\n" + gl.getShaderInfoLog(shader));
	return shader;
}

function uglLinkProgram(vs, fs)
{
	var prg = gl.createProgram();
	gl.attachShader(prg, vs);
	gl.attachShader(prg, fs);
	gl.linkProgram(prg);
	if (!gl.getProgramParameter(prg, gl.LINK_STATUS)) alert("Error linking shader:\n" + gl.getProgramInfoLog(prg));
	return prg;
}

_loadingImageCount = 0;

function uglLoadTexture(target, handle, filename, refWidth, refHeight, refMips)
{
    var image = new Image();
    image.onload = function () {
        gl.bindTexture(target, handle);
        gl.texImage2D(target, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);

        var w = image.width,
            h = image.height,
            a, b, c, d, mc = 1;

        a = b = gl.LINEAR;
        c = d = gl.CLAMP_TO_EDGE;

        refWidth(w);
        refHeight(h);

        if ((w == (w & -w)) && (h == (h & -h))) {
            gl.generateMipmap(target);
            b = gl.LINEAR_MIPMAP_LINEAR;
            c = d = gl.REPEAT;
            while (w > 1 || h > 1) {
                if (w > 1) w >>= 1;
                if (h > 1) h >>= 1;
                mc++;
            }
        }

        gl.texParameteri(target, gl.TEXTURE_MAG_FILTER, a);
        gl.texParameteri(target, gl.TEXTURE_MIN_FILTER, b);
        gl.texParameteri(target, gl.TEXTURE_WRAP_S, c);
        gl.texParameteri(target, gl.TEXTURE_WRAP_T, d);

        refMips(mc);

        gl.bindTexture(target, null);
        _loadingImageCount--;
    };
    image.src = "data/" + filename;
	_loadingImageCount++;
}

function uglLoadPngAsBytes(filename, size, callback)
{
    var image = new Image();
    image.onload = function () {
        with (document.createElement('canvas')) {
            width = image.width;
            height = image.height;
            var c = getContext('2d');
            c.drawImage(this, 0, 0);
            var data = new Uint8Array(size);
            var img = c.getImageData(0, 0, image.width, image.height);
            for (var i = 0; i < size; i++) data[i] = img.data[4 * ((i / 3) | 0) + (i % 3)];
            callback.Invoke(data);
        }
        _loadingImageCount--;
    };
    image.src = "data/"+filename;
	_loadingImageCount++;
}

/*
Fields =
{
    2: ["X", "Y"],
    3: ["X", "Y", "Z"],
    4: ["X", "Y", "Z", "W"],
    9: ["M11", "M12", "M13", "M21", "M22", "M23", "M31", "M32", "M33"],
    16: ["M11", "M12", "M13", "M14", "M21", "M22", "M23", "M24", "M31", "M32", "M33", "M34", "M41", "M42", "M43", "M44"]
};

function uglFloatArray(value, stride, count) {
    if (count === undefined) value = [value], count = 1;
    var r = new Float32Array(stride * count), i = 0, f = Fields[stride], l = f.length;
    if (stride == 1) for (var j = 0; j < count; j++) r[i++] = value[j];
    else for (var j = 0; j < count; j++) for (var k = 0; k < l; k++) r[i++] = value[j][f[k]];
    return r;
}
*/

function uglFloat2(x)
{
    return new Float32Array([x.X, x.Y]);
}

function uglFloat3(x)
{
    return new Float32Array([x.X, x.Y, x.Z]);
}

function uglFloat4(x)
{
    return new Float32Array([x.X, x.Y, x.Z, x.W]);
}

function uglFloat3x3(x)
{
    return new Float32Array([
        x.M11, x.M12, x.M13,
        x.M21, x.M22, x.M23,
        x.M31, x.M32, x.M33]);
}

function uglFloat4x4(x)
{
    return new Float32Array([
        x.M11, x.M12, x.M13, x.M14, 
        x.M21, x.M22, x.M23, x.M24, 
        x.M31, x.M32, x.M33, x.M34, 
        x.M41, x.M42, x.M43, x.M44]);
}

function uglFloat(x,l)
{
    var r = new Float32Array(l);
    for (var i = 0; i < l; i++)
    { 
        r[i] = x[i];
    }
    return r;
}

function uglFloat2Array(x,l)
{
    var r = new Float32Array(l*2);
    for (var i = 0; i < l; i++)
    { 
        r[i*2 + 0] = x[i].X;
        r[i*2 + 1] = x[i].Y;
    }
    return r;
}

function uglFloat3Array(x,l)
{
    var r = new Float32Array(l*3);
    for (var i = 0; i < l; i++)
    { 
        r[i*3 + 0] = x[i].X;
        r[i*3 + 1] = x[i].Y;
        r[i*3 + 2] = x[i].Z;
    }
    return r;
}

function uglFloat4Array(x,l)
{
    var r = new Float32Array(l*4);
    for (var i = 0; i < l; i++)
    { 
        r[i*4 + 0] = x[i].X;
        r[i*4 + 1] = x[i].Y;
        r[i*4 + 2] = x[i].Z;
        r[i*4 + 3] = x[i].W;
    }
    return r;
}

function uglFloat3x3Array(x,l)
{
    var r = new Float32Array(l*9);
    for (var i = 0; i < l; i++)
    { 
        r[i*9 + 0] = x[i].M11;
        r[i*9 + 1] = x[i].M12;
        r[i*9 + 2] = x[i].M13;
        r[i*9 + 3] = x[i].M21;
        r[i*9 + 4] = x[i].M22;
        r[i*9 + 5] = x[i].M23;
        r[i*9 + 6] = x[i].M31;
        r[i*9 + 7] = x[i].M32;
        r[i*9 + 8] = x[i].M33;
    }
    return r;
}

function uglFloat4x4Array(x,l)
{
    var r = new Float32Array(l*16);
    for (var i = 0; i < l; i++)
    { 
        r[i*16 + 0] = x[i].M11;
        r[i*16 + 1] = x[i].M12;
        r[i*16 + 2] = x[i].M13;
        r[i*16 + 3] = x[i].M14;
        r[i*16 + 4] = x[i].M21;
        r[i*16 + 5] = x[i].M22;
        r[i*16 + 6] = x[i].M23;
        r[i*16 + 7] = x[i].M24;
        r[i*16 + 8] = x[i].M31;
        r[i*16 + 9] = x[i].M32;
        r[i*16 +10] = x[i].M33;
        r[i*16 +11] = x[i].M34;
        r[i*16 +12] = x[i].M41;
        r[i*16 +13] = x[i].M42;
        r[i*16 +14] = x[i].M43;
        r[i*16 +15] = x[i].M44;
    }
    return r;
}

function uglShort2Array(x, l) {
    var r = new Int16Array(l * 2);
    for (var i = 0; i < l; i++) {
        r[i * 2 + 0] = x[i].X;
        r[i * 2 + 1] = x[i].Y;
    }
    return r;
}

function uglShort4Array(x, l) {
    var r = new Int16Array(l * 4);
    for (var i = 0; i < l; i++) {
        r[i * 4 + 0] = x[i].X;
        r[i * 4 + 1] = x[i].Y;
        r[i * 4 + 2] = x[i].Z;
        r[i * 4 + 3] = x[i].W;
    }
    return r;
}

function uglUShort2Array(x,l)
{
    var r = new Uint16Array(l*2);
    for (var i = 0; i < l; i++)
    { 
        r[i*2 + 0] = x[i].X;
        r[i*2 + 1] = x[i].Y;
    }
    return r;
}

function uglUShort4Array(x,l)
{
    var r = new Uint16Array(l*4);
    for (var i = 0; i < l; i++)
    { 
        r[i*4 + 0] = x[i].X;
        r[i*4 + 1] = x[i].Y;
        r[i*4 + 2] = x[i].Z;
        r[i*4 + 3] = x[i].W;
    }
    return r;
}

function uglSByte2Array(x,l)
{
    var r = new Int8Array(l*2);
    for (var i = 0; i < l; i++)
    { 
        r[i*2 + 0] = x[i].X;
        r[i*2 + 1] = x[i].Y;
    }
    return r;
}

function uglSByte4Array(x,l)
{
    var r = new Int8Array(l*4);
    for (var i = 0; i < l; i++)
    { 
        r[i*4 + 0] = x[i].X;
        r[i*4 + 1] = x[i].Y;
        r[i*4 + 2] = x[i].Z;
        r[i*4 + 3] = x[i].W;
    }
    return r;
}

function uglByte2Array(x,l)
{
    var r = new Uint8Array(l*2);
    for (var i = 0; i < l; i++)
    { 
        r[i*2 + 0] = x[i].X;
        r[i*2 + 1] = x[i].Y;
    }
    return r;
}

function uglByte4Array(x,l)
{
    var r = new Uint8Array(l*4);
    for (var i = 0; i < l; i++)
    { 
        r[i*4 + 0] = x[i].X;
        r[i*4 + 1] = x[i].Y;
        r[i*4 + 2] = x[i].Z;
        r[i*4 + 3] = x[i].W;
    }
    return r;
}
