from genutil import *

allCases = []

VERTEX = "VERTEX"
FRAGMENT = "FRAGMENT"

CASE_FRAGMENT_SHADER_TEMPLATE = """
case ${{NAME}}
	version 300 es
	expect ${{EXPECT}}

	vertex ""
		#version 300 es
		precision highp float;

		in vec4 a_pos;

		void main()
		{
			gl_Position = a_pos;
		}
	""

	fragment ""
		${{SOURCE}}
	""
end"""[1:]

CASE_VERTEX_SHADER_TEMPLATE = """
case ${{NAME}}
	version 300 es
	expect ${{EXPECT}}

	vertex ""
		${{SOURCE}}
	""

	fragment ""
		#version 300 es
		precision highp float;

		layout(location=0) out vec4 o_color;

		void main()
		{
			o_color = vec4(1.0);
		}
	""
end"""[1:]

class UniformBlockCase(ShaderCase):
	def __init__(self, name, shaderType, source, valid):
		self.name = name
		self.shaderType = shaderType
		self.source = source
		self.valid = valid

	def __str__(self):
		if self.shaderType == FRAGMENT:
			sourceParams = {
				"OUTPUT":				"o_color",
				"OUTPUT_DECLARATION":	"layout(location=0) out vec4 o_color;"
			}

			source = fillTemplate(self.source, sourceParams)

			testCaseParams = {
				"NAME":		self.name,
				"SOURCE":	source,
				"EXPECT":	("build_successful" if self.valid else "compile_fail")
			}

			return fillTemplate(CASE_FRAGMENT_SHADER_TEMPLATE, testCaseParams)
		elif self.shaderType == VERTEX:
			sourceParams = {
				"OUTPUT":				"gl_Position",
				"OUTPUT_DECLARATION":	""
			}

			source = fillTemplate(self.source, sourceParams)

			testCaseParams = {
				"NAME":		self.name,
				"SOURCE":	source,
				"EXPECT":	("build_successful" if self.valid else "compile_fail")
			}

			return fillTemplate(CASE_VERTEX_SHADER_TEMPLATE, testCaseParams)

		assert False

def createCases(name, source, valid):
	return [UniformBlockCase(name + "_vertex", VERTEX, source, valid),
			UniformBlockCase(name + "_fragment", FRAGMENT, source, valid)]

repeatShaderTemplate = """
#version 300 es
precision highp float;

${{OUTPUT_DECLARATION}}

uniform UniformBlock
{
	uniform vec4 uniformMember;
} uniformBlock;

void main()
{
	${{OUTPUT}} = uniformBlock.uniformMember;
}"""[1:]

layoutQualifierShaderTemplate = """
#version 300 es
precision highp float;

${{OUTPUT_DECLARATION}}

layout(%s) uniform UniformBlock
{
	vec4 uniformMember;
} uniformBlock;

void main()
{
	${{OUTPUT}} = uniformBlock.uniformMember;
}"""[1:]

layoutGlobalQualifierShaderTemplate = """
#version 300 es
precision highp float;

layout(%s) uniform;

${{OUTPUT_DECLARATION}}

uniform UniformBlock
{
	vec4 uniformMember;
} uniformBlock;

void main()
{
	${{OUTPUT}} = uniformBlock.uniformMember;
}"""[1:]

layoutMemberQualifierShaderTemplate = """
#version 300 es
precision highp float;

${{OUTPUT_DECLARATION}}

uniform UniformBlock
{
	layout(%s) mat4 uniformMember;
} uniformBlock;

void main()
{
	${{OUTPUT}} = uniformBlock.uniformMember[0];
}"""[1:]

layoutMemberVec4QualifierShaderTemplate = """
#version 300 es
precision highp float;

${{OUTPUT_DECLARATION}}

uniform UniformBlock
{
	layout(%s) vec4 uniformMember;
} uniformBlock;

void main()
{
	${{OUTPUT}} = uniformBlock.uniformMember;
}"""[1:]

noInstanceNameShaderTemplate = """
#version 300 es
precision highp float;

${{OUTPUT_DECLARATION}}

uniform UniformBlock
{
	vec4 uniformMember;
};

void main()
{
	${{OUTPUT}} = uniformMember;
}"""[1:]

sameVariableAndInstanceNameShaderTemplate = """
#version 300 es
precision highp float;

${{OUTPUT_DECLARATION}}

uniform UniformBlock
{
	vec4 uniformMember;
} uniformBlock;

void main()
{
	vec4 uniformBlock = vec4(0.0);
	${{OUTPUT}} = uniformBlock;
}"""[1:]

sameVariableAndBlockNameShaderTemplate = """
#version 300 es
precision highp float;

${{OUTPUT_DECLARATION}}

uniform UniformBlock
{
	vec4 uniformMember;
} uniformBlock;

void main()
{
	vec4 UniformBlock = vec4(0.0);
	${{OUTPUT}} = UniformBlock + uniformBlock.uniformMember;
}"""[1:]

repeatedBlockShaderTemplate = """
#version 300 es
precision highp float;

${{OUTPUT_DECLARATION}}

uniform UniformBlock
{
	vec4 uniformMember;
} uniformBlockA;

uniform UniformBlock
{
	vec4 uniformMember;
} uniformBlockB;

void main()
{
	${{OUTPUT}} = uniformBlockA.uniformMember + uniformBlockB.uniformMember;
}"""[1:]

repeatedBlockNoInstanceNameShaderTemplate = """
#version 300 es
precision highp float;

${{OUTPUT_DECLARATION}}

uniform UniformBlock
{
	vec4 uniformMember;
} uniformBlock;

uniform UniformBlock
{
	vec4 uniformMember;
};

void main()
{
	${{OUTPUT}} = uniformBlock.uniformMember + uniformMember;
}"""[1:]

structMemberShaderTemplate = """
#version 300 es
precision highp float;

${{OUTPUT_DECLARATION}}

struct Struct
{
	vec4 uniformMember;
};

uniform UniformBlock
{
	Struct st;
} uniformBlock;

void main()
{
	${{OUTPUT}} = uniformBlock.st.uniformMember;
}"""[1:]

layoutStructMemberQualifierShaderTemplate = """
#version 300 es
precision highp float;

${{OUTPUT_DECLARATION}}

struct Struct
{
	vec4 uniformMember;
};

uniform UniformBlock
{
	layout(%s) Struct st;
} uniformBlock;

void main()
{
	${{OUTPUT}} = uniformBlock.st.uniformMember;
}"""[1:]

longIdentifierBlockNameShaderTemplate = ("""
#version 300 es
precision highp float;

${{OUTPUT_DECLARATION}}

// Total of 1024 characters
uniform """ + ("a" * 1024) + """
{
	vec4 uniformMember;
} uniformBlock;

void main()
{
	${{OUTPUT}} = uniformBlock.uniformMember;
}""")[1:]

longIdentifierInstanceNameShaderTemplate = ("""
#version 300 es
precision highp float;

${{OUTPUT_DECLARATION}}

uniform UniformBlock
{
	vec4 uniformMember;
} """ + ("a" * 1024) + """;
// Total of 1024 characters

void main()
{
	${{OUTPUT}} = """ + ("a" * 1024) + """.uniformMember;
}""")[1:]

underscoreIdentifierInstanceNameShaderTemplate = ("""
#version 300 es
precision highp float;

${{OUTPUT_DECLARATION}}

uniform UniformBlock
{
	vec4 uniformMember;
} _;

void main()
{
	${{OUTPUT}} = _.uniformMember;
}""")[1:]

underscoreIdentifierBlockNameShaderTemplate = ("""
#version 300 es
precision highp float;

${{OUTPUT_DECLARATION}}

uniform _
{
	vec4 uniformMember;
} uniformBlock;

void main()
{
	${{OUTPUT}} = uniformBlock.uniformMember;
}""")[1:]

validCases = (createCases("repeat_interface_qualifier", repeatShaderTemplate, True)
			+ sum([createCases("layout_%s" % qualifier, layoutQualifierShaderTemplate % qualifier, True)
						for qualifier in ["shared", "packed", "std140", "row_major", "column_major"]], [])
			+ createCases("layout_all", layoutQualifierShaderTemplate % "shared, packed, std140, row_major, column_major", True)
			+ createCases("layout_all_8_times", layoutQualifierShaderTemplate % str.join(", ", ["shared", "packed", "std140", "row_major", "column_major"] * 8), True)
			+ sum([createCases("global_layout_%s" % qualifier, layoutGlobalQualifierShaderTemplate % qualifier, True)
						for qualifier in ["shared", "packed", "std140", "row_major", "column_major"]], [])
			+ createCases("global_layout_all", layoutGlobalQualifierShaderTemplate % "shared, packed, std140, row_major, column_major", True)
			+ createCases("global_layout_all_8_times", layoutGlobalQualifierShaderTemplate % str.join(", ", ["shared", "packed", "std140", "row_major", "column_major"] * 8), True)
			+ sum([createCases("member_layout_%s" % qualifier, layoutMemberQualifierShaderTemplate % qualifier, True)
						for qualifier in ["row_major", "column_major"]], [])
			+ sum([createCases("member_layout_%s_vec4" % qualifier, layoutMemberVec4QualifierShaderTemplate % qualifier, True)
						for qualifier in ["row_major", "column_major"]], [])
			+ createCases("member_layout_all", layoutMemberQualifierShaderTemplate % "row_major, column_major", True)
			+ createCases("member_layout_all_8_times", layoutMemberQualifierShaderTemplate % str.join(", ", ["row_major", "column_major"] * 8), True)
			+ createCases("no_instance_name", noInstanceNameShaderTemplate, True)
			+ createCases("same_variable_and_block_name", sameVariableAndBlockNameShaderTemplate, True)
			+ createCases("same_variable_and_instance_name", sameVariableAndInstanceNameShaderTemplate, True)
			+ createCases("struct_member", structMemberShaderTemplate, True)
			+ sum([createCases("struct_member_layout_%s" % qualifier, layoutStructMemberQualifierShaderTemplate % qualifier, True)
						for qualifier in ["row_major", "column_major"]], [])
			+ createCases("struct_member_layout_all", layoutStructMemberQualifierShaderTemplate % "row_major, column_major", True)
			+ createCases("struct_member_layout_all_8_times", layoutStructMemberQualifierShaderTemplate % str.join(", ", ["row_major", "column_major"] * 8), True)
			+ createCases("long_block_name", longIdentifierBlockNameShaderTemplate, True)
			+ createCases("long_instance_name", longIdentifierInstanceNameShaderTemplate, True)
			+ createCases("underscore_block_name", underscoreIdentifierBlockNameShaderTemplate, True)
			+ createCases("underscore_instance_name", underscoreIdentifierInstanceNameShaderTemplate, True))

invalidMemberInterfaceQualifierShaderTemplate = """
#version 300 es
precision highp float;

${{OUTPUT_DECLARATION}}

uniform UniformBlock
{
	%s vec4 uniformMember;
} uniformBlock;

void main()
{
	${{OUTPUT}} = uniformBlock.uniformMember;
}"""[1:]

conflictingInstanceNamesShaderTemplate = """
#version 300 es
precision highp float;

${{OUTPUT_DECLARATION}}

uniform UniformBlockA
{
	vec4 uniformMember;
} uniformBlock;

uniform UniformBlockB
{
	vec4 uniformMember;
} uniformBlock;

void main()
{
	${{OUTPUT}} = uniformBlock.uniformMember;
}"""[1:]

conflictingFunctionAndInstanceNameShaderTemplate = """
#version 300 es
precision highp float;

${{OUTPUT_DECLARATION}}

uniform UniformBlock
{
	vec4 uniformMember;
} uniformBlock;

float uniformBlock (float x)
{
	return x;
}

void main()
{
	${{OUTPUT}} = uniformBlock.uniformMember;
}"""[1:]

conflictingFunctionAndBlockNameShaderTemplate = """
#version 300 es
precision highp float;

${{OUTPUT_DECLARATION}}

uniform UniformBlock
{
	vec4 uniformMember;
} uniformBlock;

float UniformBlock (float x)
{
	return x;
}

void main()
{
	${{OUTPUT}} = uniformBlock.uniformMember;
}"""[1:]

conflictingVariableAndInstanceNameShaderTemplate = """
#version 300 es
precision highp float;

${{OUTPUT_DECLARATION}}

uniform UniformBlock
{
	vec4 uniformMember;
} uniformBlock;

%s vec4 uniformBlock;

void main()
{
	${{OUTPUT}} = uniformBlock.uniformMember;
}"""[1:]

conflictingVariableAndBlockNameShaderTemplate = """
#version 300 es
precision highp float;

${{OUTPUT_DECLARATION}}

uniform UniformBlock
{
	vec4 uniformMember;
} uniformBlock;

%s vec4 UniformBlock;

void main()
{
	${{OUTPUT}} = uniformBlock.uniformMember;
}"""[1:]


matchingInstanceAndBlockNameShaderTemplate = """
#version 300 es
precision highp float;

${{OUTPUT_DECLARATION}}

uniform UniformBlock
{
	vec4 uniformMember;
} UniformBlock;

void main()
{
	${{OUTPUT}} = UniformBlock.uniformMember;
}"""[1:]

referenceUsingBlockNameShaderTemplate = """
#version 300 es
precision highp float;

${{OUTPUT_DECLARATION}}

uniform UniformBlock
{
	vec4 uniformMember;
} uniformBlock;

void main()
{
	${{OUTPUT}} = UniformBlock.uniformMember;
}"""[1:]

emptyBlockShaderTemplate = """
#version 300 es
precision highp float;

${{OUTPUT_DECLARATION}}

uniform UniformBlock
{
} uniformBlock;

void main()
{
	${{OUTPUT}} = vec4(0.0);
}"""[1:]

emptyLayoutShaderTemplate = """
#version 300 es
precision highp float;

${{OUTPUT_DECLARATION}}

layout() uniform UniformBlock
{
	vec4 uniformMember;
} uniformBlock;

void main()
{
	${{OUTPUT}} = uniformBlock.uniformMember;
}"""[1:]

emptyGlobalLayoutShaderTemplate = """
#version 300 es
precision highp float;

layout() uniform;

${{OUTPUT_DECLARATION}}

uniform UniformBlock
{
	vec4 uniformMember;
} uniformBlock;

void main()
{
	${{OUTPUT}} = uniformBlock.uniformMember;
}"""[1:]

emptyMemberLayoutShaderTemplate = """
#version 300 es
precision highp float;

${{OUTPUT_DECLARATION}}

uniform UniformBlock
{
	layout() vec4 uniformMember;
} uniformBlock;

void main()
{
	${{OUTPUT}} = uniformBlock.uniformMember;
}"""[1:]

invalidMemberLayoutShaderTemplate = """
#version 300 es
precision highp float;

${{OUTPUT_DECLARATION}}

uniform UniformBlock
{
	layout(%s) vec4 uniformMember;
} uniformBlock;

void main()
{
	${{OUTPUT}} = uniformBlock.uniformMember;
}"""[1:]

structureDefinitionShaderTemplate = """
#version 300 es
precision highp float;

${{OUTPUT_DECLARATION}}

uniform UniformBlock
{
	struct A
	{
		vec4 uniformMember;
	} a;
} uniformBlock;

void main()
{
	${{OUTPUT}} = uniformBlock.a.uniformMember;
}"""[1:]

samplerShaderTemplate = """
#version 300 es
precision highp float;

${{OUTPUT_DECLARATION}}

uniform UniformBlock
{
	sampler2D sampler;
	vec4 uniformMember;
} uniformBlock;

void main()
{
	${{OUTPUT}} = uniformBlock.uniformMember;
}"""[1:]

missingBlockNameShaderTemplate = """
#version 300 es
precision highp float;

${{OUTPUT_DECLARATION}}

uniform
{
	vec4 uniformMember;
} uniformBlock;

void main()
{
	${{OUTPUT}} = uniformBlock.uniformMember;
}"""[1:]

invalidNumberBlockNameShaderTemplate = """
#version 300 es
precision highp float;

${{OUTPUT_DECLARATION}}

uniform 0UniformBlock
{
	vec4 uniformMember;
} uniformBlock;

void main()
{
	${{OUTPUT}} = uniformBlock.uniformMember;
}"""[1:]

invalidHashBlockNameShaderTemplate = """
#version 300 es
precision highp float;

${{OUTPUT_DECLARATION}}

uniform #UniformBlock
{
	vec4 uniformMember;
} uniformBlock;

void main()
{
	${{OUTPUT}} = uniformBlock.uniformMember;
}"""[1:]

invalidDollarBlockNameShaderTemplate = """
#version 300 es
precision highp float;

${{OUTPUT_DECLARATION}}

uniform $UniformBlock
{
	vec4 uniformMember;
} uniformBlock;

void main()
{
	${{OUTPUT}} = uniformBlock.uniformMember;
}"""[1:]

invalidIdentifierBlockNameShaderTemplate = """
#version 300 es
precision highp float;

${{OUTPUT_DECLARATION}}

uniform gl_UniformBlock
{
	vec4 uniformMember;
} uniformBlock;

void main()
{
	${{OUTPUT}} = uniformBlock.uniformMember;
}"""[1:]

tooLongIdentifierBlockNameShaderTemplate = ("""
#version 300 es
precision highp float;

${{OUTPUT_DECLARATION}}

// Total of 1025 characters
uniform """ + ("a" * 1025) + """
{
	vec4 uniformMember;
} uniformBlock;

void main()
{
	${{OUTPUT}} = uniformBlock.uniformMember;
}""")[1:]

invalidNumberInstanceNameShaderTemplate = """
#version 300 es
precision highp float;

${{OUTPUT_DECLARATION}}

uniform UniformInstance
{
	vec4 uniformMember;
} 0uniformBlock;

void main()
{
	${{OUTPUT}} = 0uniformBlock.uniformMember;
}"""[1:]

invalidHashInstanceNameShaderTemplate = """
#version 300 es
precision highp float;

${{OUTPUT_DECLARATION}}

uniform UniformInstance
{
	vec4 uniformMember;
} #uniformBlock;

void main()
{
	${{OUTPUT}} = #uniformBlock.uniformMember;
}"""[1:]

invalidDollarInstanceNameShaderTemplate = """
#version 300 es
precision highp float;

${{OUTPUT_DECLARATION}}

uniform UniformInstance
{
	vec4 uniformMember;
} $uniformBlock;

void main()
{
	${{OUTPUT}} = $uniformBlock.uniformMember;
}"""[1:]

invalidIdentifierInstanceNameShaderTemplate = """
#version 300 es
precision highp float;

${{OUTPUT_DECLARATION}}

uniform UniformBlock
{
	vec4 uniformMember;
} gl_uniformBlock;

void main()
{
	${{OUTPUT}} = gl_uniformBlock.uniformMember;
}"""[1:]

tooLongIdentifierInstanceNameShaderTemplate = ("""
#version 300 es
precision highp float;

${{OUTPUT_DECLARATION}}

uniform UniformBlock
{
	vec4 uniformMember;
} """ + ("a" * 1025) + """;
// Total of 1025 characters

void main()
{
	${{OUTPUT}} = """ + ("a" * 1025) + """.uniformMember;
}""")[1:]

invalidCases = (
			sum([createCases("member_%s_interface_qualifier" % qualifier, invalidMemberInterfaceQualifierShaderTemplate % qualifier, False)
					for qualifier in ["in", "out", "buffer", "attribute", "varying"]], [])
			+ createCases("conflicting_instance_names", conflictingInstanceNamesShaderTemplate, False)
			+ createCases("conflicting_function_and_instance_name", conflictingFunctionAndInstanceNameShaderTemplate, False)
			+ createCases("conflicting_function_and_block_name", conflictingFunctionAndBlockNameShaderTemplate, False)
			+ sum([createCases("conflicting_%s_and_instance_name" % qualifier, conflictingVariableAndInstanceNameShaderTemplate % qualifier, False)
					for qualifier in ["uniform", "in", "out"]], [])
			+ sum([createCases("conflicting_%s_and_block_name" % qualifier, conflictingVariableAndBlockNameShaderTemplate % qualifier, False)
					for qualifier in ["uniform", "in", "out"]], [])
			+ createCases("matching_instance_and_block_name", matchingInstanceAndBlockNameShaderTemplate, False)
			+ createCases("reference_using_block_name", referenceUsingBlockNameShaderTemplate, False)
			+ createCases("empty_block", emptyBlockShaderTemplate, False)
			+ createCases("empty_layout", emptyLayoutShaderTemplate, False)
			+ createCases("empty_member_layout", emptyMemberLayoutShaderTemplate, False)
			+ createCases("empty_global_layout", emptyGlobalLayoutShaderTemplate, False)
			+ createCases("structure_definition", structureDefinitionShaderTemplate, False)
			+ sum([createCases("member_layout_%s" % qualifier, invalidMemberLayoutShaderTemplate % qualifier, False)
					for qualifier in ["shared", "packed", "std140"]], [])
			+ createCases("missing_block_name", missingBlockNameShaderTemplate, False)
			+ createCases("invalid_number_block_name", invalidNumberBlockNameShaderTemplate, False)
			+ createCases("invalid_hash_block_name", invalidHashBlockNameShaderTemplate, False)
			+ createCases("invalid_dollar_block_name", invalidDollarBlockNameShaderTemplate, False)
			+ createCases("invalid_identifier_block_name", invalidIdentifierBlockNameShaderTemplate, False)
			+ createCases("too_long_block_name", tooLongIdentifierBlockNameShaderTemplate, False)
			+ createCases("invalid_number_instance_name", invalidNumberInstanceNameShaderTemplate, False)
			+ createCases("invalid_hash_instance_name", invalidDollarInstanceNameShaderTemplate, False)
			+ createCases("invalid_dollar_instance_name", invalidDollarInstanceNameShaderTemplate, False)
			+ createCases("invalid_identifier_instance_name", invalidIdentifierInstanceNameShaderTemplate, False)
			+ createCases("repeated_block", repeatedBlockShaderTemplate, False)
			+ createCases("repeated_block_no_instance_name", repeatedBlockNoInstanceNameShaderTemplate, False)
		)

allCases.append(CaseGroup("valid", "Valid uniform interface block syntax tests.", validCases))
allCases.append(CaseGroup("invalid", "Invalid uniform interface block syntax tests.", invalidCases))

if __name__ == "__main__":
	print "Generating shader case files."
	writeAllCases("uniform_block.test", allCases)