#ifndef _MATH_H_RAY
#define _MATH_H_RAY
// (Linux) We had troubles with an already existing _MATH_H symbol.


#define raydium_trigo_cos(a) raydium_math_cos(a)
#define raydium_trigo_sin(a) raydium_math_sin(a)
#define raydium_trigo_cos_inv(a) raydium_math_cos_inv(a)
#define raydium_trigo_sin_inv(a) raydium_math_sin_inv(a)
#define raydium_trigo_abs(a) raydium_math_abs(a)
#define raydium_trigo_min(a,b) raydium_math_min(a,b)
#define raydium_trigo_max(a,b) raydium_math_max(a,b)
#define raydium_trigo_isfloat(a) raydium_math_isfloat(a)
#define raydium_trigo_round(a) raydium_math_round(a)
#define raydium_trigo_rotate(a,b,c,d,e) raydium_math_rotate(a,b,c,d,e)
#define raydium_trigo_pos_to_matrix(a,b) raydium_math_pos_to_matrix(a,b)
#define raydium_trigo_pos_get_modelview(a) raydium_math_pos_get_modelview(a)
#define raydium_trigo_pow2_next(a) raydium_math_pow2_next(a)

/*=
Maths
200
*/

// Little introduction to math.c
/**
This section is mostly designed for internal uses, but provides some
usefull maths functions, mostly for trigonometrical uses.

Historical note: most functions here were originally named with "trigo" prefix,
since this module was named trigo.c. Aliases are provided for compatibility
reasons, of course. (but watch out for old bindings !)
**/

__rayapi GLfloat raydium_math_cos (GLfloat i);
/**
Obvious (degrees)
**/

__rayapi GLfloat raydium_math_sin (GLfloat i);
/**
Obvious (degrees)
**/

__rayapi GLfloat raydium_math_cos_inv (GLfloat i);
/**
Obvious (degrees)
**/

__rayapi GLfloat raydium_math_sin_inv (GLfloat i);
/**
Obvious (degrees)
**/

#define raydium_math_rad2deg(a) ((a)*180/PI)
/**
Radians to degrees.
**/

#define raydium_math_deg2rad(a) ((a)*PI/180)
/**
Degrees to radians.
**/

#define raydium_math_abs(a) ( (a) < (0) ? (-(a)) : (a) )
/**
Obvious
define raydium_trigo_abs is deprecated, for compatibility only.
**/

#define raydium_math_min(a,b) ( (a) < (b) ? (a) : (b) )
/**
Obvious
define raydium_trigo_min is deprecated, for compatibility only.
**/

#define raydium_math_max(a,b) ( (a) > (b) ? (a) : (b) )
/**
Obvious
define raydium_trigo_max is deprecated, for compatibility only.
**/

#define raydium_math_isfloat(a) ( (!isnan(a) && !isinf(a)) ? 1 : 0)
/**
Test two cases : "Not a Number" and "Infinite"
define raydium_trigo_isfloat is deprecated, for compatibility only.
**/

#define raydium_math_round(a) ((int)((a)>0?((a)+0.5):((a)-0.5)))
/**
Will obviously "round" ##a## instead of the default C floor behaviour
define raydium_trigo_round is deprecated, for compatibility only.
**/

__rayapi void raydium_math_rotate (GLfloat * p, GLfloat rx, GLfloat ry, GLfloat rz, GLfloat * res);
/**
Rotate p (GLfloat * 3) by (rx,ry,rx) angles (degrees).
Result is stored in res (GLfloat * 3)
**/

__rayapi void raydium_math_normalize_vector4 (GLfloat * vector);
/**
Vector normalization.
**/

__rayapi void raydium_math_pos_to_matrix (GLfloat * pos, GLfloat * m);
/**
Generates a ODE style matrix (16 Glfloat) from pos (GLfloat * 3)
**/

__rayapi void raydium_math_matrix4_identity_set(GLfloat * mati);
/**
Set 4x4 matrix ##mati## to identity.
**/

__rayapi void raydium_math_multiply_matrix4(GLfloat *matrix1,GLfloat *matrix2,GLfloat *result );
/**
Matrix 4x4 multiplication.
**/

__rayapi void raydium_math_invert_matrix4(GLfloat * matin, GLfloat * matout);
/**
Compute inverse of 4x4 Matrix
**/

__rayapi void raydium_math_translate_matrix4_3f(GLfloat x, GLfloat y, GLfloat z,GLfloat *matrix);
/**
Translate 4x4 Matrix ##matrix## by (x,y,z) vector.
**/

__rayapi void raydium_math_rotate_matrix4_vector_angle(GLfloat angleInRadians, GLfloat x, GLfloat y, GLfloat z,GLfloat *matrix);
/**
Rotate 4x4 matrix ##matrix## arround vector ##(x,y,z)## for ##angleInRadians## radians.
**/

__rayapi void raydium_math_crossproduct (GLfloat *pvector1, GLfloat *pvector2,GLfloat *normal);
/**
Compute cross (vectorial) product of ##pvector1## and ##pvector2##. Result in ##normal## vector.
**/

__rayapi void raydium_math_multiply_matrix4_vectors_array(GLfloat *matrix1,GLfloat *vect, int n_elems,GLfloat *result);
/**
Multiply ##n_elems## vector array by 4x4 matrixj, k;


    dataPtr=data;

    if( arDetectMarker(dataPtr, thresh, &marker_info, &marker_num) < 0 ) {
        //cleanup();
        raydium_log("arDetectMarker error");
        exit(0);
    }


    k = -1;
    for( j = 0; j < marker_num; j++ ) {
        if( patt_id == marker_info[j].id ) {
            if( k == -1 ) k = j;
            else if( marker_info[k].cf < marker_info[j].cf ) k = j;
        }
    }
    if( k == -1 ) {
        raydium_log("no marker available %i",raydium_random_i(0,10));
        return 1;
    }
    else
        raydium_log("Marker %f",marker_info[k].cf);

//    arGetTransMat(&marker_info[k], patt_center, patt_width, patt_trans);
    arGetTransMatCont(&marker_info[k], patt_trans,patt_center, patt_width, patt_trans);
    arUtilMatInv(patt_trans, cam_trans);
    argConvGlpara(patt_trans, gl_para);

    rot_ok=1;

return 1;
}


void create_perso(void)
{
int a;

    raydium_ode_object_delete_name("PLAYER");
    a=raydium_ode_object_create("PLAYER");
    raydium_ode_object_sphere_add("player",a,0.1,RAYDIUM_ODE_AUTODETECT*0.8,RAYDIUM_ODE_STANDARD,0,PLAYER_MODEL);
    raydium_ode_element_material_name("player",RAYDIUM_ODE_MATERIAL_SOFT2);
    raydium_ode_element_player_set_name("player",1);
    raydium_ode_motor_create("player_react",a,RAYDIUM_ODE_MOTOR_ROCKET);
    raydium_ode_motor_rocket_set_name("player_react","player",0,0,0);
    raydium_ode_motor_rocket_playermovement_name("player_react",1);
    raydium_ode_element_slip_name("ground",RAYDIUM_ODE_SLIP_ICE/2.f);

    raydium_ode_object_move_name_3f("PLAYER",0,0,1);
}


void create_car(void)
{
int a;
 
#define BREAK_FORCE     130
#define ROTFRICTION     0.0005
#define ERP_CFM         0.3,0.8
 
raydium_ode_object_delete_name("WATURE");
 
a=raydium_ode_object_create("WATURE");
raydium_ode_object_box_add("corps",a,1,1.2,0.6,0.4,RAYDIUM_ODE_STANDARD,0,"clio.tri");
raydium_ode_element_slip_name("corps",RAYDIUM_ODE_SLIP_ICE);
 
raydium_ode_object_sphere_add("pneu_ag",a,0.5,RAYDIUM_ODE_AUTODETECT,RAYDIUM_ODE_STANDARD,0,"roue5.tri");
raydium_ode_element_rotfriction_name("pneu_ag",ROTFRICTION);
raydium_ode_element_move_name_3f("pneu_ag",0.42,0.253,-0.180);
raydium_ode_joint_attach_hinge2_name("suspet_ag","corps","pneu_ag",RAYDIUM_ODE_JOINT_SUSP_DEFAULT_AXES);
raydium_ode_joint_break_force_name("suspet_ag",BREAK_FORCE);
raydium_ode_joint_suspension_name("suspet_ag",ERP_CFM);
raydium_ode_object_sphere_add("pneu_ad",a,0.5,RAYDIUM_ODE_AUTODETECT,RAYDIUM_ODE_STANDARD,0,"roue5.tri");
raydium_ode_element_rotfriction_name("pneu_ad",ROTFRICTION);
raydium_ode_element_move_name_3f("pneu_ad",0.42,-0.253,-0.180);
raydium_ode_joint_attach_hinge2_name("suspet_ad","corps","pneu_ad",RAYDIUM_ODE_JOINT_SUSP_DEFAULT_AXES);
raydium_ode_joint_break_force_name("suspet_ad",BREAK_FORCE);
raydium_ode_joint_suspension_name("suspet_ad",ERP_CFM);
raydium_ode_object_sphere_add("pneu_rg",a,0.5,RAYDIUM_ODE_AUTODETECT,RAYDIUM_ODE_STANDARD,0,"roue5.tri");
raydium_ode_element_rotfriction_name("pneu_rg",ROTFRICTION);
raydium_ode_element_move_name_3f("pneu_rg",-0.444,0.253,-0.180);
raydium_ode_joint_attach_hinge2_name("suspet_rg","corps","pneu_rg",RAYDIUM_ODE_JOINT_SUSP_DEFAULT_AXES);
raydium_ode_joint_hinge2_block_name("suspet_rg",1);
raydium_ode_joint_break_force_name("suspet_rg",BREAK_FORCE);
raydium_ode_joint_suspension_name("suspet_rg",ERP_CFM);
 
raydium_ode_object_sphere_add("pneu_rd",a,0.5,RAYDIUM_ODE_AUTODETECT,RAYDIUM_ODE_STANDARD,0,"roue5.tri");
raydium_ode_element_rotfriction_name("pneu_rd",ROTFRICTION);
raydium_ode_element_move_name_3f("pneu_rd",-0.444,-0.253,-0.180);
raydium_ode_joint_attach_hinge2_name("suspet_rd","corps","pneu_rd",RAYDIUM_ODE_JOINT_SUSP_DEFAULT_AXES);
raydium_ode_joint_hinge2_block_name("suspet_rd",1);
raydium_ode_joint_break_force_name("suspet_rd",BREAK_FORCE);
raydium_ode_joint_suspension_name("suspet_rd",ERP_CFM);
 
raydium_ode_motor_create("moteur",a,RAYDIUM_ODE_MOTOR_ENGINE);
raydium_ode_motor_attach_name("moteur","suspet_ag",1);
raydium_ode_motor_attach_name("moteur","suspet_ad",1);
raydium_ode_motor_power_max_name("moteur",0.2);
 
raydium_ode_motor_create("direction",a,RAYDIUM_ODE_MOTOR_ANGULAR);
raydium_ode_motor_attach_name("direction","suspet_ag",0);
raydium_ode_motor_attach_name("direction","suspet_ad",0);
raydium_ode_motor_power_max_name("direction",0.2);

raydium_ode_object_move_name_3f("WATURE",0,0,1);
}


void display(void)
{
float speed,direct;
static float scale=50;
 
raydium_joy_key_emul();
 
direct=raydium_joy_x*0.3;
speed=raydium_joy_y*55;
 
raydium_ode_motor_speed_name("moteur",-speed);
raydium_ode_motor_angle_name("direction",direct);

if(raydium_key_last==1027) exit(0);

if(raydium_key[GLUT_KEY_F1]) scale++;
if(raydium_key[GLUT_KEY_F2]) scale--;

if(raydium_key_last==1000+'c') 
    {
    char dummy[128];
    raydium_ode_name_auto("caisse",dummy);
    raydium_ode_object_box_add(dummy,raydium_ode_object_find("GLOBAL"),1,RAYDIUM_ODE_AUTODETECT,RAYDIUM_ODE_AUTODETECT,RAYDIUM_ODE_AUTODETECT,RAYDIUM_ODE_STANDARD,0,"crate.tri");
    raydium_ode_element_move_name_3f(dummy,0,0,2);
    raydium_ode_element_ttl_set_name(dummy,raydium_ode_get_physics_freq()*60);
    }
                                
if(raydium_key_last==1032) 
    {
    raydium_ode_object_delete_name("WATURE");
    create_car();
    }


raydium_clear_frame();


// useless :)
raydium_camera_look_at(0,0.01,3,0,0,0); 

raydium_live_texture_mask_name("webcam.tga",1);
ar_camera_start(patt_trans);
raydium_light_update_position_all();
glScalef(scale,scale,scale);
glPushMatrix();

// because of glScale ...
glEnable(GL_NORMALIZE);
raydium_ode_draw_all(0);
//raydium_ode_draw_all(1);
glDisable(GL_NORMALIZE);

glPopMatrix();
ar_camera_stop();

raydium_osd_printf(2,4,20,0.5,"font2.tga","%f",scale);

raydium_ode_network_element_send_iterative(RAYDIUM_ODE_NETWORK_OPTIMAL);
raydium_rendering_finish();
}


int main(int argc, char **argv)
{
int device;
ARParam  wparam;
FILE *fp;
char server[128];

raydium_init_args(argc,argv);
raydium_window_create(800,600,RAYDIUM_RENDERING_WINDOW,"Augmented Reality - libAR test");
raydium_texture_filter_change(RAYDIUM_TEXTURE_FILTER_TRILINEAR);
raydium_projection_near=1;
raydium_projection_far=10000;
raydium_projection_fov=60; // useless
raydium_window_view_update();

raydium_light_on(0);
memcpy(raydium_light_color[0],sun,raydium_internal_size_vector_float_4);
raydium_light_intensity[0]=100000;

raydium_light_position[0][0]=50;
raydium_light_position[0][1]=150;
raydium_light_position[0][2]=200;

raydium_light_update_all(0);
raydium_background_color_change(sun[0],sun[1],sun[2],sun[3]);
raydium_fog_disable();

    fp=raydium_file_fopen(cparam_name,"rb");
    if(!fp)
        exit(1);
    fclose(fp);
    fp=raydium_file_fopen(patt_name,"rb");
    if(!fp)
        exit(1);
    fclose(fp);

    /* set the initial camera parameters */
    if( arParamLoad(cparam_name, 1, &wparam) < 0 ) {
        printf("Camera parameter load error !!\n");
        exit(0);
    }
    arParamChangeSize( &wparam, CAMERA_RES_X, CAMERA_RES_Y, &cparam );
    arInitCparam( &cparam );
    printf("*** Camera Parameter ***\n");
    arParamDisp( &cparam );

    if( (patt_id=arLoadPatt(patt_name)) < 0 ) {
        printf("pattern load error !!\n");
        exit(0);
    }


argConvGLcpara( &cparam, AR_GL_CLIP_NEAR, AR_GL_CLIP_FAR, gl_cpara );
arImageProcMode = AR_IMAGE_PROC_IN_FULL;

//device=raydium_live_video_open_auto();
device=raydium_live_video_open(RAYDIUM_LIVE_DEVICE_AUTO,CAMERA_RES_X,CAMERA_RES_Y);
raydium_live_texture_video(device,"webcam.tga");
//raydium_video_open("s80.jpgs","webcam.tga");
raydium_live_texture_refresh_callback_set_name("webcam.tga",data_callback);

raydium_shadow_enable();

if(raydium_init_cli_option("server",server))
     if(!raydium_network_client_connect_to(server)) 
        exit(1);

raydium_ode_ground_set_name("area.tri");
create_car();

//create_perso();
//raydium_object_anim_automatic_name(PLAYER_MODEL,"stand",10);

raydium_callback(&display);

return 0;
}