// Class: openfl.display.Shader

var $global = typeof window != "undefined" ? window : typeof global != "undefined" ? global : typeof self != "undefined" ? self : this

$global.Object.defineProperty(exports, "__esModule", {value: true});

var __map_reserved = {};

// Imports

var $hxClasses = require("./../../hxClasses_stub").default;
var $import = require("./../../import_stub").default;
function lime_utils_Log() {return require("./../../lime/utils/Log");}
function StringTools() {return require("./../../StringTools");}
function lime_graphics_opengl_GLContextType() {return require("./../../lime/graphics/opengl/GLContextType");}
function openfl_display_ShaderData() {return require("./../../openfl/display/ShaderData");}
function EReg() {return require("./../../EReg");}
function openfl_display_ShaderInput() {return require("./../../openfl/display/ShaderInput");}
function Reflect() {return require("./../../Reflect");}
function openfl_display_ShaderParameter() {return require("./../../openfl/display/ShaderParameter");}
function lime_graphics_opengl__$WebGLContext_WebGLContext_$Impl_$() {return require("./../../lime/graphics/opengl/_WebGLContext/WebGLContext_Impl_");}
function haxe_ds_StringMap() {return require("./../../haxe/ds/StringMap");}

// Constructor

var Shader = function(code) {
	this.byteCode = code;
	this.precisionHint = "full";
	this.__glSourceDirty = true;
	this.__numPasses = 1;
	this.__data = new (openfl_display_ShaderData().default)(code);
}

// Meta

Shader.__name__ = ["openfl","display","Shader"];
Shader.prototype = {
	__clearUseArray: function() {
		var _g = 0;
		var _g1 = this.__paramBool;
		while(_g < _g1.length) {
			var parameter = _g1[_g];
			++_g;
			parameter.__useArray = false;
		}
		var _g2 = 0;
		var _g11 = this.__paramFloat;
		while(_g2 < _g11.length) {
			var parameter1 = _g11[_g2];
			++_g2;
			parameter1.__useArray = false;
		}
		var _g3 = 0;
		var _g12 = this.__paramInt;
		while(_g3 < _g12.length) {
			var parameter2 = _g12[_g3];
			++_g3;
			parameter2.__useArray = false;
		}
	},
	__createGLShader: function(source,type) {
		var shader = this.gl.createShader(type);
		this.gl.shaderSource(shader,source);
		this.gl.compileShader(shader);
		if(this.gl.getShaderParameter(shader,this.gl.COMPILE_STATUS) == 0) {
			var message = type == this.gl.VERTEX_SHADER ? "Error compiling vertex shader" : "Error compiling fragment shader";
			message += "\n" + this.gl.getShaderInfoLog(shader);
			message += "\n" + source;
			(lime_utils_Log().default).error(message,{ fileName : "Shader.hx", lineNumber : 177, className : "openfl.display.Shader", methodName : "__createGLShader"});
		}
		return shader;
	},
	__createGLProgram: function(vertexSource,fragmentSource) {
		var vertexShader = this.__createGLShader(vertexSource,this.gl.VERTEX_SHADER);
		var fragmentShader = this.__createGLShader(fragmentSource,this.gl.FRAGMENT_SHADER);
		var program = this.gl.createProgram();
		var _g = 0;
		var _g1 = this.__paramFloat;
		while(_g < _g1.length) {
			var param = _g1[_g];
			++_g;
			if(param.name.indexOf("Position") > -1 && (StringTools().default).startsWith(param.name,"openfl_")) {
				this.gl.bindAttribLocation(program,0,param.name);
				break;
			}
		}
		this.gl.attachShader(program,vertexShader);
		this.gl.attachShader(program,fragmentShader);
		this.gl.linkProgram(program);
		if(this.gl.getProgramParameter(program,this.gl.LINK_STATUS) == 0) {
			var message = "Unable to initialize the shader program";
			message += "\n" + this.gl.getProgramInfoLog(program);
			(lime_utils_Log().default).error(message,{ fileName : "Shader.hx", lineNumber : 213, className : "openfl.display.Shader", methodName : "__createGLProgram"});
		}
		return program;
	},
	__disable: function() {
		if(this.glProgram != null) {
			this.__disableGL();
		}
	},
	__disableGL: function() {
		var _g = 0;
		var _g1 = this.__paramBool;
		while(_g < _g1.length) {
			var parameter = _g1[_g];
			++_g;
			parameter.__disableGL(this.gl);
		}
		var _g2 = 0;
		var _g11 = this.__paramFloat;
		while(_g2 < _g11.length) {
			var parameter1 = _g11[_g2];
			++_g2;
			parameter1.__disableGL(this.gl);
		}
		var _g3 = 0;
		var _g12 = this.__paramInt;
		while(_g3 < _g12.length) {
			var parameter2 = _g12[_g3];
			++_g3;
			parameter2.__disableGL(this.gl);
		}
		this.gl.bindBuffer(this.gl.ARRAY_BUFFER,null);
		this.gl.bindTexture(this.gl.TEXTURE_2D,null);
		if(this.gl.type == (lime_graphics_opengl_GLContextType().default).OPENGL) {
			this.gl.disable(this.gl.TEXTURE_2D);
		}
	},
	__enable: function() {
		this.__init();
		if(this.glProgram != null) {
			this.__enableGL();
		}
	},
	__enableGL: function() {
		var textureCount = 0;
		var _g = 0;
		var _g1 = this.__inputBitmapData;
		while(_g < _g1.length) {
			var input = _g1[_g];
			++_g;
			if(input.input != null) {
				this.gl.uniform1i(input.index,textureCount);
				++textureCount;
			}
		}
		if(this.gl.type == (lime_graphics_opengl_GLContextType().default).OPENGL && textureCount > 0) {
			this.gl.enable(this.gl.TEXTURE_2D);
		}
	},
	__init: function() {
		if(this.__data == null) {
			this.__data = new (openfl_display_ShaderData().default)(null);
		}
		if(this.__glFragmentSource != null && this.__glVertexSource != null && (this.glProgram == null || this.__glSourceDirty)) {
			this.__initGL();
		}
	},
	__initGL: function() {
		if(this.__glSourceDirty || this.__paramBool == null) {
			this.__glSourceDirty = false;
			this.glProgram = null;
			this.__inputBitmapData = [];
			this.__paramBool = [];
			this.__paramFloat = [];
			this.__paramInt = [];
			this.__processGLData(this.get_glVertexSource(),"attribute");
			this.__processGLData(this.get_glVertexSource(),"uniform");
			this.__processGLData(this.get_glFragmentSource(),"uniform");
		}
		if(this.gl != null && this.glProgram == null) {
			var prefix = "#ifdef GL_ES\n\t\t\t\tprecision " + (this.precisionHint == "full" ? "mediump" : "lowp") + " float;\n\t\t\t\t#endif\n\t\t\t\t";
			var vertex = prefix + this.get_glVertexSource();
			var fragment = prefix + this.get_glFragmentSource();
			var id = vertex + fragment;
			if(Shader.__glPrograms.exists(id)) {
				this.glProgram = Shader.__glPrograms.get(id);
			} else {
				this.glProgram = this.__createGLProgram(vertex,fragment);
				Shader.__glPrograms.set(id,this.glProgram);
			}
			if(this.glProgram != null) {
				var _g = 0;
				var _g1 = this.__inputBitmapData;
				while(_g < _g1.length) {
					var input = _g1[_g];
					++_g;
					if(input.__isUniform) {
						input.index = this.gl.getUniformLocation(this.glProgram,input.name);
					} else {
						input.index = this.gl.getAttribLocation(this.glProgram,input.name);
					}
				}
				var _g2 = 0;
				var _g11 = this.__paramBool;
				while(_g2 < _g11.length) {
					var parameter = _g11[_g2];
					++_g2;
					if(parameter.__isUniform) {
						parameter.index = this.gl.getUniformLocation(this.glProgram,parameter.name);
					} else {
						parameter.index = this.gl.getAttribLocation(this.glProgram,parameter.name);
					}
				}
				var _g3 = 0;
				var _g12 = this.__paramFloat;
				while(_g3 < _g12.length) {
					var parameter1 = _g12[_g3];
					++_g3;
					if(parameter1.__isUniform) {
						parameter1.index = this.gl.getUniformLocation(this.glProgram,parameter1.name);
					} else {
						parameter1.index = this.gl.getAttribLocation(this.glProgram,parameter1.name);
					}
				}
				var _g4 = 0;
				var _g13 = this.__paramInt;
				while(_g4 < _g13.length) {
					var parameter2 = _g13[_g4];
					++_g4;
					if(parameter2.__isUniform) {
						parameter2.index = this.gl.getUniformLocation(this.glProgram,parameter2.name);
					} else {
						parameter2.index = this.gl.getAttribLocation(this.glProgram,parameter2.name);
					}
				}
			}
		}
	},
	__processGLData: function(source,storageType) {
		var lastMatch = 0;
		var position;
		var regex;
		var name;
		var type;
		if(storageType == "uniform") {
			regex = new (EReg().default)("uniform ([A-Za-z0-9]+) ([A-Za-z0-9_]+)","");
		} else {
			regex = new (EReg().default)("attribute ([A-Za-z0-9]+) ([A-Za-z0-9_]+)","");
		}
		while(regex.matchSub(source,lastMatch)) {
			type = regex.matched(1);
			name = regex.matched(2);
			if((StringTools().default).startsWith(name,"gl_")) {
				continue;
			}
			var isUniform = storageType == "uniform";
			if((StringTools().default).startsWith(type,"sampler")) {
				var input = new (openfl_display_ShaderInput().default)();
				input.name = name;
				input.__isUniform = isUniform;
				this.__inputBitmapData.push(input);
				switch(name) {
				case "bitmap":
					this.__bitmap = input;
					break;
				case "openfl_Texture":
					this.__texture = input;
					break;
				default:
				}
				(Reflect().default).setField(this.__data,name,input);
				if(this.__isGenerated) {
					(Reflect().default).setField(this,name,input);
				}
			} else if(!(Reflect().default).hasField(this.__data,name) || (Reflect().default).field(this.__data,name) == null) {
				var parameterType;
				switch(type) {
				case "bool":
					parameterType = "bool";
					break;
				case "bvec2":
					parameterType = "bool2";
					break;
				case "bvec3":
					parameterType = "bool3";
					break;
				case "bvec4":
					parameterType = "bool4";
					break;
				case "dvec2":case "vec2":
					parameterType = "float2";
					break;
				case "dvec3":case "vec3":
					parameterType = "float3";
					break;
				case "double":case "float":
					parameterType = "float";
					break;
				case "ivec3":case "uvec3":
					parameterType = "int3";
					break;
				case "ivec4":case "uvec4":
					parameterType = "int4";
					break;
				case "mat2":case "mat2x2":
					parameterType = "matrix2x2";
					break;
				case "mat2x3":
					parameterType = "matrix2x3";
					break;
				case "mat2x4":
					parameterType = "matrix2x4";
					break;
				case "mat3x2":
					parameterType = "matrix3x2";
					break;
				case "mat3":case "mat3x3":
					parameterType = "matrix3x3";
					break;
				case "mat3x4":
					parameterType = "matrix3x4";
					break;
				case "mat4":case "mat4x4":
					parameterType = "matrix4x4";
					break;
				case "mat4x2":
					parameterType = "matrix4x2";
					break;
				case "mat4x3":
					parameterType = "matrix4x3";
					break;
				case "int":case "uint":
					parameterType = "int";
					break;
				case "ivec2":case "uvec2":
					parameterType = "int2";
					break;
				case "dvec4":case "vec4":
					parameterType = "float4";
					break;
				default:
					parameterType = null;
				}
				var length;
				switch(parameterType) {
				case "bool2":case "float2":case "int2":
					length = 2;
					break;
				case "bool3":case "float3":case "int3":
					length = 3;
					break;
				case "bool4":case "float4":case "int4":case "matrix2x2":
					length = 4;
					break;
				case "matrix3x3":
					length = 9;
					break;
				case "matrix4x4":
					length = 16;
					break;
				default:
					length = 1;
				}
				var arrayLength;
				switch(parameterType) {
				case "matrix2x2":
					arrayLength = 2;
					break;
				case "matrix3x3":
					arrayLength = 3;
					break;
				case "matrix4x4":
					arrayLength = 4;
					break;
				default:
					arrayLength = 1;
				}
				switch(parameterType) {
				case "bool":case "bool2":case "bool3":case "bool4":
					var parameter = new (openfl_display_ShaderParameter().default)();
					parameter.name = name;
					parameter.type = parameterType;
					parameter.__arrayLength = arrayLength;
					parameter.__isBool = true;
					parameter.__isUniform = isUniform;
					parameter.__length = length;
					this.__paramBool.push(parameter);
					if(name == "openfl_HasColorTransform") {
						this.__hasColorTransform = parameter;
					}
					(Reflect().default).setField(this.__data,name,parameter);
					if(this.__isGenerated) {
						(Reflect().default).setField(this,name,parameter);
					}
					break;
				case "int":case "int2":case "int3":case "int4":
					var parameter1 = new (openfl_display_ShaderParameter().default)();
					parameter1.name = name;
					parameter1.type = parameterType;
					parameter1.__arrayLength = arrayLength;
					parameter1.__isInt = true;
					parameter1.__isUniform = isUniform;
					parameter1.__length = length;
					this.__paramInt.push(parameter1);
					(Reflect().default).setField(this.__data,name,parameter1);
					if(this.__isGenerated) {
						(Reflect().default).setField(this,name,parameter1);
					}
					break;
				default:
					var parameter2 = new (openfl_display_ShaderParameter().default)();
					parameter2.name = name;
					parameter2.type = parameterType;
					parameter2.__arrayLength = arrayLength;
					if(arrayLength > 0) {
						var elements = arrayLength * arrayLength;
						var this1;
						if(elements != null) {
							this1 = new Float32Array(elements);
						} else {
							this1 = null;
						}
						parameter2.__uniformMatrix = this1;
					}
					parameter2.__isFloat = true;
					parameter2.__isUniform = isUniform;
					parameter2.__length = length;
					this.__paramFloat.push(parameter2);
					if((StringTools().default).startsWith(name,"openfl_")) {
						switch(name) {
						case "openfl_Alpha":
							this.__alpha = parameter2;
							break;
						case "openfl_ColorMultiplier":
							this.__colorMultiplier = parameter2;
							break;
						case "openfl_ColorOffset":
							this.__colorOffset = parameter2;
							break;
						case "openfl_Matrix":
							this.__matrix = parameter2;
							break;
						case "openfl_Position":
							this.__position = parameter2;
							break;
						case "openfl_TextureCoord":
							this.__textureCoord = parameter2;
							break;
						case "openfl_TextureSize":
							this.__textureSize = parameter2;
							break;
						default:
						}
					}
					(Reflect().default).setField(this.__data,name,parameter2);
					if(this.__isGenerated) {
						(Reflect().default).setField(this,name,parameter2);
					}
				}
			}
			position = regex.matchedPos();
			lastMatch = position.pos + position.len;
		}
	},
	__update: function() {
		if(this.glProgram != null) {
			this.__updateGL();
		}
	},
	__updateFromBuffer: function(shaderBuffer) {
		if(this.glProgram != null) {
			this.__updateGLFromBuffer(shaderBuffer);
		}
	},
	__updateGL: function() {
		var textureCount = 0;
		var _g = 0;
		var _g1 = this.__inputBitmapData;
		while(_g < _g1.length) {
			var input = _g1[_g];
			++_g;
			if(input.input != null) {
				input.__updateGL(this.gl,textureCount);
				++textureCount;
			}
		}
		var _g2 = 0;
		var _g11 = this.__paramBool;
		while(_g2 < _g11.length) {
			var parameter = _g11[_g2];
			++_g2;
			parameter.__updateGL(this.gl);
		}
		var _g3 = 0;
		var _g12 = this.__paramFloat;
		while(_g3 < _g12.length) {
			var parameter1 = _g12[_g3];
			++_g3;
			parameter1.__updateGL(this.gl);
		}
		var _g4 = 0;
		var _g13 = this.__paramInt;
		while(_g4 < _g13.length) {
			var parameter2 = _g13[_g4];
			++_g4;
			parameter2.__updateGL(this.gl);
		}
	},
	__updateGLFromBuffer: function(shaderBuffer) {
		var textureCount = 0;
		var input;
		var inputData;
		var inputFilter;
		var inputMipFilter;
		var inputWrap;
		var _g1 = 0;
		var _g = shaderBuffer.inputCount;
		while(_g1 < _g) {
			var i = _g1++;
			input = shaderBuffer.inputRefs[i];
			inputData = shaderBuffer.inputs[i];
			inputFilter = shaderBuffer.inputFilter[i];
			inputMipFilter = shaderBuffer.inputMipFilter[i];
			inputWrap = shaderBuffer.inputWrap[i];
			if(inputData != null) {
				input.__updateGL(this.gl,textureCount,inputData,inputFilter,inputMipFilter,inputWrap);
				++textureCount;
			}
		}
		if(shaderBuffer.paramDataLength > 0) {
			if(shaderBuffer.paramDataBuffer == null) {
				shaderBuffer.paramDataBuffer = this.gl.createBuffer();
			}
			this.gl.bindBuffer(this.gl.ARRAY_BUFFER,shaderBuffer.paramDataBuffer);
			(lime_graphics_opengl__$WebGLContext_WebGLContext_$Impl_$().default).bufferData(this.gl,this.gl.ARRAY_BUFFER,shaderBuffer.paramData,this.gl.DYNAMIC_DRAW);
		} else {
			this.gl.bindBuffer(this.gl.ARRAY_BUFFER,null);
		}
		var boolIndex = 0;
		var floatIndex = 0;
		var intIndex = 0;
		var boolCount = shaderBuffer.paramBoolCount;
		var floatCount = shaderBuffer.paramFloatCount;
		var paramData = shaderBuffer.paramData;
		var boolRef;
		var floatRef;
		var intRef;
		var hasOverride;
		var overrideBoolValue = null;
		var overrideFloatValue = null;
		var overrideIntValue = null;
		var _g11 = 0;
		var _g2 = shaderBuffer.paramCount;
		while(_g11 < _g2) {
			var i1 = _g11++;
			hasOverride = false;
			if(i1 < boolCount) {
				boolRef = shaderBuffer.paramRefs_Bool[boolIndex];
				var _g3 = 0;
				var _g21 = shaderBuffer.overrideCount;
				while(_g3 < _g21) {
					var j = _g3++;
					if(boolRef.name == shaderBuffer.overrideNames[j]) {
						overrideBoolValue = shaderBuffer.overrideValues[j];
						hasOverride = true;
						break;
					}
				}
				if(hasOverride) {
					boolRef.__updateGL(this.gl,overrideBoolValue);
				} else {
					boolRef.__updateGLFromBuffer(this.gl,paramData,shaderBuffer.paramPositions[i1],shaderBuffer.paramLengths[i1]);
				}
				++boolIndex;
			} else if(i1 < boolCount + floatCount) {
				floatRef = shaderBuffer.paramRefs_Float[floatIndex];
				var _g31 = 0;
				var _g22 = shaderBuffer.overrideCount;
				while(_g31 < _g22) {
					var j1 = _g31++;
					if(floatRef.name == shaderBuffer.overrideNames[j1]) {
						overrideFloatValue = shaderBuffer.overrideValues[j1];
						hasOverride = true;
						break;
					}
				}
				if(hasOverride) {
					floatRef.__updateGL(this.gl,overrideFloatValue);
				} else {
					floatRef.__updateGLFromBuffer(this.gl,paramData,shaderBuffer.paramPositions[i1],shaderBuffer.paramLengths[i1]);
				}
				++floatIndex;
			} else {
				intRef = shaderBuffer.paramRefs_Int[intIndex];
				var _g32 = 0;
				var _g23 = shaderBuffer.overrideCount;
				while(_g32 < _g23) {
					var j2 = _g32++;
					if(intRef.name == shaderBuffer.overrideNames[j2]) {
						overrideIntValue = shaderBuffer.overrideValues[j2];
						hasOverride = true;
						break;
					}
				}
				if(hasOverride) {
					intRef.__updateGL(this.gl,overrideIntValue);
				} else {
					intRef.__updateGLFromBuffer(this.gl,paramData,shaderBuffer.paramPositions[i1],shaderBuffer.paramLengths[i1]);
				}
				++intIndex;
			}
		}
	},
	get_data: function() {
		if(this.__glSourceDirty || this.__data == null) {
			this.__init();
		}
		return this.__data;
	},
	set_data: function(value) {
		return this.__data = value;
	},
	get_glFragmentSource: function() {
		return this.__glFragmentSource;
	},
	set_glFragmentSource: function(value) {
		if(value != this.__glFragmentSource) {
			this.__glSourceDirty = true;
		}
		return this.__glFragmentSource = value;
	},
	get_glVertexSource: function() {
		return this.__glVertexSource;
	},
	set_glVertexSource: function(value) {
		if(value != this.__glVertexSource) {
			this.__glSourceDirty = true;
		}
		return this.__glVertexSource = value;
	}
};
Shader.prototype.__class__ = $hxClasses["openfl.display.Shader"] = Shader;

// Init

Object.defineProperties(Shader.prototype,{ "data" : { get : function () { return this.get_data (); }, set : function (v) { return this.set_data (v); }}, "glFragmentSource" : { get : function () { return this.get_glFragmentSource (); }, set : function (v) { return this.set_glFragmentSource (v); }}, "glVertexSource" : { get : function () { return this.get_glVertexSource (); }, set : function (v) { return this.set_glVertexSource (v); }}});

// Statics


Shader.__glPrograms = new (haxe_ds_StringMap().default)()

// Export

exports.default = Shader;