//
// Irrlicht Wrapper for Imperative Languages
// Frank Dodd (2006)
//
// Additional Work
// Michael Taupitz (since 2006)
// Marius Eckardt (since 2007)
//
// This wrapper DLL encapsulates a sub-set of the features of the powerful
// Irrlicht 3D Graphics Engine exposing the Object Oriented architecture and
// providing a functional 3D SDK for languages that are not object oriented.
//
// This source was created with the help of the great examples in the Irrlicht
// SDK and the excellent Irrlicht documentation. This software was developed
// using the GCC compiler and Code::Blocks IDE
//

/* ////////////////////////////////////////////////////////////////////////////
includes
*/
#include "irrlichtwrapper.h"


/* ////////////////////////////////////////////////////////////////////////////
global variables
*/

/* ////////////////////////////////////////////////////////////////////////////
external variables
*/
extern IrrlichtDevice *device;
extern IVideoDriver* driver;
extern ISceneManager* smgr;
extern IGUIEnvironment* guienv;


/* ////////////////////////////////////////////////////////////////////////////
Global Function Declarations

all of the below functions are declared as C functions and are exposed without
any mangled names so that they can be easily imported into imperative
languages like FreeBasic
*/
extern "C"
{
/* A Helper-Routine by Christian Clavet and Vitek
*/
void ISceneNode_assignTriangleSelector(scene::ISceneNode* node, scene::ISceneManager* smgr, io::IFileSystem* ifs)
{
 // From vitek
  if (!node || !smgr || !ifs)
    return;

  if (node->getType() == scene::ESNT_OCT_TREE)
  {
    io::IAttributes* attribs = ifs->createEmptyAttributes();
    if (attribs)
    {
      node->serializeAttributes(attribs);

      // get the mesh name out
      core::stringc name = attribs->getAttributeAsString("Mesh");

      attribs->drop();

      // get the animated mesh for the object
      scene::IAnimatedMesh* mesh = smgr->getMesh(name.c_str());
      if (mesh)
      {
        scene::ITriangleSelector* selector =
          smgr->createOctTreeTriangleSelector(mesh->getMesh(0), node, 128);
        node->setTriangleSelector(selector);
        selector->drop();
      }
    }
  }
  else if (node->getType() == scene::ESNT_MESH)
  {
    scene::IMeshSceneNode* msn = (scene::IMeshSceneNode*)node;

    if (msn->getMesh())
    {
      scene::ITriangleSelector* selector =
        smgr->createTriangleSelector(msn->getMesh(), msn);
      msn->setTriangleSelector(selector);
      selector->drop();
    }
  }
  else if (node->getType() == scene::ESNT_ANIMATED_MESH)
  {
    scene::IAnimatedMeshSceneNode* msn = (scene::IAnimatedMeshSceneNode*)node;

    scene::IAnimatedMesh* am = msn->getMesh();
    if (am)
    {
      scene::ITriangleSelector* selector =
        smgr->createTriangleSelector(am->getMesh(0), msn);
      msn->setTriangleSelector(selector);
      selector->drop();
    }
  }
  else if (node->getType() == scene::ESNT_TERRAIN)
  {
    scene::ITerrainSceneNode* tsn = (scene::ITerrainSceneNode*)node;

    scene::ITriangleSelector* selector =
      smgr->createTerrainTriangleSelector(tsn, 0); // probably don't want lod 0 all the time...
    tsn->setTriangleSelector(selector);
    selector->drop();
  }
  else
  {
    // not something we want to collide with
  }
}


/****h* /Collison
* NAME
*    Create Collision groups and check for Collisions
* DESCRIPTION
*   Calls for creating collision groups and for calculating collisions in the scene.
****
*/


/* ////////////////////////////////////////////////////////////////////////////
COLLISION FUNCTIONS
*/
/****f* Collision/IrrGetCollisionGroupFromComplexMesh
* NAME
*    IrrGetCollisionGroupFromComplexMesh - creates a collision selector from a complex Node like a Map
* SYNOPSIS
*    *selector = IrrGetCollisionGroupFromComplexMesh( *ptr2IrrAnimatedMesh, *ptr2IrrSceneNode)
* FUNCTION
*    Creates an optimized triangle selection group from a large complex mesh like a map.
*    This group can then be used in collision functions to collide objects against this node.
*    You need to supply both the mesh the node was created from and the node itself.
* INPUTS
*    *ptr2IrrAnimatedMesh  - Pointer to a Mesh
*    *ptr2IrrSceneNode     - Poinnter to a SceneNode
* RESULT
*    A Pointer to a Scelector for the Created CollisionGroup.
* EXAMPLE
*    *mySelector = IrrGetCollisionGroupFromComplexMesh( *myMesh, *myNode )
****
* This is for creating the PB-Importfile, and avoiding to make everything manual
#PBIMPORT:IrrGetCollisionGroupFromComplexMesh(*ptr2IrrAnimatedMesh.l, *ptr2IrrSceneNode.l)
*/
STATIC_EXPORT void * DLL_EXPORT IrrGetCollisionGroupFromComplexMesh( IAnimatedMesh* mesh, ISceneNode* node )
{
    ITriangleSelector* selector = smgr->createOctTreeTriangleSelector(
            mesh->getMesh(0), node, 128);

    return (void *)selector;
}

/****f* Collision/IrrGetCollisionGroupFromBox
* NAME
*    IrrGetCollisionGroupFromBox - creates a collision selector from triangles in a nodes bounding box
* SYNOPSIS
*    *selector = IrrGetCollisionGroupFromBox(  *ptr2IrrSceneNode)
* FUNCTION
*    Creates a collision object from triangles in a node defined by the nodes bounding box.
* INPUTS
*    *SceneNode     - Poinnter to a SceneNode
* RESULT
*    A Pointer to a Scelector for the BoundeBox.
* EXAMPLE
*    *ObjectSelector = IrrGetCollisionGroupFromBox( *myNode )
****
* This is for creating the PB-Importfile, and avoiding to make everything manual
#PBIMPORT:IrrGetCollisionGroupFromBox(*ptr2IrrSceneNode.l)
*/
STATIC_EXPORT void * DLL_EXPORT IrrGetCollisionGroupFromBox( ISceneNode *  node )
{
    return (void *)smgr->createTriangleSelectorFromBoundingBox( node );
}

/****f* Collision/IrrGetCollisionGroupFromTerrain
* NAME
*    IrrGetCollisionGroupFromTerrain - creates a collision selector from a terrain node
* SYNOPSIS
*    *selector = IrrGetCollisionGroupFromTerrain(  *ptr2IrrTerrainSceneNode, level_of_detail.l)
* FUNCTION
*    Creates a collision object from a terrain node. A higher level of detail improves the collision detection but consumes more resources and can effect the speed of the process.
* INPUTS
*    *ptr2IrrTerrainSceneNode     - Poinnter to a TerrainSceneNode
*   level_of_detail.l             - Level of detail, 0 for highest detail.
* RESULT
*    A Pointer to a Selector for the Terrain
* EXAMPLE
*    *TerrainSelector = IrrGetCollisionGroupFromTerrain( *myNode, 0 )
****
* This is for creating the PB-Importfile, and avoiding to make everything manual
#PBIMPORT:IrrGetCollisionGroupFromTerrain(*ptr2IrrTerrainSceneNode, level_of_detail.l=0)
*/
STATIC_EXPORT void * DLL_EXPORT IrrGetCollisionGroupFromTerrain( ITerrainSceneNode *  node, int level_of_detail )
{
    return ( void *)smgr->createTerrainTriangleSelector( node, level_of_detail );
}

/****f* Collision/IrrCreateCombinedCollisionGroup
* NAME
*    IrrCreateCombinedCollisionGroup - creates a meta-selector that is a group of selector objects
* SYNOPSIS
*    *metaselector = IrrCreateCombinedCollisionGroup(  )
* FUNCTION
*   Creates a collision object that can be used to combine several collision objects together so you
*   could add a couple of maps and a terrain for example. Initially the combined collision object is empty.
*
* RESULT
*    A Pointer to a Meta-Selector
* EXAMPLE
*    *MetaSelector = IrrCreateCombinedCollisionGroup()
****
* This is for creating the PB-Importfile, and avoiding to make everything manual
#PBIMPORT:IrrCreateCombinedCollisionGroup()
*/
STATIC_EXPORT void * DLL_EXPORT IrrCreateCombinedCollisionGroup( void )
{
    return (void *)smgr->createMetaTriangleSelector();
}

/****f* Collision/IrrAddCollisionGroupToCombination
* NAME
*    IrrAddCollisionGroupToCombination - Adds a collision object to group of collision objects
* SYNOPSIS
*    IrrAddCollisionGroupToCombination(*MetaSelector, *Selector  )
* FUNCTION
*   Adds a collision object to group of collision objects
* INPUTS
*   *MetaSelector       - Pointer to a Meta-selector (Created with IrrCreateCombinedCollisionGroup() )
*   *selector           - Pointer to a Triangle-Selector
* EXAMPLE
*    IrrAddCollisionGroupToCombination(*myMetaSelector, *mySelector)
*    IrrAddCollisionGroupToCombination(*myMetaSelector, *myTerrainSelector)
****
* This is for creating the PB-Importfile, and avoiding to make everything manual
#PBIMPORT:IrrAddCollisionGroupToCombination(*ptr2IrrMetaTriangleSelector.l, *ptr2IrrTriangleSelector.l)
*/
STATIC_EXPORT void DLL_EXPORT IrrAddCollisionGroupToCombination( IMetaTriangleSelector *ms, ITriangleSelector *ts )
{
    ms->addTriangleSelector( ts );
}

/****f* Collision/IrrRemoveAllCollisionGroupsFromCombination
* NAME
*    IrrRemoveAllCollisionGroupsFromCombination - remove all triangle selectors from the meta selector
* SYNOPSIS
*    IrrRemoveAllCollisionGroupsFromCombination(*MetaSelector)
* FUNCTION
*   Empty a collision group object so that you can add different collision groups to it
* INPUTS
*   *MetaSelector       - Pointer to a Meta-selector (Created with IrrCreateCombinedCollisionGroup() )
* EXAMPLE
*    IrrRemoveAllCollisionGroupsFromCombination(*myMetaSelector)
****
* This is for creating the PB-Importfile, and avoiding to make everything manual
#PBIMPORT:IrrRemoveAllCollisionGroupsFromCombination(*ptr2IrrMetaTriangleSelector.l)
*/
STATIC_EXPORT void DLL_EXPORT IrrRemoveAllCollisionGroupsFromCombination( IMetaTriangleSelector *ms )
{
    ms->removeAllTriangleSelectors();
}

/* ----------------------------------------------------------------------------
remove a triangle selector from the meta selector group
*/
/**
* This is for creating the PB-Importfile, and avoiding to make everything manual
#PBIMPORT:IrrRemoveCollisionGroupFromCombination(*ptr2IrrMetaTriangleSelector.l, *ptr2IrrTriangleSelector.l)
*/
STATIC_EXPORT void DLL_EXPORT IrrRemoveCollisionGroupFromCombination( IMetaTriangleSelector *ms, ITriangleSelector *ts )
{
    ms->removeTriangleSelector( ts );
}


/* ----------------------------------------------------------------------------
detect the collision point of a ray in the scene with a collision object if a
collision was detected 1 is returned and vector collision contains the
co-ordinates of the point of collision
*/
/**
* This is for creating the PB-Importfile, and avoiding to make everything manual
#PBIMPORT:IrrGetCollisionPoint(*vectorStart.IRR_VECTOR, *vectorEnd.IRR_VECTOR, *ptr2IrrTriangleSelector.l, *vectorCollision.IRR_VECTOR)
*/
STATIC_EXPORT int DLL_EXPORT IrrGetCollisionPoint(
                                    IRR_VECTOR &vectorStart,
                                    IRR_VECTOR &vectorEnd,
                                    ITriangleSelector *ts,
                                    IRR_VECTOR &vectorCollision )
{
    line3d< f32 > ray(  vectorStart.x, vectorStart.y, vectorStart.z,
                        vectorEnd.x, vectorEnd.y, vectorEnd.z );
    vector3df collideVect;
    triangle3df collideTri;
    int collided;
    if ( collided = smgr->getSceneCollisionManager()->getCollisionPoint(
        ray, ts, collideVect, collideTri ))
    {
        vectorCollision.x = collideVect.X;
        vectorCollision.y = collideVect.Y;
        vectorCollision.z = collideVect.Z;
    }
    return collided;
}

/* ----------------------------------------------------------------------------
get a ray that goes from the specified camera and through the screen coordinates
the information is coppied into the supplied start and end vectors
*/
/**
* This is for creating the PB-Importfile, and avoiding to make everything manual
#PBIMPORT:IrrGetRayFromScreenCoordinates(x.l, y.l, *ptr2IrrCamera.l, *vectorStart.IRR_VECTOR, *vectorEnd.IRR_VECTOR)
*/
STATIC_EXPORT void DLL_EXPORT IrrGetRayFromScreenCoordinates(
                                    int x,
                                    int y,
                                    ICameraSceneNode *camera,
                                    IRR_VECTOR &vectorStart,
                                    IRR_VECTOR &vectorEnd )
{
    line3d< f32 > ray;
    position2d< s32 > position( x, y );

    ray = smgr->getSceneCollisionManager()->getRayFromScreenCoordinates(
            position, camera );

    vectorStart.x = ray.start.X;
    vectorStart.y = ray.start.Y;
    vectorStart.z = ray.start.Z;

    vectorEnd.x = ray.end.X;
    vectorEnd.y = ray.end.Y;
    vectorEnd.z = ray.end.Z;
}

/* ----------------------------------------------------------------------------
a ray is cast through the camera and the nearest node that is hit by the ray is
returned. if no node is hit zero is returned for the object
*/
/**
* This is for creating the PB-Importfile, and avoiding to make everything manual
#PBIMPORT:IrrGetCollisionNodeFromCamera(*ptr2IrrCamera.l, bitMask.l=-1)
*/
STATIC_EXPORT void *DLL_EXPORT IrrGetCollisionNodeFromCamera( ICameraSceneNode *camera , s32 bitMask)
{
    return (void *)smgr->getSceneCollisionManager()->getSceneNodeFromCameraBB( camera, bitMask );
}

/* ----------------------------------------------------------------------------
a ray is cast through the supplied coordinates and the nearest node that is hit
by the ray is returned. if no node is hit zero is returned for the object
*/
/**
* This is for creating the PB-Importfile, and avoiding to make everything manual
#PBIMPORT:IrrGetCollisionNodeFromRay(*vectorStart.IRR_VECTOR, *vectorEnd.IRR_VECTOR)
*/
STATIC_EXPORT void *DLL_EXPORT IrrGetCollisionNodeFromRay(
                                    IRR_VECTOR &vectorStart,
                                    IRR_VECTOR &vectorEnd )
{
    line3d< f32 > ray(  vectorStart.x, vectorStart.y, vectorStart.z,
                        vectorEnd.x, vectorEnd.y, vectorEnd.z );
    return (void *)smgr->getSceneCollisionManager()->getSceneNodeFromRayBB( ray );
}

/* ----------------------------------------------------------------------------
a ray is cast through the screen at the specified co-ordinates and the nearest
node that is hit by the ray is returned. if no node is hit zero is returned for
the object
*/
/**
* This is for creating the PB-Importfile, and avoiding to make everything manual
#PBIMPORT:IrrGetCollisionNodeFromScreenCoordinates(x.l, y.l)
*/
STATIC_EXPORT void * DLL_EXPORT IrrGetCollisionNodeFromScreenCoordinates( int x, int y )
{
    position2d< s32 > position( x, y );



    return (void *)smgr->getSceneCollisionManager()->
            getSceneNodeFromScreenCoordinatesBB( position );
}


/**
* This is for creating the PB-Importfile, and avoiding to make everything manual
#PBIMPORT:IrrGetCollisionNodeFromCursor(bitMask.l)
*/
STATIC_EXPORT void * DLL_EXPORT IrrGetCollisionNodeFromCursor(s32 bitMask )
{
    core::line3d<f32> line = smgr->getSceneCollisionManager()->getRayFromScreenCoordinates(device->getCursorControl()->getPosition(), smgr->getActiveCamera());
    return (void *)smgr->getSceneCollisionManager()->getSceneNodeFromRayBB(line,bitMask);
}

/* ----------------------------------------------------------------------------
screen co-ordinates are returned for the position of the specified 3d
co-ordinates if an object were drawn at them on the screen, this is ideal for
drawing 2D bitmaps or text around or on your 3D object on the screen
*/
/**
* This is for creating the PB-Importfile, and avoiding to make everything manual
#PBIMPORT:IrrGetScreenCoordinatesFrom3DPosition( *x.l, *y.l, *vector.IRR_VECTOR)
*/
STATIC_EXPORT void DLL_EXPORT IrrGetScreenCoordinatesFrom3DPosition(
                                    int &x,
                                    int &y,
                                    IRR_VECTOR &vect )
{
    vector3df pos( vect.x, vect.y, vect.z );
    position2d< s32 > position = smgr->getSceneCollisionManager()->
            getScreenCoordinatesFrom3DPosition( pos );
    x = position.X;
    y = position.Y;
}

/* ----------------------------------------------------------------------------
To enable a simple method of Collision-Check with BoundeingBoxes of Nodes
*/
/**
* This is for creating the PB-Importfile, and avoiding to make everything manual
#PBIMPORT:IrrNodesIntersect(*ptr2IrrSceneNode.l,*ptr2IrrSceneNode2.l)
*/
STATIC_EXPORT bool DLL_EXPORT IrrNodesIntersect(ISceneNode* node1,ISceneNode* node2)
{
    return node1->getBoundingBox().intersectsWithBox (node2->getBoundingBox());
}


//

/**
* This is for creating the PB-Importfile, and avoiding to make everything manual
#PBIMPORT:IrrCollisionAssign(*ptr2IrrSceneNode.l)
*/
STATIC_EXPORT void DLL_EXPORT IrrCollisionAssign(ISceneNode* node)
//void ISceneNode_assignTriangleSelectors(scene::ISceneNode* node, scene::ISceneManager* smgr, io::IFileSystem* ifs)
{
    io::IFileSystem* ifs = device->getFileSystem();

    // From Vitek
    // assign a selector for this node
    ISceneNode_assignTriangleSelector(node, smgr, ifs);

  // now recurse on children...
  core::list<scene::ISceneNode*>::ConstIterator begin = node->getChildren().begin();
  core::list<scene::ISceneNode*>::ConstIterator end   = node->getChildren().end();

  for (/**/; begin != end; ++begin)
    IrrCollisionAssign(*begin);
}


/**
* This is for creating the PB-Importfile, and avoiding to make everything manual
#PBIMPORT:IrrCollisionGatherCombined(*ptr2IrrSceneNode.l, *ptr2IrrMetaTriangleSelector.l)
*/
STATIC_EXPORT void DLL_EXPORT IrrCollisionGatherCombined(ISceneNode* node, scene::IMetaTriangleSelector* meta)
//void ISceneNode_gatherTriangleSelectors(scene::ISceneNode* node, scene::IMetaTriangleSelector* meta, const core::aabbox3df& box)
{
// From Vitek
    scene::ITriangleSelector* selector = node->getTriangleSelector();
    // if (selector && node->getTransformedBoundingBox().intersectsWithBox(box))
    meta->addTriangleSelector(selector);

    // now recurse on children...
    core::list<scene::ISceneNode*>::ConstIterator begin = node->getChildren().begin();
    core::list<scene::ISceneNode*>::ConstIterator end   = node->getChildren().end();

    for (/**/; begin != end; ++begin)
        IrrCollisionGatherCombined(*begin, meta);
}

// experimental at first ....


STATIC_EXPORT void DLL_EXPORT IrrGetCollisionResultPosition(
        ITriangleSelector *  selector,
        IRR_VECTOR  & position,
        IRR_VECTOR *  radius,
        IRR_VECTOR *  DirectionAndSpeed,
        IRR_TRIANGLE & tris,
        bool outFalling)
//Missing   f32  slidingSpeed = 0.0005f,
//Missing  const core::vector3df &  gravityDirectionAndSpeed = core::vector3df(0.0f, 0.0f, 0.0f
{
    triangle3df resTris  (    vector3df(tris.pointA.x,tris.pointA.y,tris.pointA.z),
                            vector3df(tris.pointB.x,tris.pointB.y,tris.pointB.z),
                            vector3df(tris.pointC.x,tris.pointC.y,tris.pointC.z) );

    vector3df result = smgr->getSceneCollisionManager()->getCollisionResultPosition(
        selector,
        vector3df(position.x,position.y,position.z),
        vector3df(radius->x, radius->y,radius->z),
        vector3df(DirectionAndSpeed->x, DirectionAndSpeed->y, DirectionAndSpeed->z),
        resTris,
        outFalling);

    position.x = result.X;
    position.y = result.Y;
    position.z = result.Z;

    tris.pointA.x = resTris.pointA.X;
    tris.pointA.y = resTris.pointA.Y;
    tris.pointA.z = resTris.pointA.Z;

    tris.pointB.x = resTris.pointB.X;
    tris.pointB.y = resTris.pointB.Y;
    tris.pointB.z = resTris.pointB.Z;

    tris.pointC.x = resTris.pointC.X;
    tris.pointC.y = resTris.pointC.Y;
    tris.pointC.z = resTris.pointC.Z;



}

/*
        f32 pos_x, f32 pos_y, f32 pos_z,
        f32 rad_x, f32_rad_y, f32 rad_z,
        f32 dirspeed_x,f32 dirspeed_y,f32 dirspeed_z,


  const core::vector3df &  ellipsoidPosition,
  const core::vector3df &  ellipsoidRadius,
  const core::vector3df &  ellipsoidDirectionAndSpeed,
  core::triangle3df &  triout,
  bool &  outFalling,

)
{
}
virtual core::vector3df irr::scene::ISceneCollisionManager::getCollisionResultPosition  (  ITriangleSelector *  selector,
  const core::vector3df &  ellipsoidPosition,
  const core::vector3df &  ellipsoidRadius,
  const core::vector3df &  ellipsoidDirectionAndSpeed,
  core::triangle3df &  triout,
  bool &  outFalling,
  f32  slidingSpeed = 0.0005f,
  const core::vector3df &  gravityDirectionAndSpeed = core::vector3df(0.0f, 0.0f, 0.0f)
 )  [pure virtual]

   Collides a moving ellipsoid with a 3d world with gravity and returns the resulting new position of the ellipsoid. This can be used for moving a character in a 3d world: The character will slide at walls and is able to walk up stairs. The method used how to calculate the collision result position is based on the paper "Improved Collision detection and Response" by Kasper Fauerby.

Parameters:
 selector,:  TriangleSelector containing the triangles of the world. It can be created for example using ISceneManager::createTriangleSelector() or ISceneManager::createTriangleOctTreeSelector().
 ellipsoidPosition,:  Position of the ellipsoid.
 ellipsoidRadius,:  Radius of the ellipsoid.
 ellipsoidDirectionAndSpeed,:  Direction and speed of the movement of the ellipsoid.
 triout,:  Optional parameter where the last triangle causing a collision is stored, if there is a collision.
 outFalling,:  Is set to true if the ellipsoid is falling down, caused by gravity.
 slidingSpeed,:  DOCUMENTATION NEEDED.
 gravityDirectionAndSpeed,:  Direction and force of gravity.

Returns:
Returns the new position of the ellipsoid.

*/

/* ////////////////////////////////////////////////////////////////////////////
all of the above functions are declared as C functions and are exposed without
any mangled names
*/
}
