///////////////////////////////////////////////////////////////////////////////
// Filename: quadtreeclass.cpp
///////////////////////////////////////////////////////////////////////////////
#include "quadtreeclass.h"


QuadTreeClass::QuadTreeClass()
{
    m_parentNode = 0;
    m_renderList = 0;
}


QuadTreeClass::QuadTreeClass(const QuadTreeClass& other)
{
}


QuadTreeClass::~QuadTreeClass()
{
}


void QuadTreeClass::Initialize(float terrainWidth, float nodeSize, float centerX, float centerZ)
{
    // Calculate upper left corner of the terrain mesh based on the center location and the size of the terrain.
    m_upperLeftX = centerX - terrainWidth / 2.0f;
    m_upperLeftZ = centerZ + terrainWidth / 2.0f;

    // Set the width of the terrain.
    m_terrainWidth = terrainWidth;
    m_nodeWidth = nodeSize;

    // Create the parent node for the quad tree.
	m_parentNode = new NodeType;
    m_nodeCount = 0;

	// Recursively build the quad tree based on the mesh dimensions.
	CreateTreeNode(m_parentNode, centerX, centerZ, m_terrainWidth);

	// Create the render list after the nodes have been created and the node count is populated.
	m_renderList = new int[m_nodeCount];

	return;
}


void QuadTreeClass::Shutdown()
{
    // Release the render list.
    if(m_renderList)
    {
        delete [] m_renderList;
        m_renderList = 0;
    }

    // Recursively release the quad tree data.
	if(m_parentNode)
	{
		ReleaseNode(m_parentNode);
		delete m_parentNode;
		m_parentNode = 0;
	}

	return;
}


void QuadTreeClass::Render(FrustumClass* Frustum, float minHeight, float maxHeight)
{
	// Reset the number of triangles that are drawn for this frame.
	m_visibleNodes = 0;

	// Render each node that is visible starting at the parent node and moving down the tree.
	// Note we send in the min and max height from the overall terrain, as the height accuracy doesn't affect culling as much as the width and depth do in a quad tree.
	RenderNode(m_parentNode, Frustum, minHeight, maxHeight);

	return;
}


void QuadTreeClass::CreateTreeNode(NodeType* node, float positionX, float positionZ, float width)
{
    float offsetX, offsetZ, posX, posZ;
    int i, j;


    // Initialize the children nodes of this node to null.
	node->nodes[0] = 0;
	node->nodes[1] = 0;
	node->nodes[2] = 0;
	node->nodes[3] = 0;

	// Store the node position and size.
	node->positionX = positionX;
	node->positionZ = positionZ;
	node->width = width;

    // If there are too many triangles in this node then split it into four equal sized smaller tree nodes.
	if(width > m_nodeWidth)
	{
		for(i=0; i<4; i++)
		{
			// Calculate the position offsets for the new child node.
			offsetX = (((i % 2) < 1) ? -1.0f : 1.0f) * (width / 4.0f);
			offsetZ = (((i % 4) < 2) ? -1.0f : 1.0f) * (width / 4.0f);

            // Create the child nodes.
            node->nodes[i] = new NodeType;

            // Extend the tree starting from this newly created child node using the calculated offsets for the new node's size and location.
            CreateTreeNode(node->nodes[i], (positionX + offsetX), (positionZ + offsetZ), (width / 2.0f));
		}

		return;
	}

    // If there are the right number of triangles then we have determined that this must be a bottom child node.  Therefore we set its parameters and stop traversing downward.
    m_nodeCount++;

    // Calculate the node ID by figuring out what ID would match up to the index of the terrain node array.
    // The quad tree is built non-linear, and the terrain node array was built in a linear fashion.
    // Therefore we need to use this node's position to figure out what terrain node index it would result in.
    posX = positionX - (node->width / 2.0f);
    posZ = positionZ + (node->width / 2.0f);

    posX = posX - m_upperLeftX;
    posZ = posZ - m_upperLeftZ;

    i = (int)(posX / node->width);
    j = (int)(posZ / node->width) * -1;

    node->nodeID = j * (m_terrainWidth / m_nodeWidth) + i;

	return;
}


void QuadTreeClass::ReleaseNode(NodeType* node)
{
	int i;


	// Recursively go down the tree and release the bottom nodes first and move back upwards releasing as we go.
	for(i=0; i<4; i++)
	{
		if(node->nodes[i] != 0)
		{
			ReleaseNode(node->nodes[i]);
		}
	}

	// Delete the four child nodes for this current node.
	for(i=0; i<4; i++)
	{
		if(node->nodes[i])
		{
			delete node->nodes[i];
			node->nodes[i] = 0;
		}
	}

	return;
}


void QuadTreeClass::RenderNode(NodeType* node, FrustumClass* Frustum, float minHeight, float maxHeight)
{
    float maxWidth, maxDepth, minWidth, minDepth;
    int count, i;
    bool result;


	// Get the dimensions of the node for the frustum to use.
	maxWidth = node->positionX + (node->width / 2.0f);
	minWidth = node->positionX - (node->width / 2.0f);
	maxDepth = node->positionZ + (node->width / 2.0f);
	minDepth = node->positionZ - (node->width / 2.0f);

	// Check to see if the node can be viewed.
	result = Frustum->CheckRectangle2(maxWidth, maxHeight, maxDepth, minWidth, minHeight, minDepth);

	// If it can't be seen then none of its children can either so don't continue down the tree, this is where the speed is gained.
	if(!result)
	{
		return;
	}

	// If it can be seen then check all four child nodes to see if they can also be seen.
	count = 0;
	for(i=0; i<4; i++)
	{
		if(node->nodes[i] != 0)
		{
			count++;
			RenderNode(node->nodes[i], Frustum, minHeight, maxHeight);
		}
	}

	// If there weren't any children nodes then there is no need to continue as parent nodes won't contain any triangles to render.
	if(count != 0)
	{
		return;
	}

	// Otherwise if this node can be seen and has triangles in it then render these triangles.  Populate the render list with this node ID so the terrain class object knows to render it.
	m_renderList[m_visibleNodes] = node->nodeID;
	m_visibleNodes++;

	return;
}


void QuadTreeClass::GetNodesDrawn(int& nodesDrawn, int& nodesCulled)
{
    nodesDrawn = m_visibleNodes;
    nodesCulled = m_nodeCount - m_visibleNodes;
    return;
}


void QuadTreeClass::GetRenderList(int* outputRenderList, int& count)
{
    count = m_visibleNodes;
	memcpy(outputRenderList, m_renderList, sizeof(int) * count);
	return;
}
