// ----------------------------------------------------------------------------
// Blitz-Irrlicht 1.8.3 Wrapper by Ed Musolino 2015
// Based off of Frank Dodd's Irrlicht 1.7 Freebasic Wrapper (2006-2010)
// ----------------------------------------------------------------------------

//  This software is provided 'as-is', without any express Or implied
//  warranty.  In no event will the authors be held liable For any damages
//  arising from the use of this software.

//  Permission is granted To anyone To use this software For any purpose,
//  including commercial applications, And To alter it And redistribute it
//  freely, subject To the following restrictions:

//  1. The Origin of this software must Not be misrepresented; you must not
//     claim that you wrote the original software. If you use this software
//     in a product, an acknowledgement in the product documentation would be
//     appreciated but is Not required.
//  2. Altered source versions must be clearly marked as such, And must Not be
//     misrepresented as being the original software.
//  3. This notice may Not be removed Or altered from any source distribution.
//
// /////////////////////////////////////////////////////////////////////////////

#include "bIrrlicht.h"
#include "CTerrainSphereNode.h"
#include "CTerrainTileSceneNode.h"
#include <io.h>



extern IrrlichtDevice*		device;
extern IVideoDriver*		driver;
extern ISceneManager*		smgr;
extern IGUIEnvironment*		guienv;




extern "C"
{
/*
CreateTerrain ( grid_size[,parent] )
Parameters
grid_size - no of grid squares along each side of terrain, and must be a power of 2 value, e.g. 32, 64, 128, 256, 512, 1024. 
parent (optional) - parent entity of terrain  

Description
Creates a terrain entity and returns its handle. 

The terrain extends from 0,0,0 to grid_size,1,grid_size. 
A terrain is a special type of polygon object that uses real-time level of detail (LOD) to display landscapes which should theoretically consist of over a million polygons with only a few thousand. The way it does this is by constantly rearranging a certain amount of polygons to display high levels of detail close to the viewer and low levels further away. 
This constant rearrangement of polygons is occasionally noticeable however, and is a well-known side-effect of all LOD landscapes. This 'pop-in' effect can be reduced in lots of ways though, as the other terrain help files will go on to explain. 
The optional parent parameter allows you to specify a parent entity for the terrain so that when the parent is moved the child terrain will move with it. However, this relationship is one way; applying movement commands to the child will not affect the parent. 
Specifying a parent entity will still result in the terrain being created at position 0,0,0 rather than at the parent entity's position. 
*/
/* ----------------------------------------------------------------------------
create a terrain node from a highfield map overriding many default parameters
*/
DLL_EXPORT void * BBCALL iCreateTiledTerrain(
								  IImage * image,
								  int tileSize,
								  int dataX, int dataY,
								  float xPos, float yPos, float zPos,
								  float xRot, float yRot, float zRot,
								  float xSca, float ySca, float zSca,
								  int smoothing,
								  int maxLOD,
								  int patchSize )
{
    CTerrainTileSceneNode* terrain = new CTerrainTileSceneNode(
			smgr->getRootSceneNode(),
			smgr,
			device->getFileSystem(),
			0,												// id
			maxLOD,
			(scene::E_TERRAIN_PATCH_SIZE)patchSize,
			core::vector3df(xPos, yPos, zPos),
			core::vector3df(xRot, yRot, zRot),
			core::vector3df(xSca, ySca, zSca));

	if ( image )
	{
		terrain->loadHeightMapImage( image,
				core::position2d<s32>(dataX, dataY),
				tileSize,
				video::SColor(255,255,255,128), 0);
	}

	return terrain;
}

/* ----------------------------------------------------------------------------
set the adjacent tile to this tile node
*/
DLL_EXPORT void BBCALL iAttachTile ( CTerrainTileSceneNode* terrain, CTerrainTileSceneNode* neighbour, int edge )
{
    // attach a neighbouring tile
    terrain->attachTile( neighbour, edge);
}

/* ----------------------------------------------------------------------------
load the height and UV data from an image
*/
DLL_EXPORT void BBCALL iLoadTileStructure ( CTerrainTileSceneNode* terrain, IImage * image, int dataX, int dataY )
{
	if ( image )
	{
		terrain->loadStructure( image, core::position2d<s32>(dataX, dataY), 0, terrain->getTerrainSize());
	}
}

/* ----------------------------------------------------------------------------
load the vertex colors from an image
*/
DLL_EXPORT void BBCALL iLoadTileColor( CTerrainTileSceneNode* terrain, IImage * image, int dataX, int dataY )
{
	if ( image )
	{
		terrain->loadColor( image, core::position2d<s32>(dataX, dataY));
	}
}


/*
LoadTerrain ( file$[,parent] )
Parameters
file$ - filename of image file to be used as height map 
parent (optional) - parent entity of terrain  

Description
Loads a terrain from an image file and returns the terrain's handle. 

The image's red channel is used to determine heights. Terrain is initially the same width and depth as the image,
and 1 unit high. 

Tips on generating nice terrain: 

* Smooth or blur the height map 
* Reduce the y scale of the terrain 
* Increase the x/z scale of the terrain 
* Reduce the camera range 

When texturing an entity, a texture with a scale of 1,1,1 (default) will be the same size as one of the terrain's
grid squares. A texture that is scaled to the same size as the size of the bitmap used to load it or the no. of grid
square used to create it, will be the same size as the terrain. 
The optional parent parameter allows you to specify a parent entity for the terrain so that when the parent is moved
the child terrain will move with it. However, this relationship is one way; applying movement commands to the child
will not affect the parent. 
Specifying a parent entity will still result in the terrain being created at position 0,0,0 rather than at the parent
entity's position. 
A heightmaps dimensions (width and height) must be the same and must be a power of 2, e.g. 32, 64, 128, 256, 512, 1024. 
*/
/* ----------------------------------------------------------------------------
create a terrain node from a highfield map overriding many default parameters
*/
DLL_EXPORT void * BBCALL iLoadTerrain(
								  char *cptrFile,
								  float xPos, float yPos, float zPos,
								  float xRot, float yRot, float zRot,
								  float xSca, float ySca, float zSca,
								  int a, int r, int g, int b,
								  int smoothing,
								  int maxLOD,
								  int patchSize )
{
//	if(!driver->createImageFromFile (cptrFile))
//	{
//		return NULL;
//	}
    return smgr->addTerrainSceneNode(
			cptrFile,								// heightmap
			NULL,									// parent
			-1,										// id
			core::vector3df(xPos,yPos,zPos),		// position
			core::vector3df(xRot,yRot,zRot),		// rotation
			core::vector3df(xSca,ySca,zSca),		// scale
			video::SColor(a,r,g,b),					// vertex color
			(s32)maxLOD,							// the maximum level of detail
			(scene::E_TERRAIN_PATCH_SIZE)patchSize,	// the size of the patches
			(s32)smoothing );						// the number of times to smooth the terrain
	
}


/*
TerrainSize ( terrain )
Parameters
terrain - terrain handle  

Description
Returns the grid size used to create a terrain.  
*/



/*
TerrainDetail terrain,detail_level[,vertex_morph]
Parameters
terrain - terrain handle 
detail_level - detail level of terrain 
vertex_morph (optional) - True to enable vertex morphing of terrain. Defaults to False.  

Description
Sets the detail level for a terrain. This is the number of triangles used to represent the terrain. A typical value is 2000. 

The optional vertex_morph parameter specifies whether to enable vertex morphing. It is recommended you set this to True, as it will reduce the visibility of LOD 'pop-in'.  
*/



/*
TerrainShading terrain,enable
Parameters
terrain - terrain handle 
enable - True to enable terrain shading, False to to disable it. The default mode is False.  

Description
Enables or disables terrain shading. 

Shaded terrains are a little slower than non-shaded terrains, and in some instances can increase the visibility of LOD 'pop-in'. However, the option is there to have shaded terrains if you wish to do so.  
*/




/*
TerrainHeight# ( terrain,grid_x,grid_z )
Parameters
terrain - terrain handle 
grid_x - grid x coordinate of terrain 
grid_z - grid z coordinate of terrain  

Description
Returns the height of the terrain at terrain grid coordinates x,z. The value returned is in the range 0 to 1. 
*/
/* ----------------------------------------------------------------------------
get the height of a point on a terrain tile node
*/
DLL_EXPORT float BBCALL iTiledTerrainHeight ( CTerrainTileSceneNode* terrain, float X, float Z )
{
    // scale the terrain texture
    return terrain->getHeight( X, Z );
}
/* ----------------------------------------------------------------------------
get the height of a point on a terrain
*/
DLL_EXPORT float BBCALL iTerrainHeight( ITerrainSceneNode *terrain, float X, float Z )
{
    return terrain->getHeight( X, Z );
}


/*
ModifyTerrain terrain,grid_x,grid_z,height#[,realtime]
Parameters
terrain - terrain handle 
grid_x - grid x coordinate of terrain 
grid_y - grid y coordinate of terrain 
height# - height of point on terrain. Should be in the range 0-1. 
realtime (optional) - True to modify terrain immediately. False to modify terrain when RenderWorld in next called. Defaults to False.  

Description
Sets the height of a point on a terrain.  
*/



/*
TerrainX# (terrain,x#,y#,z# )
Parameters
terrain - terrain handle 
x# - world x coordinate 
y# - world y coordinate 
z# - world z coordinate  

Description
Returns the interpolated x coordinate on a terrain. 
*/





/*
TerrainY# (terrain,x#,y#,z# )
Parameters
terrain - terrain handle 
x# - world x coordinate 
y# - world y coordinate 
z# - world z coordinate  

Description
Returns the interpolated y coordinate on a terrain. 

Gets the ground's height, basically. 
*/




/*
TerrainZ# (terrain,x#,y#,z# )
Parameters
terrain - terrain handle 
x# - world x coordinate 
y# - world y coordinate 
z# - world z coordinate  

Description
Returns the interpolated z coordinate on a terrain. 
*/







/* ----------------------------------------------------------------------------
scale the texture on a terrain node
*/
DLL_EXPORT void BBCALL iScaleTerrainTexture ( ITerrainSceneNode* terrain, float X, float Y )
{
    // scale the terrain texture
    terrain->scaleTexture( X, Y );
}

/* ----------------------------------------------------------------------------
scale the texture on a terrain node
*/
DLL_EXPORT void BBCALL iScaleSphericalTexture ( ITerrainSphereNode* terrain, float X, float Y )
{
    // scale the terrain texture
    terrain->scaleTexture( X, Y );
}

/* ----------------------------------------------------------------------------
scale the texture on a terrain tile node
*/
DLL_EXPORT void BBCALL iScaleTileTexture ( CTerrainTileSceneNode* terrain, float X, float Y )
{
    // scale the terrain texture
    terrain->scaleTexture( X, Y );
}


/* ----------------------------------------------------------------------------
create a terrain node from a highfield map overriding many default parameters
*/
DLL_EXPORT void * BBCALL iCreateSphericalTerrain(
								  char *cptrFile0,
								  char *cptrFile1,
								  char *cptrFile2,
								  char *cptrFile3,
								  char *cptrFile4,
								  char *cptrFile5,
								  float xPos, float yPos, float zPos,
								  float xRot, float yRot, float zRot,
								  float xSca, float ySca, float zSca,
								  int a, int r, int g, int b,
								  int smoothing,
								  bool spherical,
								  int maxLOD,
								  int patchSize )
{
    CTerrainSphereNode* terrain = new CTerrainSphereNode(
			NULL,
			smgr,
			device->getFileSystem(),
			0,												// id
			maxLOD,
			(scene::E_TERRAIN_PATCH_SIZE)patchSize,
			core::vector3df(xPos, yPos, zPos),
			core::vector3df(xRot, yRot, zRot),
			core::vector3df(xSca, ySca, zSca));

	io::IReadFile * file[CUBE_FACES];
	file[0] = device->getFileSystem()->createAndOpenFile(cptrFile0);
	file[1] = device->getFileSystem()->createAndOpenFile(cptrFile1);
	file[2] = device->getFileSystem()->createAndOpenFile(cptrFile2);
	file[3] = device->getFileSystem()->createAndOpenFile(cptrFile3);
	file[4] = device->getFileSystem()->createAndOpenFile(cptrFile4);
	file[5] = device->getFileSystem()->createAndOpenFile(cptrFile5);
	if (file)
	{
		terrain->loadHeightMap(file, video::SColor(255,255,255,255), 0);
		for ( s32 i = 0; i < CUBE_FACES; i++ )
            file[i]->drop();
	}

//	terrain->drop();

	return (CTerrainSphereNode*)terrain;
}


/* ----------------------------------------------------------------------------
Apply six textures to the surface of a spherical terrain
*/
DLL_EXPORT void BBCALL iSphericalTerrainTexture(
                                            CTerrainSphereNode* terrain,
                                            ITexture *textureTop,
                                            ITexture *textureFront,
                                            ITexture *textureBack,
                                            ITexture *textureLeft,
                                            ITexture *textureRight,
                                            ITexture *textureBottom,
                                            u32 materialIndex )
{
    // set six textures on the spherical terrain one for each face on a cube
    terrain->setTextures( textureTop, textureFront, textureBack,
            textureLeft, textureRight, textureBottom, materialIndex );
}


/* ----------------------------------------------------------------------------
Apply six images to the vertex colors of the faces
*/
DLL_EXPORT void BBCALL iSphericalTerrainVertexColor(
                                            CTerrainSphereNode* terrain,
                                            IImage *imageTop,
                                            IImage *imageFront,
                                            IImage *imageBack,
                                            IImage *imageLeft,
                                            IImage *imageRight,
                                            IImage *imageBottom )
{
    terrain->loadVertexColor( &imageTop );
}

/* ----------------------------------------------------------------------------
Get the surface position of a logical point on the terrain
*/
DLL_EXPORT float BBCALL iSphericalTerrainSurfaceX(
                                            CTerrainSphereNode* terrain,
                                            s32 face,
                                            float logicalX,
                                            float logicalZ)
{
    vector3df pos;
    terrain->getSurfacePosition( face, logicalX, logicalZ, pos );

    return pos.X;
}

DLL_EXPORT float BBCALL iSphericalTerrainSurfaceY(
                                            CTerrainSphereNode* terrain,
                                            s32 face,
                                            float logicalX,
                                            float logicalZ)
{
    vector3df pos;
    terrain->getSurfacePosition( face, logicalX, logicalZ, pos );

    return pos.Y;
}

DLL_EXPORT float BBCALL iSphericalTerrainSurfaceZ(
                                            CTerrainSphereNode* terrain,
                                            s32 face,
                                            float logicalX,
                                            float logicalZ)
{
    vector3df pos;
    terrain->getSurfacePosition( face, logicalX, logicalZ, pos );

    return pos.Z;
}



/* ----------------------------------------------------------------------------
Get the surface position and angle of a logical point on the terrain. This is
not the normal of the surface but essentially the angles to the gravitational
center
*/
DLL_EXPORT float BBCALL iSphericalTerrainAngleRX(
                                            CTerrainSphereNode* terrain,
                                            s32 face,
                                            float logicalX,
                                            float logicalZ )
{
    vector3df pos;
    terrain->getSurfacePosition( face, logicalX, logicalZ, pos );

	return pos.normalize().getHorizontalAngle().X;
}

DLL_EXPORT float BBCALL iSphericalTerrainAngleRY(
                                            CTerrainSphereNode* terrain,
                                            s32 face,
                                            float logicalX,
                                            float logicalZ )
{
    vector3df pos;
    terrain->getSurfacePosition( face, logicalX, logicalZ, pos );

	return pos.normalize().getHorizontalAngle().Y;
}

DLL_EXPORT float BBCALL iSphericalTerrainAngleRZ(
                                            CTerrainSphereNode* terrain,
                                            s32 face,
                                            float logicalX,
                                            float logicalZ )
{
    vector3df pos;
    terrain->getSurfacePosition( face, logicalX, logicalZ, pos );

	return pos.normalize().getHorizontalAngle().Z;
}



/* ----------------------------------------------------------------------------
Convert a co-ordinate into a logical Spherical terrain position
Thanks for the example from "David" posted on Infinity-Universe forum
http://www.infinity-universe.com/Infinity/index.php?option=com_smf&Itemid=75&topic=2305.0.html
*/
DLL_EXPORT float BBCALL iSphericalSurfaceLogicalX(
                                            CTerrainSphereNode* terrain,
                                            float px,
                                            float py,
                                            float pz,
                                            s32 &face )
{
	float logicalX;
	// get the vector from the center of the sphere
	vector3df pos = vector3df( px, py, pz ) - terrain->getPosition();

    float fpx = (float)fabs(pos.X);
	float fpy = (float)fabs(pos.Y);
	float fpz = (float)fabs(pos.Z);

	if( fpx >= fpy )
	{
		if( fpx >= fpz )
            face = ( pos.X > 0.0) ? 3 : 4;  // Left and Right
		else
            face = ( pos.Z > 0.0) ? 1 : 2;  // Front and Back
	}
	else
	{
		if( fpy >= fpz)
            face = ( pos.Y > 0.0) ? 0 : 5;  // Top and Bottom
		else
            face = ( pos.Z > 0.0) ? 1 : 2;  // Front and Back
	}

	switch ( face )
	{
		case 0:	// Top
            /* this gives more accurate results but not accurate enough to
            justify the sin's imo
			logicalX = 1.0 - ( sinf( pos.X / fpy * PI / (PI / 1.5) ) + 1.0 ) / 2.0;
			logicalZ = ( sinf( pos.Z / fpy * PI / (PI / 1.5) ) + 1.0 ) / 2.0; */

			logicalX = 1.0f - ( pos.X / fpy + 1.0f ) / 2.0f;
			break;

		case 1:	// Front
			logicalX = 1.0f - ( pos.X / fpz + 1.0f ) / 2.0f;
			break;

		case 2:	// Back
			logicalX = ( pos.X / fpz + 1.0f ) / 2.0f;
			break;

		case 3:	// Left
			logicalX = ( pos.Z / fpx + 1.0f ) / 2.0f;
			break;

		case 4:	// Right
			logicalX = 1.0f - ( pos.Z / fpx + 1.0f ) / 2.0f;
			break;

		case 5:	// Bottom
			logicalX = ( pos.X / fpy + 1.0f ) / 2.0f;
			break;

        default:
            printf( "logicalX Corordinate Error\n" );
	}
	return logicalX;
}

DLL_EXPORT float BBCALL iSphericalSurfaceLogicalZ(
                                            CTerrainSphereNode* terrain,
                                            float px,
                                            float py,
                                            float pz,
                                            s32 &face )
{
	float logicalZ;
	// get the vector from the center of the sphere
	vector3df pos = vector3df( px, py, pz ) - terrain->getPosition();

    float fpx = (float)fabs(pos.X);
	float fpy = (float)fabs(pos.Y);
	float fpz = (float)fabs(pos.Z);

	if( fpx >= fpy )
	{
		if( fpx >= fpz )
            face = ( pos.X > 0.0) ? 3 : 4;  // Left and Right
		else
            face = ( pos.Z > 0.0) ? 1 : 2;  // Front and Back
	}
	else
	{
		if( fpy >= fpz)
            face = ( pos.Y > 0.0) ? 0 : 5;  // Top and Bottom
		else
            face = ( pos.Z > 0.0) ? 1 : 2;  // Front and Back
	}

	switch ( face )
	{
		case 0:	// Top
            /* this gives more accurate results but not accurate enough to
            justify the sin's imo
			logicalX = 1.0 - ( sinf( pos.X / fpy * PI / (PI / 1.5) ) + 1.0 ) / 2.0;
			logicalZ = ( sinf( pos.Z / fpy * PI / (PI / 1.5) ) + 1.0 ) / 2.0; */

			logicalZ = ( pos.Z / fpy + 1.0f ) / 2.0f;
			break;

		case 1:	// Front
			logicalZ = 1.0f - ( pos.Y / fpz + 1.0f ) / 2.0f;
			break;

		case 2:	// Back
			logicalZ = 1.0f - ( pos.Y / fpz + 1.0f ) / 2.0f;
			break;

		case 3:	// Left
			logicalZ = 1.0f - ( pos.Y / fpx + 1.0f ) / 2.0f;
			break;

		case 4:	// Right
			logicalZ = 1.0f - ( pos.Y / fpx + 1.0f ) / 2.0f;
			break;

		case 5:	// Bottom
			logicalZ = ( pos.Z / fpy + 1.0f ) / 2.0f;
			break;

        default:
            printf( "logicalZ Corordinate Error\n" );
	}
	return logicalZ;
}















}


