// MCar.cpp: implementation of the MCar class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "mirrorM.h"
#include "MCar.h"
#include "MainFrm.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

MCar::MCar()
{

}

MCar::~MCar()
{

}




#define CAR_MAX_SPEED 200
#define CAR_MIN_SPEED 40 //Below that speed the car stops (speed becomes 0)

//Controls how fast the car will reach its top speed
// A higher value makes a car with a better acceleration
#define CAR_ACCELERATION 5  

//Controls how fast the car will reach stop
//When the space key is not pressed
// A smaller value makes a car with a better breaks
#define	CAR_SPEED_RESTRAIN_FACTOR 0.85 
																			
#define	CAR_AIR_SPEED_RESTRAIN_FACTOR 0.98 

//obstacles that are higher than this value
//will stop the car. (The car wouldnt be able to climb over them)
//One can also use the speed instead of this constant to
//give a more realistic behavior (when the speed is high
//the car can climb on bigger barriers)
#define barrier_HEIGHT 200

//Controls how fast the wheels will
//get back to normal position after we turn them
//A smaller value cause the wheels to get strait faster.
#define WHEELS_RESTRAIN_FACTOR	0.5

//Controls how fast the wheels
//will respond to the keyboard
#define WHEELS_TURNING_SPEED	2.5

//When the car move forward, the wheels have
//to spin so the spin match the forward movement
#define WHEELS_SPIN_SPEED		25

//The maximum angle that the wheel can get
//a bigger number makes it possible to take
//sharper turns.
#define WHEELS_MAX_ANGLE		30

//When the four wheels are disconnected from
//the ground the car start to fall down.
//A bigger value means that we will reach the car faster.
#define GRAVITY	10

/*
void main(void)
{
  int	initialize(car_info_type *car_info);
  void	move_car(car_info_type *car_info);
  car_info_type car_info;

	if(initialize(&car_info)!=OK) {
		printf("Failed to init, aborting\n");
		return; 
	}

	DWORD camera=Morfit_camera_get_default_camera();


	int res = Morfit_3D_card_use(YES);
	
	//The main loop
	while( (GetAsyncKeyState(VK_ESCAPE)&1) ==0 ) {
		move_car(&car_info);
		Morfit_engine_render(NULL,camera);	
	}
}
*/

#define SOUND_OFF		1
#define SOUND_ENGINE	2
//#define SOUND_BREAKS	3
//#define SOUND_STARTER	4
//#define SOUND_PLANE		5
#define SOUND_BOOM		6

int MCar::initialize()
{

	//my_car object should be defined in world6.wld
	DWORD car_handle=Morfit_object_get_object_using_name("carBody");
	if(car_handle==NULL) {
		printf("couldnt find carBody\n");
		return(VR_ERROR);
	}
	
	//Lets disconnect the car from the road track
	//(In the file car_road.wld the car is set to chase the road)
	Morfit_object_set_chase_type(car_handle,NO_CHASE);

	
	
	//This gives a good balance between speed and accuracy
	Morfit_engine_set_perspective_correction_accuracy(0.05);
	
	//Using a zbuffer we have more accuracy, but we lose speed
	//Morfit_engine_use_zbuffer(NO);
//	Morfit_engine_set_resolution(640,480,16);

	//Note that it makes a difference if we call this function
	//before Morfit_engine_set_resolution or after
	//(The right thing is to call it after ...)
//	Morfit_engine_maximize_default_rendering_window();
	
	//Try playing with this number and see how it
	//influence the quality of the render image (and the speed)
	Morfit_engine_set_picture_quality(1000000);

	//so we wont see the cursor


	DWORD front_left_wheel_handle=Morfit_object_get_object_using_name("wheelfrontleft");
	DWORD front_right_wheel_handle=Morfit_object_get_object_using_name("wheelfrontright");
	DWORD back_left_wheel_handle=Morfit_object_get_object_using_name("wheelBackLeft");
	DWORD back_right_wheel_handle=Morfit_object_get_object_using_name("wheelBackright");
	UINT Rudder = Morfit_object_get_object_using_name("Rudder");
	UINT carWindow = Morfit_object_get_object_using_name("CarWindow");
	
	if(front_left_wheel_handle==NULL || front_right_wheel_handle==NULL || back_left_wheel_handle==NULL || back_right_wheel_handle==NULL) {
		printf("couldnt find one of the wheels\n");
		return(VR_ERROR);
	}

	//Get the dimensions of the car
	double box[2][3];
	Morfit_object_get_bounding_box(car_handle, box);
	car_info.length=box[1][0]-box[0][0];
	car_info.width=box[1][1]-box[0][1];
	car_info.height=box[1][2]-box[0][2];

	Morfit_object_get_bounding_box(front_left_wheel_handle, box);
	car_info.wheel_length=box[1][0]-box[0][0];
	car_info.wheel_width=box[1][1]-box[0][1];
	car_info.wheel_front_height=box[1][2]-box[0][2];

	Morfit_object_get_bounding_box(back_left_wheel_handle, box);

	car_info.wheel_back_height=box[1][2]-box[0][2];

	
	car_info.car_handle=car_handle;
	car_info.front_left_wheel_handle[0]=front_left_wheel_handle;
	car_info.front_right_wheel_handle[0]=front_right_wheel_handle;
	car_info.back_left_wheel_handle[0]=back_left_wheel_handle;
	car_info.back_right_wheel_handle[0]=back_right_wheel_handle;
	car_info.m_Rudder = Rudder;
	car_info.m_carWindow = carWindow;

	car_info.speed=0;
	car_info.wheels_angle=0;
	car_info.wheels_spin_angle=0;
	car_info.is_air_mode=NO;
	car_info.current_sound=SOUND_OFF;
	car_info.m_weelsMarkFlag = FALSE;

	car_info.wheel_mark_bitmap=Morfit_bitmap_load("worlds\\bitmaps\\wl_mark",0);
	return(OK);

}


 
void MCar::move_car()
{


/*	//First lets disable all the wheels so
	//when we call Morfit_object_is_movement possible()
	//We wont get NO, just because we discovered colision with 
	//the wheels. An object that is disabled wont be rendered and
	//wont be taking part in colision detection.
	Morfit_object_disable(car_info.front_left_wheel_handle[0]);
	Morfit_object_disable(car_info.front_right_wheel_handle[0]);
	Morfit_object_disable(car_info.back_left_wheel_handle[0]);
	Morfit_object_disable(car_info.back_right_wheel_handle[0]);
*/
	move_car_according_to_wheels();	

/*	//Lets enable the wheels again
	Morfit_object_enable(car_info.front_left_wheel_handle[0]);
	Morfit_object_enable(car_info.front_right_wheel_handle[0]);
	Morfit_object_enable(car_info.back_left_wheel_handle[0]);
	Morfit_object_enable(car_info.back_right_wheel_handle[0]);

*/	put_wheels_in_place();

	create_wheels_mark_on_the_ground(car_info.back_left_wheel_handle[0]);
	create_wheels_mark_on_the_ground(car_info.back_right_wheel_handle[0]);
	
	//Turning wheels left or right
	if(GetAsyncKeyState(VK_LEFT)<0) {
		
		turn_wheel_left();
	}
	else
	if(GetAsyncKeyState(VK_RIGHT)<0) {
		turn_wheel_right();
	}
	else{
		//turn wheels to the direction of the car
		turn_wheels_default();
	}

	

	//handle car speed

	if(GetAsyncKeyState(VK_DOWN)<0) 
	{
			if(car_info.speed > - CAR_MIN_SPEED && car_info.speed < 0)
				car_info.speed = - CAR_MIN_SPEED;
      		car_info.speed-=CAR_ACCELERATION;
			
			if(car_info.speed < -CAR_MAX_SPEED) car_info.speed=-CAR_MAX_SPEED;

			if(car_info.speed < - CAR_MAX_SPEED / 2)
				car_info.m_weelsMarkFlag = FALSE;
			else
				car_info.m_weelsMarkFlag = TRUE;

	}
	else if(!(GetAsyncKeyState(VK_UP)<0))
		{
			car_info.speed*=CAR_SPEED_RESTRAIN_FACTOR;
			if(car_info.speed>-CAR_MIN_SPEED && car_info.speed < 0) car_info.speed=0;
			if(car_info.speed<CAR_MIN_SPEED&& car_info.speed > 0) car_info.speed=0;
		}
	
		else if(GetAsyncKeyState(VK_UP)<0) 
		{
			if(car_info.speed < CAR_MIN_SPEED && car_info.speed > 0)
				car_info.speed = CAR_MIN_SPEED;
      		car_info.speed+=CAR_ACCELERATION;
			if(car_info.speed>CAR_MAX_SPEED) car_info.speed=CAR_MAX_SPEED;

			if(car_info.speed < CAR_MAX_SPEED / 2)
				car_info.m_weelsMarkFlag = FALSE;
			else
				car_info.m_weelsMarkFlag = TRUE;

		}


		
		TRACE1("%.2f\n",car_info.speed);
		

		
	
	

	createMirror();
}

void MCar::set_sounds()
{
	
	if(car_info.current_sound!=SOUND_ENGINE && car_info.speed) 
	{
		sndPlaySound ("sound\\drive.wav", SND_ASYNC | SND_LOOP);
		car_info.current_sound=SOUND_ENGINE;
	}
	else if(car_info.current_sound == SOUND_BOOM)
	{
		sndPlaySound ("sound\\t1.wav",SND_ASYNC);
		car_info.current_sound=SOUND_OFF;
	}
	else if(!car_info.speed)
	{
		sndPlaySound (NULL, NULL);
		car_info.current_sound=SOUND_OFF;
	}
	
	
	
	
}

void MCar::turn_wheel_left()
{
	car_info.wheels_angle+=WHEELS_TURNING_SPEED;
	if(car_info.wheels_angle>WHEELS_MAX_ANGLE)
		car_info.wheels_angle=WHEELS_MAX_ANGLE;
}
	
void MCar::turn_wheel_right()
{
	car_info.wheels_angle-=WHEELS_TURNING_SPEED;
	if(car_info.wheels_angle <-WHEELS_MAX_ANGLE ) car_info.wheels_angle=-WHEELS_MAX_ANGLE;
}

void MCar::turn_wheels_default()
{
	car_info.wheels_angle*=WHEELS_RESTRAIN_FACTOR;
}

void MCar::set_wheels_spin_angle()
{
	car_info.wheels_spin_angle+=-car_info.speed/CAR_MAX_SPEED*WHEELS_SPIN_SPEED  ;
	if(car_info.wheels_spin_angle>360) car_info.wheels_spin_angle-=360;
	else
	if(car_info.wheels_spin_angle<-360) car_info.wheels_spin_angle+=360;
}

//We dont make any assumption on the current location and the angle of
//the wheel. We positioned them from scratch
// giving the right location and the right angle
void MCar::put_wheels_in_place()
{
  
  
 


	
	//Turn the wheel so they are strait with the car
//	Morfit_object_get_axis_system(car_info.car_handle, X,Y,Z);
//	Morfit_object_set_axis_system(car_info.front_left_wheel_handle[0], X,Y,Z);
//	Morfit_object_set_axis_system(car_info.front_right_wheel_handle[0], X,Y,Z);
//	Morfit_object_set_axis_system(car_info.back_left_wheel_handle[0], X,Y,Z);
//	Morfit_object_set_axis_system(car_info.back_right_wheel_handle[0], X,Y,Z);

	//Put the wheels temporary in the center of the car.
//	Morfit_object_get_location(car_info.car_handle,&car_location[0],&car_location[1],&car_location[2]);
//	Morfit_object_set_location(car_info.front_left_wheel_handle[0],car_location[0],car_location[1],car_location[2]);
//	Morfit_object_set_location(car_info.front_right_wheel_handle[0],car_location[0],car_location[1],car_location[2]);
//	Morfit_object_set_location(car_info.back_left_wheel_handle[0],car_location[0],car_location[1],car_location[2]);
//	Morfit_object_set_location(car_info.back_right_wheel_handle[0],car_location[0],car_location[1],car_location[2]);

	
	double width,length,height;
	width=car_info.width;
	length=car_info.length;
	height=car_info.height;
	//Put the wheels where they should be
//	Morfit_object_move(car_info.front_left_wheel_handle[0],OBJECT_SPACE,-length/3, -width/2, -height/2.7);
//	Morfit_object_move(car_info.front_right_wheel_handle[0],OBJECT_SPACE,-length/3, width/2, -height/2.7);
//	Morfit_object_move(car_info.back_left_wheel_handle[0],OBJECT_SPACE,length/3, -width/2, -height/2.7);
//	Morfit_object_move(car_info.back_right_wheel_handle[0],OBJECT_SPACE,length/3, width/2, -height/2.7);
	Morfit_object_advance(car_info.front_left_wheel_handle[0]);
	Morfit_object_advance(car_info.front_left_wheel_handle[0]);

	Morfit_object_advance(car_info.front_right_wheel_handle[0]);
	Morfit_object_advance(car_info.front_right_wheel_handle[0]);

	Morfit_object_advance(car_info.back_left_wheel_handle[0]);
	Morfit_object_advance(car_info.back_left_wheel_handle[0]);

	Morfit_object_advance(car_info.back_right_wheel_handle[0]);
	Morfit_object_advance(car_info.back_right_wheel_handle[0]);

	Morfit_object_advance(car_info.m_Rudder);
	Morfit_object_advance(car_info.m_Rudder);

	Morfit_object_advance(car_info.m_carWindow);
	Morfit_object_advance(car_info.m_carWindow);

	//Turn the wheels in the right direction
	//We turn only the front wheels of course
	Morfit_object_rotate_z(car_info.front_left_wheel_handle[0],car_info.wheels_angle,OBJECT_SPACE);
	Morfit_object_rotate_z(car_info.front_right_wheel_handle[0],car_info.wheels_angle,OBJECT_SPACE);


	Morfit_object_rotate_x(car_info.m_Rudder,car_info.wheels_angle,OBJECT_SPACE);

	//Turn wheels according to wheels_spin_angle (around the Y axis)
	set_wheels_spin_angle();
	Morfit_object_rotate_y(car_info.front_left_wheel_handle[0],car_info.wheels_spin_angle,OBJECT_SPACE);
	Morfit_object_rotate_y(car_info.front_right_wheel_handle[0],car_info.wheels_spin_angle,OBJECT_SPACE);
	Morfit_object_rotate_y(car_info.back_left_wheel_handle[0],car_info.wheels_spin_angle,OBJECT_SPACE);
	Morfit_object_rotate_y(car_info.back_right_wheel_handle[0],car_info.wheels_spin_angle,OBJECT_SPACE);
	
}

void MCar::create_wheels_mark_on_the_ground(DWORD wheel)
{
	//We create wheels mark only when we use the breaks
	//(when the up arrow key is not pressed)
	// 
	if(GetAsyncKeyState(VK_UP)<0) return;

	if(car_info.m_weelsMarkFlag == FALSE) return; 

	//get the direction we will need it for Morfit_polygon_add_patch_easy() so the
	//wheel mark will be in the direction of the wheel
	double direction[3];
	Morfit_object_get_direction(wheel, &direction[0],&direction[1],&direction[2]);
	
	//we want to find the point on the ground that the wheel is standing on
	//We will take two points one in the center of the wheel and one deep down below that point
	double start_location[3], end_location[3];
	Morfit_object_get_location(wheel, &start_location[0], &start_location[1],&start_location[2]);
	end_location[0]=start_location[0];
	end_location[1]=start_location[1];
	end_location[2]=-100000000;

	//finding the intersection point with the ground	
	DWORD polygon, blocking_object;
	double intersection[3];
	if(Morfit_object_is_movement_possible(wheel, start_location, end_location, &polygon, intersection, &blocking_object)==YES)
		return; //We can find the intersection with the ground

	if(blocking_object!=NULL) return; //we want to get the ground (NULL) and not another object (another object could be a helicopter fpr example)
									  //We might get into it when we jump from the hill.
	
	if(Morfit_polygon_is_rotated(polygon)==YES) return; //We dont want to leave marks on the trees
														//or on the cows if we jump on them do we ?
	
	int rgb[3]={0,0,0};
	DWORD patch=Morfit_polygon_add_patch_easy(polygon, intersection,40,direction, car_info.wheel_mark_bitmap, rgb);
	//Morfit_polygon_set_translucent(patch,CONTROL_GLASS);
	
	
}


void MCar::move_car_according_to_wheels()
{

	turn_car_according_to_wheels();
	move_car_forward();
}

void MCar::turn_car_according_to_wheels()
{

	if(!(car_info.is_air_mode==NO && car_info.speed)) return;



	double turn_angle;

	double objectPoints[2][3] = {0,0,0,0,0,0},startWorldPoints[2][3],endWorldPoints[2][3];
	
	int direction;
	if(car_info.speed > 0)
		direction = -1;
	else
		direction = 1;

	objectPoints[0][0] = objectPoints[1][0]	= car_info.length/2 * direction;
	objectPoints[0][2] = objectPoints[1][2]	= - car_info.height;
	objectPoints[0][1] = car_info.width / 2;
	objectPoints[1][1]	= - car_info.width / 2;

	Morfit_object_convert_point_to_world_space(car_info.car_handle, objectPoints[0],startWorldPoints[0]);
	Morfit_object_convert_point_to_world_space(car_info.car_handle, objectPoints[1],startWorldPoints[1]);



	

		if(car_info.speed > 0)
			turn_angle = car_info.wheels_angle*0.1;
		else
			turn_angle = - car_info.wheels_angle*0.1;

		Morfit_object_rotate_z(car_info.car_handle,turn_angle,OBJECT_SPACE);


	Morfit_object_convert_point_to_world_space(car_info.car_handle, objectPoints[0],endWorldPoints[0]);
	Morfit_object_convert_point_to_world_space(car_info.car_handle, objectPoints[1],endWorldPoints[1]);


	DWORD intersectedPoly;
	DWORD intersectedObject;
	double intersection[3];

	
	
	DWORD res1 = Morfit_object_is_movement_possible(car_info.car_handle, startWorldPoints[0],endWorldPoints[0],
										&intersectedPoly,intersection,&intersectedObject);


	DWORD res2 = Morfit_object_is_movement_possible(car_info.car_handle, startWorldPoints[1],endWorldPoints[1],
										&intersectedPoly,intersection,&intersectedObject);

	if(res1 == NO || res2 == NO)
		Morfit_object_rotate_z(car_info.car_handle,-turn_angle,OBJECT_SPACE);
	
}

void MCar::move_car_forward() 
{
	if(checkIntersectionForward())
		Morfit_object_move(car_info.car_handle,OBJECT_SPACE,-car_info.speed,0,0);
	else
	{
		if(car_info.speed > 100)
		car_info.current_sound = SOUND_BOOM;
		car_info.speed = 0;
	}
	set_sounds();

}


void MCar::switch_to_air_mode()
{


}

void MCar::copy_vec(double dst[3], double src[3])
{
	dst[0]=src[0];
	dst[1]=src[1];
	dst[2]=src[2];

}


void MCar::createMirror()
{
	
	static UINT car = 0,wheels[4] = {0,0,0,0};

	if(car)
	{
		Morfit_object_delete(car);
		for(int i = 0; i < 4;i ++)
			Morfit_object_delete(wheels[i]);
	}
	

	car = Morfit_object_duplicate(car_info.car_handle,NO);
	wheels[0] = Morfit_object_duplicate(car_info.back_left_wheel_handle[0],NO);
	wheels[1] = Morfit_object_duplicate(car_info.back_right_wheel_handle[0],NO);
	wheels[2] = Morfit_object_duplicate(car_info.front_left_wheel_handle[0],NO);
	wheels[3] = Morfit_object_duplicate(car_info.front_right_wheel_handle[0],NO);

	double rotationCenter[3],car_location[3], car_direction[3];
	Morfit_object_get_location(car,&car_location[0],&car_location[1],&car_location[2]);
	Morfit_object_get_direction(car,&car_direction[0],&car_direction[1],&car_direction[2]);

	
	Morfit_object_convert_point_to_object_space(car,m_intersection,rotationCenter);
//	Morfit_object_set_rotation_center(car, rotationCenter);

	

	Morfit_object_rotate_x(car,180,OBJECT_SPACE);
	Morfit_object_move(car,WORLD_SPACE,0,0,-(car_info.height*1.4));

	Morfit_object_get_location(car,&car_location[0],&car_location[1],&car_location[2]);
	Morfit_object_enable(car);


	for(int i = 0; i < 4;i ++)
		if(i > 1)
		Morfit_object_move(wheels[i],WORLD_SPACE,0,0,-(car_info.wheel_front_height*1.1));
	else
		Morfit_object_move(wheels[i],WORLD_SPACE,0,0,-(car_info.wheel_back_height*1.1));


}

BOOL MCar::checkIntersectionForward()
{

	

	
	
	double speed = - car_info.speed;

	


	if(!speed) return TRUE;

	int direction;
	if(speed < 0)
		direction = -1;
	else
		direction = 1;



	
	double locationObjectSpace[3] = {0,0,0},newLocationObjectSpace[3];
	double locationWorldSpace[3] = {0,0,0},newLocationWorldSpace[3];
	
	
	locationObjectSpace[0] += car_info.length/2 * direction;

	copy_vec(newLocationObjectSpace,locationObjectSpace);

	newLocationObjectSpace[0] += speed + 10 * direction;

	locationObjectSpace[2] -= car_info.height;
	newLocationObjectSpace[2] -= car_info.height;

	copy_vec(locationWorldSpace,locationObjectSpace);
	copy_vec(newLocationWorldSpace,newLocationObjectSpace);

	
	Morfit_object_convert_point_to_world_space(car_info.car_handle, locationWorldSpace, locationWorldSpace);
	Morfit_object_convert_point_to_world_space(car_info.car_handle, newLocationWorldSpace, newLocationWorldSpace);
	
	DWORD intersectedPoly;
	DWORD intersectedObject;
	double intersection[3];

	CMainFrame *mFrame = (CMainFrame*)AfxGetMainWnd();


	
	if(NO == Morfit_object_is_movement_possible(car_info.car_handle, locationWorldSpace,newLocationWorldSpace,
										&intersectedPoly,intersection,&intersectedObject))

		return FALSE;




	

	copy_vec(locationWorldSpace,locationObjectSpace);
	copy_vec(newLocationWorldSpace,newLocationObjectSpace);

	locationWorldSpace[1] -= car_info.width / 2;
	newLocationWorldSpace[1] -= car_info.width / 2;

	
	Morfit_object_convert_point_to_world_space(car_info.car_handle, locationWorldSpace, locationWorldSpace);
	Morfit_object_convert_point_to_world_space(car_info.car_handle, newLocationWorldSpace, newLocationWorldSpace);
	
	
	
	if(NO == Morfit_object_is_movement_possible(car_info.car_handle, locationWorldSpace,newLocationWorldSpace,
										&intersectedPoly,intersection,&intersectedObject))

		return FALSE;






	copy_vec(locationWorldSpace,locationObjectSpace);
	copy_vec(newLocationWorldSpace,newLocationObjectSpace);

	locationWorldSpace[1] += car_info.width / 2;
	newLocationWorldSpace[1] += car_info.width / 2;

	
	Morfit_object_convert_point_to_world_space(car_info.car_handle, locationWorldSpace, locationWorldSpace);
	Morfit_object_convert_point_to_world_space(car_info.car_handle, newLocationWorldSpace, newLocationWorldSpace);
	
	
	
	if(NO == Morfit_object_is_movement_possible(car_info.car_handle, locationWorldSpace,newLocationWorldSpace,
										&intersectedPoly,intersection,&intersectedObject))

		return FALSE;

	


	return TRUE;
	
}
