#pragma OPENCL EXTENSION cl_amd_printf : enable

//http://cas.ee.ic.ac.uk/people/dt10/research/rngs-gpu-mwc64x.html
uint randUint(uint2* rvec) 
{  
    #define A 4294883355U
    uint x=(*rvec).x;
	uint c=(*rvec).y;
    uint res = x ^ c;
    uint hi = mul_hi(x,A);
    x = x*A + c;
    c = hi + (x<c);
    *rvec = (uint2)(x,c);
    return res;
    #undef A
}
inline float randFloat(uint2* rvec) 
{
    return (float)(randUint(rvec)) / (float)(0xFFFFFFFF);
}
inline float randFloatRange(float lo, float hi, uint2* rvec)
{
 return lo + (float)randUint(rvec)/((float)(0xFFFFFFFF)/(hi-lo));
}

float4 matrixVectorMul(float4 row0, float4 row1, float4 row2, float4 vector )
{
	float4 returnVector;
	returnVector.x = dot(row0, vector);
	returnVector.y = dot(row1, vector);
	returnVector.z = dot(row2, vector);
	returnVector.w = vector.w;
	return returnVector;
}

//sphericalCoord.x - r
//sphericalCoord.y - theta
//sphericalCoord.z - pi
float4 cartesianToSpherical(float4 cartesianCoord)
{
	float r = sqrt( cartesianCoord.x * cartesianCoord.x + cartesianCoord.y * cartesianCoord.y + cartesianCoord.z * cartesianCoord.z );
	return (float4)(r, acos( cartesianCoord.z / r ), atan2( cartesianCoord.y, cartesianCoord.x ), 1.0);
}
float4 sphericalToCartesian(float4 sphericalCoord)
{
	return (float4)(sphericalCoord.x*sin(sphericalCoord.y)*cos(sphericalCoord.z), sphericalCoord.x*sin(sphericalCoord.y)*sin(sphericalCoord.z), sphericalCoord.x*cos(sphericalCoord.y), 1.0);
}

float4 getRandomPosOnConeTwo(float maxAngle, float scale, uint2* rvec, float4 row0, float4 row1, float4 row2)
{
	float4 v;
	v.x = randFloatRange(-maxAngle, maxAngle, rvec);
	v.y = randFloatRange(-maxAngle, maxAngle, rvec);
	v.z = 1.0f;//sqrt(1.0f - (v.x * v.x + v.y * v.y));
	v.w = 0.0f;
	v = normalize(v);
	if(v.x < (0.0 + randFloatRange(-0.05, 0.05, rvec))) v.w = 0.50196075;
	else v.w = 0.99999994;
	return matrixVectorMul(row0, row1, row2, v);

}
#define PI 3.14159
float4 getRandomPosOnConeThree(float4 dir, float maxAngle, float scale, uint2* rvec)
{
	float4 s = cartesianToSpherical(dir);

	
	s.x = scale;
	
	float thetaVariation = maxAngle; //randFloatRange(0.0, 3.0, rvec);// * randFloatRange(-1.0, 1.0, rvec);
	float phiVariation = randFloatRange(0.0, 0.1, rvec);
	s.y += thetaVariation;
	s.z += phiVariation;

	
	//(0 <= theta <= pi)
	if(s.y > PI) s.y -= PI;
	else if(s.y < 0) s.y += PI;

	//(0 <= phi < 2*pi)
	if(s.z >= 2*PI ) s.z -= 2*PI;
	else if (s.z < 0) s.z += 2*PI;
	/*

	//printf("T: %f ", s.z);
	volatile float color = s.z/6.0;

	//float p = s.z;
//if(p < 0) p += 6.28;
//if(p > 3.14) p = 3.14-(p-3.14);

*/
	float4 c = sphericalToCartesian(s);
	//c.w = color;
	




//vM.w += 0.50;
//c.w = fmin((float)0.9, c.w);
//c.w = fmax((float)0.30, c.w);


return c;

}

float4 getRandomPosOnCone(float maxAngle, float scale, uint2* rvec, float4 row0, float4 row1, float4 row2, float sRot)
{
float theta = maxAngle;
float phi = (get_local_id(0)*0.608318)+randFloatRange(-0.30, 0.30, rvec);  //randFloatRange(0, 6.28318, rvec);
//float phi = randFloatRange(0, 6.28318, rvec);
//(0 <= phi < 2*pi)
if(phi >= 2*PI ) phi -= 2*PI;
else if (phi < 0) phi += 2*PI;

float4 v = (float4)(cos(phi)*sin(theta), sin(phi)*sin(theta), cos(theta), 1.0);
v *= scale;
float4 vM = matrixVectorMul(row0, row1, row2, v);
//vM.w = 0.50196075;

float p = phi-sRot;
if(p < 0) p += 6.28;


if(p > 3.14) p = 3.14-(p-3.14);
vM.w = p/2.50;
//vM.w += 0.50;
vM.w = fmin((float)0.9, vM.w);
vM.w = fmax((float)0.30, vM.w);

return vM;
}

__kernel void EmitFromCone(__global float4* positions, __global float4* velocities, __global float4* attributes, float4 position, float4 row0, float4 row1, float4 row2, float coneAngle, float coneScale, int workLimit, int segmentEnd, int segmentLength, __global uint2* rvec, float sRot)
{
	if(get_global_id(0) < workLimit)
	{
		uint index = get_global_id(0);
		if(index >= segmentEnd) index -= segmentLength;
		if(attributes[index].x < 0.01f)
		{
			uint2 randomSeed = rvec[index];
			positions[index] = position;
			velocities[index] = getRandomPosOnCone(coneAngle, coneScale, &randomSeed, row0, row1, row2, sRot);
			//velocities[index] = getRandomPosOnConeThree(dir, coneAngle, coneScale, &randomSeed);
			attributes[index] = (float4)(20.0, velocities[index].w, 0.0, 1.0);
			rvec[index] = randomSeed;
		}
		else return;
	}
	else return;
}
/*
__kernel void SpawnParticles(__global float4 *positions, __global float4 *velocities, __global float4 *attributes, __global float4* spawnPositions, __global float4* spawnVelocities, __global float4* spawnAttributes, int workLimit, int segmentEnd, int segmentLength)
{
	if(get_global_id(0) < workLimit)
	{
		uint index = get_global_id(0);
		if(index >= segmentEnd) index -= segmentLength;

		if(attributes[index].x < 0.0001f)
		{
			positions[index] = spawnPositions[index];
			velocities[index] = spawnVelocities[index];
			//TODO: mark segment?
			attributes[index] = spawnAttributes[index];
		}
	}
	else return;
}
*/