#include "MetaModels.h"


float uvsample(float ratio)
{
	if (ratio < 0.5)
	{
		return 2 * ratio;
	}
	else
	{
		return 2 - 2 * ratio;
	}
}

MetaModels::MetaModels()
{
	vertexBuffer = nullptr;
	indexBuffer = nullptr;
	upperRadius = DEFAUTLUR;
	downRadius = DEFAULTDR;
	height = DEFAULTH;
	maxTriArea = DEFAULTA;
	startindex = 0;
	gridH = gridV = 1;
}

MetaModels::MetaModels(LShapes model)
{
	m = model;
	vertexBuffer = nullptr;
	indexBuffer = nullptr;
	upperRadius = DEFAUTLUR;
	downRadius = DEFAULTDR;
	height = DEFAULTH;
	maxTriArea = DEFAULTA;
	startindex = 0;
	gridH = gridV = 1;
}

MetaModels::MetaModels(LShapes model, float *vertexbuffer, unsigned int *indexbuffer, unsigned int startindex)
{
	vertexBuffer = vertexbuffer;
	indexBuffer = indexbuffer;
	m = model;
	upperRadius = DEFAUTLUR;
	downRadius = DEFAULTDR;
	height = DEFAULTH;
	maxTriArea = DEFAULTA;
	this->startindex = startindex;
	gridH = gridV = 1;
}

MetaModels::~MetaModels()
{
}

void MetaModels::setUpperRadius(float r)
{
	upperRadius = r;
}

void MetaModels::setDownRadius(float r)
{
	downRadius = r;
}

void MetaModels::setHeight(float h)
{
	height = h;
}

void MetaModels::setUDHA(float ur, float dr, float h, float a)
{
	upperRadius = ur;
	downRadius = dr;
	height = h;
	maxTriArea = a;
}

void MetaModels::setTriArea(float ta)
{
	maxTriArea = ta;
}

void MetaModels::setLength(float l)
{
	length = l;
}

void MetaModels::setWidth(float w)
{
	width = w;
}

void MetaModels::setLenWidth(float l, float w)
{
	length = l;
	width = w;
}

void MetaModels::setGrid(unsigned int h, unsigned int v)
{
	gridH = h;
	gridV = v;
}

void MetaModels::setStartIndex(unsigned int start)
{
	startindex = start;
}

void MetaModels::bindBuffers(float *vbuffer, unsigned int *ibuffer)
{
	vertexBuffer = vbuffer;
	indexBuffer = ibuffer;

}

BFSize MetaModels::getDataSize(LShapes Models)
{
	BFSize size = { 0, 0 };
	int unitSize = sizeof(float)*(3 + 3 + 2);  //position 3f + normal 3f + uv 2f
	float edge = sqrt(maxTriArea);

	if (Models == LBranch)
	{
		//the branch body
		float phi = 2 * PI / MINPERCIRCLE;
		if (edge < 2 * downRadius) phi = acos(1 - edge*edge / (2 * downRadius*downRadius));
		int percircle = MAX(ceil(2 * PI / (phi)), MINPERCIRCLE);
		int floors = MAX(ceil(height / edge), MINFLOORS);
#ifdef PARSESAMPLE
		floors = 1;
#endif
		int count = 0;
		count += percircle * (floors + 1);

		//the two joints ---upper one
		int numOfFloors = MAX(ceil((PI / 2) * upperRadius / edge), MINFLOORS);
		count += percircle * (numOfFloors - 1);
		count += 1;

		//the joint ---downside one
		int numFloors = MAX(ceil((PI / 2)*downRadius / edge), MINFLOORS);
		count += percircle *(numFloors - 1);
		count += 1;

		size.vBuffer = count * unitSize;

		int numTri = 0;
		//number of triangles 
		numTri += 2 * (percircle * (floors + numOfFloors + numFloors - 1));
		size.iBuffer = numTri * 3 * sizeof(unsigned int);
	}
	else
	{
		size.vBuffer = (gridH + 1)*(gridV + 1)*unitSize;
		size.iBuffer = gridH * gridV * 2 * 3 * sizeof(unsigned int);
	}

	return size;
}

void MetaModels::genModels()
{

	//we put the origal point at the center of the buttom of downside semisphere
	if (vertexBuffer!=nullptr&& indexBuffer != nullptr)
	{
		int unit = 8, vert = 0, normal = 3, uv = 6;

		if (m == LBranch)
		{
			//generate a branch
			//construct cylinder body, vertex , normals and uv
			//basic common variables
			float edge = sqrt(maxTriArea);
			float angle = 2 * PI / MINPERCIRCLE;
			if (edge < 2 * downRadius) angle = acos(1 - edge*edge / (2 * downRadius*downRadius));
			int percircle = MAX(ceil(2 * PI / (angle)), MINPERCIRCLE);
			int percircleunit = percircle * unit;
			float theta = 0;
			float thetaincre = 2 * PI / (percircle);

			//cylinder body variables
			int floors = MAX(ceil(height / edge), MINFLOORS);
#ifdef PARSESAMPLE
			floors = 1;
#endif
			float verticle = 0;
			float vertincre = height / floors;
			float radius = downRadius;				//bottom up
			float rincre = -(downRadius - upperRadius) / floors;
			float steep = atan((downRadius - upperRadius) / height);

			//semisphere variables
			int upFloors = MAX(ceil((PI / 2) * upperRadius / edge), MINFLOORS);
			int downFloors = MAX(ceil((PI / 2) * downRadius / edge), MINFLOORS);
			float phiincre = -(PI / 2) / (downFloors);
			float phi = (PI / 2) + phiincre;


			//filling vertex buffer
			unsigned int i = 0, j = 0;
			//insert bottom vertex
			vertexBuffer[j] = 0;
			vertexBuffer[j + 1] = -downRadius;
			vertexBuffer[j + 2] = 0;
			vertexBuffer[j + 3] = 0;
			vertexBuffer[j + 4] = -1;
			vertexBuffer[j + 5] = 0;
			vertexBuffer[j + 6] = 0.5;
			vertexBuffer[j + 7] = 1;

			//now j=1

	//filling bottom semisphere
			while (j < downFloors)
			{
				i = 0;
				theta = 0;
				while (i < percircle)
				{
					vertexBuffer[percircleunit*j + unit + unit*i] = radius * cos(phi) * cos(theta);
					vertexBuffer[percircleunit*j + unit + unit*i + 1] = -radius * sin(phi);
					vertexBuffer[percircleunit*j + unit + unit*i + 2] = radius * cos(phi)* sin(theta);
					vertexBuffer[percircleunit*j + unit + unit*i + normal] = cos(phi)*cos(theta);
					vertexBuffer[percircleunit*j + unit + unit*i + normal + 1] = -sin(phi);
					vertexBuffer[percircleunit*j + unit + unit*i + normal + 2] = cos(phi)*sin(theta);
					vertexBuffer[percircleunit*j + unit + unit*i + uv] = uvsample(float(i) / percircle);
					vertexBuffer[percircleunit*j + unit + unit*i + uv + 1] = 1 - phi / (PI / 2);
					++i;
					theta += thetaincre;
				}
				++j;
				phi += phiincre;
			}			//now j=1+downFloors
			radius += rincre;
			verticle += vertincre;
			//filling the cylinder body
			while (j < downFloors + floors - 1)
			{
				i = 0;
				theta = 0;
				while (i < percircle)
				{
					vertexBuffer[percircleunit*j + unit + unit*i] = radius * cos(theta);
					vertexBuffer[percircleunit*j + unit + unit*i + 1] = verticle;
					vertexBuffer[percircleunit*j + unit + unit*i + 2] = radius * sin(theta);
					vertexBuffer[percircleunit*j + unit + unit*i + normal] = cos(steep)*cos(theta);
					vertexBuffer[percircleunit*j + unit + unit*i + normal + 1] = sin(steep);
					vertexBuffer[percircleunit*j + unit + unit*i + normal + 2] = cos(steep)*sin(theta);
					vertexBuffer[percircleunit*j + unit + unit*i + uv] = uvsample(float(i) / percircle);
					vertexBuffer[percircleunit*j + unit + unit*i + uv + 1] = 1 - float(j - downFloors + 1) / (floors);
					++i;
					theta += thetaincre;
				}
				++j;
				verticle += vertincre;
				radius += rincre;
			}			//now j=downFloors+floors

			//filling the upper semisphere
			phi = 0;
			phiincre = (PI / 2) / (upFloors);
			radius = upperRadius;
			while (j < downFloors + floors + upFloors - 1)
			{
				i = 0;
				theta = 0;
				while (i < percircle)
				{
					vertexBuffer[percircleunit*j + unit + unit*i] = radius * cos(phi) * cos(theta);
					vertexBuffer[percircleunit*j + unit + unit*i + 1] = height + radius * sin(phi);
					vertexBuffer[percircleunit*j + unit + unit*i + 2] = radius * cos(phi)* sin(theta);
					vertexBuffer[percircleunit*j + unit + unit*i + normal] = cos(phi)*cos(theta);
					vertexBuffer[percircleunit*j + unit + unit*i + normal + 1] = sin(phi);
					vertexBuffer[percircleunit*j + unit + unit*i + normal + 2] = cos(phi)*sin(theta);
					vertexBuffer[percircleunit*j + unit + unit*i + uv] = uvsample(float(i) / percircle);
					vertexBuffer[percircleunit*j + unit + unit*i + uv + 1] = phi / (PI / 2);
					++i;
					theta += thetaincre;
				}
				phi += phiincre;
				j++;
			}

			//filling the top vertex
			vertexBuffer[percircleunit*j + unit] = 0;
			vertexBuffer[percircleunit*j + unit + 1] = height + upperRadius;
			vertexBuffer[percircleunit*j + unit + 2] = 0;
			vertexBuffer[percircleunit*j + unit + 3] = 0;
			vertexBuffer[percircleunit*j + unit + 4] = 1;
			vertexBuffer[percircleunit*j + unit + 5] = 0;
			vertexBuffer[percircleunit*j + unit + 6] = 0.5;
			vertexBuffer[percircleunit*j + unit + 7] = 1;


			//filling the index buffer
			j = 0;
			i = 0;
			while (i < percircle)
			{
				indexBuffer[j++] = startindex;
				indexBuffer[j++] = startindex + (i + percircle - 1) % percircle + 1;
				indexBuffer[j++] = startindex + i + 1;
				i++;
			}
			unsigned int k = 0;
			while (k < downFloors + floors + upFloors - 2)
			{
				i = 0;
				while (i < percircle)
				{
					//upper triangle
					indexBuffer[j++] = startindex + 1 + k*percircle + i;
					indexBuffer[j++] = startindex + 1 + (k + 1)*percircle + (i + percircle - 1) % percircle;
					indexBuffer[j++] = startindex + 1 + (k + 1)*percircle + i;
					//downside triangle
					indexBuffer[j++] = startindex + 1 + k*percircle + i;
					indexBuffer[j++] = startindex + 1 + (k + 1)*percircle + i;
					indexBuffer[j++] = startindex + 1 + k*percircle + (i + 1) % percircle;
					i++;
				}
				k++;
			}
			i = 0;
			while (i < percircle)
			{
				indexBuffer[j++] = startindex + 1 + k*percircle + i;
				indexBuffer[j++] = startindex + 1 + (k + 1)*percircle;
				indexBuffer[j++] = startindex + 1 + k*percircle + (i + 1) % percircle;
				i++;
			}
		}
		else
		{
			//generate a leaf
			//filling the vertexBuffer
			float xpos, ypos;
			float xincre = width / gridH;
			float yincre = length / gridV;
			xpos = -width / 2;
			ypos = 0;
			for (int i = 0; i < gridV+1; i++)
			{
				xpos = -width / 2;
				for (int j = 0; j < gridH + 1; j++)
				{
					vertexBuffer[i*(gridH + 1)*unit + j*unit] = xpos;
					vertexBuffer[i*(gridH + 1)*unit + j*unit + 1] = ypos;
					vertexBuffer[i*(gridH + 1)*unit + j*unit + 2] = 0;
					vertexBuffer[i*(gridH + 1)*unit + j*unit + normal] = 0;
					vertexBuffer[i*(gridH + 1)*unit + j*unit + normal + 1] = 0;
					vertexBuffer[i*(gridH + 1)*unit + j*unit + normal + 2] = 1;
					vertexBuffer[i*(gridH + 1)*unit + j*unit + uv] = (xpos + width / 2) / width;
					vertexBuffer[i*(gridH + 1)*unit + j*unit + uv + 1] = 1 - ypos / length;
					xpos += xincre;
				}
				ypos += yincre;
			}
			//filling the indexBuffer
			int k = 0;
			for (size_t i = 0; i < gridV; i++)
			{
				for (size_t j = 0; j < gridH; j++)
				{
					//the triangle /_|
					indexBuffer[k++] = startindex + i*(gridH + 1) + j;
					indexBuffer[k++] = startindex + i*(gridH + 1) + j + 1;
					indexBuffer[k++] = startindex + (i + 1)*(gridH + 1) + j + 1;
					//the triangle |-/
					indexBuffer[k++] = startindex + i*(gridH + 1) + j;
					indexBuffer[k++] = startindex + (i + 1)*(gridH + 1) + j + 1;
					indexBuffer[k++] = startindex + (i + 1)*(gridH + 1) + j;
				}
			}
		}

	}
	else
	{
		std::cout << "Invalid vertexbuffer or indexbuffer" << std::endl;
	}
}

void MetaModels::saveAsObj()
{
	BFSize s = getDataSize(m);
	int count = 0;
	int unitSize = sizeof(float)*(3 + 3 + 2);
	int vertex = s.vBuffer / unitSize;
	int index = s.iBuffer / (3 * sizeof(unsigned int));
	std::ofstream file;
	file.open("metashape.obj");
	while (count < vertex)
	{
		file << "v " << vertexBuffer[count * 8 + 0] << " " << vertexBuffer[count * 8 + 1] << " " << vertexBuffer[count * 8 + 2] << std::endl;
		count++;
	}
	count = 0;
	while (count < vertex)
	{
		file << "vn " << vertexBuffer[count * 8 + 3] << " " << vertexBuffer[count * 8 + 4] << " " << vertexBuffer[count * 8 + 5] << std::endl;
		count++;
	}
	count = 0;
	while (count < vertex)
	{
		file << "vt " << vertexBuffer[count * 8 + 6] << " " << vertexBuffer[count * 8 + 7] << std::endl;
		count++;
	}
	count = 0;
	while (count < index)
	{
		file << "f "
			<< indexBuffer[count * 3 + 0] + 1 << "/" << indexBuffer[count * 3 + 0] + 1 << "/" << indexBuffer[count * 3 + 0] + 1 << " "
			<< indexBuffer[count * 3 + 1] + 1 << "/" << indexBuffer[count * 3 + 1] + 1 << "/" << indexBuffer[count * 3 + 1] + 1 << " "
			<< indexBuffer[count * 3 + 2] + 1 << "/" << indexBuffer[count * 3 + 2] + 1 << "/" << indexBuffer[count * 3 + 2] + 1 << std::endl;
		count++;
	}
	file.close();
}

