/*
-----------------------------------------------------------------------------
This source file is part of OpenSpace3D
For the latest info, see http://www.openspace3d.com

Copyright (c) 2012 I-maginer

This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option) any later
version.

This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along with
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place - Suite 330, Boston, MA 02111-1307, USA, or go to
http://www.gnu.org/copyleft/lesser.txt

-----------------------------------------------------------------------------
*/

/**************************************************
 Ogre 3D Newton physics library
 Version: 1.0
 Author: Bastien BOURINEAU
 Last update: 16.12.2008

**************************************************/

// arch     : SO3_PA_DEFAULT SO3_PA_MEDIUM SO3_PA_BEST
// solver   : SO3_SM_EXACT 1 2 4 8 
// friction : SO3_FM_EXACT SO3_FM_ADAPTATIVE 

struct V3Dphysics =
[
  V3DPHYS_iFps          : I,
  V3DPHYS_iArchMode     : I,
  V3DPHYS_iSolverMode   : I,
  V3DPHYS_iFrictionMode : I,
  
  V3DPHYS_bEnable       : I

] mkV3Dphysics;;

proto V3DphysCreateBodyEllipsoid = fun [SO3_OBJECT [F F F]] SO3_PHYSICBODY;;
proto V3DphysCreateBodyBox = fun [SO3_OBJECT [F F F]] SO3_PHYSICBODY;;
//proto V3DphysCreateBodyFluid = fun [SO3_OBJECT] SO3_PHYSICBODY;;


fun V3DphysEnablePhysic(sessionstr, state)=
  if (sessionstr.V3D_physics.V3DPHYS_bEnable == 1 && state == 1) then nil else
  (
    set sessionstr.V3D_physics.V3DPHYS_bEnable = state;
    SO3WorldSetEnabled (V3DgetSession sessionstr) state;
  );
  0;;


fun V3DphysResetPhysic(sessionstr)=
  SO3WorldReset (V3DgetSession sessionstr);
  0;;


fun V3DphysGetState(sessionstr)= sessionstr.V3D_physics.V3DPHYS_bEnable;;


// deprecated
fun V3DphysSetWorld(sessionstr, vec)=
  0;;

// deprecated
fun V3DphysGetWorld(sessionstr)= [0.0 0.0 0.0];;


fun V3DphysSetFps(sessionstr, fps)=
  if fps == nil then nil else
  (
    set sessionstr.V3D_physics.V3DPHYS_iFps = fps;
    SO3WorldSetFPS (V3DgetSession sessionstr) sessionstr.V3D_physics.V3DPHYS_iFps;
  );
  0;;


fun V3DphysGetFps(sessionstr)= sessionstr.V3D_physics.V3DPHYS_iFps;;


fun V3DphysSetGravity(sessionstr, g)=
  SO3WorldSetGravity (V3DgetSession sessionstr) (multiplyVectorF g [(itof iGlobalUnit) (itof iGlobalUnit) (itof iGlobalUnit)]);
  0;;


fun V3DphysGetGravity(sessionstr)= divideVectorF (SO3WorldGetGravity (V3DgetSession sessionstr)) [(itof iGlobalUnit) (itof iGlobalUnit) (itof iGlobalUnit)];;


/*fun V3DphysSetArchitectureMode(sessionstr, mode)=
  if mode == nil then nil else
  (
    set sessionstr.V3D_physics.V3DPHYS_iArchMode = mode;
    SO3WorldSetPlateformArchitecture (V3DgetSession sessionstr) sessionstr.V3D_physics.V3DPHYS_iArchMode;
  );
  0;;


fun V3DphysGetArchitectureMode(sessionstr)= sessionstr.V3D_physics.V3DPHYS_iArchMode;;


fun V3DphysGetArchitectureModeName(sessionstr)=
  let [SO3_PA_DEFAULT "default"]::[SO3_PA_MEDIUM "medium"]::[SO3_PA_BEST "best"]::nil -> l in
    switch l (V3DphysGetArchitectureMode sessionstr);;


fun V3DphysSetArchitectureModeByName(sessionstr, smode)=
  let ["default" SO3_PA_DEFAULT]::["medium" SO3_PA_MEDIUM]::["best" SO3_PA_BEST]::nil -> l in
    V3DphysSetArchitectureMode sessionstr (switchstri l smode);;
*/

fun V3DphysSetSolverMode(sessionstr, mode)=
  let if mode == nil then 1 else mode -> mode in
  (
    set sessionstr.V3D_physics.V3DPHYS_iSolverMode = mode;
    SO3WorldSetSolverModel (V3DgetSession sessionstr) sessionstr.V3D_physics.V3DPHYS_iSolverMode;
  );
  0;;


fun V3DphysGetSolverMode(sessionstr)= sessionstr.V3D_physics.V3DPHYS_iSolverMode;;


fun V3DphysGetSolverModeName(sessionstr)=
  let [SO3_SM_EXACT "exact"]::[1 "iterative one passes"]::[2 "iterative two passes"]::[4 "iterative four passes"]::[8 "iterative eight passes"]::nil -> l in
    switch l (V3DphysGetSolverMode sessionstr);;


fun V3DphysSetSolverModeByName(sessionstr, smode)=
  let ["exact" SO3_SM_EXACT]::["iterative one passes" 1]::["iterative two passes" 2]::["iterative four passes" 4]::["iterative eight passes" 8]::nil -> l in
    V3DphysSetSolverMode sessionstr (switchstri l smode);;

/*  
fun V3DphysSetFluidProperties(sessionstr, body, p)=
	let p -> [density viscosity accviscosity] in
  SO3BodySetFluidProperties (V3DgetSession sessionstr) body density viscosity accviscosity;
  0;;
*/


fun V3DgetDefaultMaterial(sessionstr)=
  let SO3PhysicsGetMaterial (V3DgetSession sessionstr) "default" -> mat in
  if (mat == nil) then 
    SO3PhysicsMaterialCreate (V3DgetSession sessionstr) "default"
  else
    mat;;


fun V3DgetAvatarMaterial(sessionstr)=
  let SO3PhysicsGetMaterial (V3DgetSession sessionstr) "avatar" -> matav in
  if (matav != nil) then
    matav
  else
  let V3DgetDefaultMaterial sessionstr -> matdef in
  (
    set matav = SO3PhysicsMaterialCreate (V3DgetSession sessionstr) "avatar";
    
    SO3PhysicsMaterialSetDefaultElasticity matdef matav 0.0;
    SO3PhysicsMaterialSetDefaultSoftness matdef matav 0.0;
    SO3PhysicsMaterialSetSurfaceThickness matdef matav 0.01;
    SO3PhysicsMaterialSetContiniousCollisionMode matdef matav 1;
    SO3PhysicsMaterialSetDefaultCollidable matdef matav 1;
    matav;
  );;


fun V3DphysSetBodyMaterial(sessionstr, body, matname)=
  let if ((matname == nil) || (!strcmp "" (strtrim matname))) then "default" else matname -> matname in
  let SO3PhysicsGetMaterial (V3DgetSession sessionstr) matname -> mat in
  let if mat == nil then SO3PhysicsMaterialCreate (V3DgetSession sessionstr) matname else mat -> mat in
  (
    SO3BodySetMaterial body mat;
    mat;
  );;


fun V3DphysCreateMaterialOnNode(sessionstr, node)=
  let SO3ObjectGetName node -> name in
  let SO3SceneNodeGetBody node -> body in
  if (body == nil) then nil else
  let SO3PhysicsMaterialCreate (V3DgetSession sessionstr) strcat name "_physmat" -> mat in
  (
    SO3BodySetMaterial body mat;
    mat;
  );;


fun V3DphysGetUniformBoundingBox(node)=
  if ((SO3ObjectGetType node) != SO3_TYPE_ENTITY) then [0.1 0.1 0.1] else
  (
    let SO3ObjectGetGlobalScale node -> oscale in
    (
      SO3ObjectSetGlobalScale node [1.0 1.0 1.0];
      let SO3ObjectGetBoundingBoxInfo node 0 -> [bsize _ _] in
      (
        SO3ObjectSetGlobalScale node oscale;
        bsize
      )
    )
  );;


fun V3DphysCreateBodyEllipsoid(node, vec)=
  let if vec == nil then V3DphysGetUniformBoundingBox node else vec -> [x y z] in
  let [(if x <=. 0.0 then 0.01 else x/.2.0) (if y <=. 0.0 then 0.01 else y/.2.0) (if z <=. 0.0 then 0.01 else z/.2.0)] -> vec in
    SO3BodyCreateEllipsoid node vec;;


fun V3DphysCreateBodyBox(node, vec)=
  let if vec == nil then V3DphysGetUniformBoundingBox node else vec -> [x y z] in
  let [(if x <=. 0.0 then 0.01 else x) (if y <=. 0.0 then 0.01 else y) (if z <=. 0.0 then 0.01 else z)] -> vec in
    SO3BodyCreateBox node vec;;


fun V3DphysCreateBodyCylinder(node, vec)=
  let if vec == nil then V3DphysGetUniformBoundingBox node else vec -> [x y z] in
  let [(if x <=. 0.0 then 0.01 else x) (if y <=. 0.0 then 0.01 else y) (if z <=. 0.0 then 0.01 else z)] -> [x y z] in
    SO3BodyCreateCylinder node (x /. 2.0) y;;


fun V3DphysCreateBodyChamferCylinder(node, vec)=
  let if vec == nil then V3DphysGetUniformBoundingBox node else vec -> [x y z] in
  let [(if x <=. 0.0 then 0.01 else x) (if y <=. 0.0 then 0.01 else y) (if z <=. 0.0 then 0.01 else z)] -> [x y z] in
    SO3BodyCreateChamferCylinder node (x /. 2.0) y;;


fun V3DphysCreateBodyCapsule(node, vec)=
  let if vec == nil then V3DphysGetUniformBoundingBox node else vec -> [x y z] in
  let [(if x <=. 0.0 then 0.01 else x) (if y <=. 0.0 then 0.01 else y) (if z <=. 0.0 then 0.01 else z)] -> [x y z] in
    SO3BodyCreateCapsule node (x /. 2.0) y;;


fun V3DphysCreateBodyCone(node, vec)=
  let if vec == nil then V3DphysGetUniformBoundingBox node else vec -> [x y z] in
  let [(if x <=. 0.0 then 0.01 else x) (if y <=. 0.0 then 0.01 else y) (if z <=. 0.0 then 0.01 else z)] -> [x y z] in
    SO3BodyCreateCone node (x /. 2.0) y;;


fun V3DphysCreateBodyPyramid(node, vec)=
  let if vec == nil then V3DphysGetUniformBoundingBox node else vec -> [x y z] in
  let [(if x <=. 0.0 then 0.01 else x) (if y <=. 0.0 then 0.01 else y) (if z <=. 0.0 then 0.01 else z)] -> vec in
    SO3BodyCreatePyramid node vec;;


fun V3DphysCreateBodyEllipsoidExt(node, vec, offsetp, offseta)=
  let if vec == nil then V3DphysGetUniformBoundingBox node else vec -> [x y z] in
  let [(if x <=. 0.0 then 0.01 else x/.2.0) (if y <=. 0.0 then 0.01 else y/.2.0) (if z <=. 0.0 then 0.01 else z/.2.0)] -> vec in
  let offseta -> [ax ay az] in
  let if (offseta != nil) then SO3MathsEulerYXZToQuat [SO3MathsDegreeToRadian ax SO3MathsDegreeToRadian ay SO3MathsDegreeToRadian az] else nil -> offsetq in
    SO3BodyCreateEllipsoidExt node vec offsetp offsetq;;


fun V3DphysCreateBodyBoxExt(node, vec, offsetp, offseta)=
  let if vec == nil then V3DphysGetUniformBoundingBox node else vec -> [x y z] in
  let [(if x <=. 0.0 then 0.01 else x) (if y <=. 0.0 then 0.01 else y) (if z <=. 0.0 then 0.01 else z)] -> vec in
  let offseta -> [ax ay az] in
  let if (offseta != nil) then SO3MathsEulerYXZToQuat [SO3MathsDegreeToRadian ax SO3MathsDegreeToRadian ay SO3MathsDegreeToRadian az] else nil -> offsetq in
    SO3BodyCreateBoxExt node vec offsetp offsetq;;


fun V3DphysCreateBodyCylinderExt(node, vec, offsetp, offseta)=
  let if vec == nil then V3DphysGetUniformBoundingBox node else vec -> [x y z] in
  let [(if x <=. 0.0 then 0.01 else x) (if y <=. 0.0 then 0.01 else y) (if z <=. 0.0 then 0.01 else z)] -> [x y z] in
  let offseta -> [ax ay az] in
  let if (offseta != nil) then SO3MathsEulerYXZToQuat [SO3MathsDegreeToRadian ax SO3MathsDegreeToRadian ay SO3MathsDegreeToRadian az] else nil -> offsetq in
    SO3BodyCreateCylinderExt node (x /. 2.0) y offsetp offsetq;;


fun V3DphysCreateBodyChamferCylinderExt(node, vec, offsetp, offseta)=
  let if vec == nil then V3DphysGetUniformBoundingBox node else vec -> [x y z] in
  let [(if x <=. 0.0 then 0.01 else x) (if y <=. 0.0 then 0.01 else y) (if z <=. 0.0 then 0.01 else z)] -> [x y z] in
  let offseta -> [ax ay az] in
  let if (offseta != nil) then SO3MathsEulerYXZToQuat [SO3MathsDegreeToRadian ax SO3MathsDegreeToRadian ay SO3MathsDegreeToRadian az] else nil -> offsetq in
    SO3BodyCreateChamferCylinderExt node (x /. 2.0) y offsetp offsetq;;


fun V3DphysCreateBodyCapsuleExt(node, vec, offsetp, offseta)=
  let if vec == nil then V3DphysGetUniformBoundingBox node else vec -> [x y z] in
  let [(if x <=. 0.0 then 0.01 else x) (if y <=. 0.0 then 0.01 else y) (if z <=. 0.0 then 0.01 else z)] -> [x y z] in
  let offseta -> [ax ay az] in
  let if (offseta != nil) then SO3MathsEulerYXZToQuat [SO3MathsDegreeToRadian ax SO3MathsDegreeToRadian ay SO3MathsDegreeToRadian az] else nil -> offsetq in
    SO3BodyCreateCapsuleExt node (x /. 2.0) y offsetp offsetq;;


fun V3DphysCreateBodyConeExt(node, vec, offsetp, offseta)=
  let if vec == nil then V3DphysGetUniformBoundingBox node else vec -> [x y z] in
  let [(if x <=. 0.0 then 0.01 else x) (if y <=. 0.0 then 0.01 else y) (if z <=. 0.0 then 0.01 else z)] -> [x y z] in
  let offseta -> [ax ay az] in
  let if (offseta != nil) then SO3MathsEulerYXZToQuat [SO3MathsDegreeToRadian ax SO3MathsDegreeToRadian ay SO3MathsDegreeToRadian az] else nil -> offsetq in
    SO3BodyCreateConeExt node (x /. 2.0) y offsetp offsetq;;


fun V3DphysCreateBodyPyramidExt(node, vec, offsetp, offseta)=
  let if vec == nil then V3DphysGetUniformBoundingBox node else vec -> [x y z] in
  let [(if x <=. 0.0 then 0.01 else x) (if y <=. 0.0 then 0.01 else y) (if z <=. 0.0 then 0.01 else z)] -> vec in
  let offseta -> [ax ay az] in
  let if (offseta != nil) then SO3MathsEulerYXZToQuat [SO3MathsDegreeToRadian ax SO3MathsDegreeToRadian ay SO3MathsDegreeToRadian az] else nil -> offsetq in
    SO3BodyCreatePyramidExt node vec offsetp offsetq;;


fun V3DphysSetMaterialProperties(material, bcoll, bcontcoll, staticfriction, kineticfriction, elasticity, softness)=
  
  0;;


fun V3DphysEnableBody(body, state)=
  SO3BodySetIgnoreCollision body !state;
  SO3BodySetVelocity body [0.0 0.0 0.0];
  SO3BodySetOmega body [0.0 0.0 0.0];
  SO3BodySetFreeze body !state;
  SO3BodySetGravityEnable body state;
  SO3BodySetSimulation body state;
  0;;


fun V3DphysFreezeBody(body, mode)=
  let if !mode == 0 then 0 else 1 -> state in
    SO3BodySetFreeze body state;
  0;;


fun V3DphysGetBodyState(body, mode)=
  SO3BodyGetSimulation body;;


fun V3DphysShowDebug(sessionstr, state)=
  SO3WorldShowLines (V3DgetSession sessionstr) state;
  0;;


fun V3DphysCreate(sessionstr)=
  let mkV3Dphysics [nil nil nil nil nil] -> physstr in
  //SO3_PA_DEFAULT SO3_SM_EXACT SO3_FM_EXACT 
  (
    set sessionstr.V3D_physics = physstr;
    
    //V3DphysSetArchitectureMode sessionstr SO3_PA_DEFAULT;
    V3DphysSetSolverMode sessionstr 1;

    V3DphysSetFps sessionstr 120;
    V3DphysSetGravity sessionstr [0.0 (-.9.81) 0.0];
    V3DphysEnablePhysic sessionstr 0;
    //V3DphysShowDebug sessionstr 1;
  );
  0;;


fun V3DphysDestroy(sessionstr)=
  V3DphysEnablePhysic sessionstr 0;
  set sessionstr.V3D_physics = nil;
  0;;


fun V3DphysReset(sessionstr)=
  V3DphysDestroy sessionstr;
  V3DphysCreate sessionstr;
  0;;