/*
 * Copyright (c) 2008-2009 Bill Whitacre http://rampancy.g0dsoft.com
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
*/

#include "spring.h"

rat_spring *rat_spring_create(rat_body *body_a,rat_body *body_b,
	vector2 anch_a,vector2 anch_b,rat_real k,rat_real rest_length,
	rat_real damping)
{
	rat_spring *spring=(rat_spring *)rat_alloc(sizeof(rat_spring));
	if (!body_a)
	{
		rat_dealloc((void *)spring);
		return NULL;
	}

	spring->body_a=body_a;
	spring->anch_a=anch_a;

	spring->body_b=body_b;
	spring->anch_b=anch_b;

	spring->k=k;
	spring->rest_length=rest_length;
	spring->damping=damping;

	spring->activity=RAT_SPRING_ACTIVITY_NEUTRAL;

	if (body_a) rat_body_add_spring(body_a,spring);
	if (body_b) rat_body_add_spring(body_b,spring);

	return spring;
}

void rat_spring_destroy(rat_spring *spring)
{
	if (spring->body_a) rat_body_remove_spring(spring->body_a,spring);
	if (spring->body_b) rat_body_remove_spring(spring->body_b,spring);

	rat_dealloc((void *)spring);
}

void rat_spring_update_global_anchor(rat_spring *spring,vector2 new_anch_b)
{
	spring->anch_b=new_anch_b;
}

void rat_spring_integrate(rat_spring *spring,rat_real inv_timestep,rat_real solve_coef)
{
	register rat_real proportional_damping=powf(spring->damping==0.0?0.0:1.0/spring->damping,-solve_coef);
	register vector2 zero_vec={0,0};
	rat_real force,distance;
	vector2 impulse,delta,normal;

	if (!spring->body_a) return;

	if (spring->body_b)
	{
		vector2 world_anch_a=vector2_getrotated(spring->anch_a,rat_hull_get_angle(spring->body_a->hull));
		vector2 world_anch_b=vector2_getrotated(spring->anch_b,rat_hull_get_angle(spring->body_b->hull));
		
		vector2 delta=vector2_sub(vector2_add(rat_hull_get_pos(spring->body_b->hull),world_anch_b),vector2_add(rat_hull_get_pos(spring->body_a->hull),world_anch_a));
		rat_real distance=vector2_magnitude(delta);
		vector2 normal=distance!=0?vector2_multf(delta,1.0f/distance):zero_vec;
		
		force=-(distance-spring->rest_length)*spring->k*inv_timestep;
		if (force>0)
			force-=rat_maxf(fabs(force),proportional_damping);
		else
			force+=rat_maxf(fabs(force),proportional_damping);
		spring->activity=force>0?RAT_SPRING_ACTIVITY_PUSHING:RAT_SPRING_ACTIVITY_PULLING;
		impulse=vector2_multf(normal,force*solve_coef);

		rat_body_apply_offset_impulse(spring->body_a,vector2_negative(impulse),world_anch_a);
		rat_body_apply_offset_impulse(spring->body_b,impulse,world_anch_b);
	}
	else // second anchor is global
	{
		vector2 world_anch_a=vector2_getrotated(spring->anch_a,rat_hull_get_angle(spring->body_a->hull));
		
		vector2 delta=vector2_sub(spring->anch_b,vector2_add(rat_hull_get_pos(spring->body_a->hull),world_anch_a));
		rat_real distance=vector2_magnitude(delta);
		vector2 normal=distance?vector2_multf(delta,1.0f/distance):zero_vec;
		
		force=-(distance-spring->rest_length)*spring->k*inv_timestep;
		if (force>0)
			force-=rat_maxf(fabs(force),proportional_damping);
		else
			force+=rat_maxf(fabs(force),proportional_damping);
		spring->activity=force>0?RAT_SPRING_ACTIVITY_PUSHING:RAT_SPRING_ACTIVITY_PULLING;
		impulse=vector2_multf(normal,force*solve_coef);

		rat_body_apply_offset_impulse(spring->body_a,vector2_negative(impulse),world_anch_a);

	}
}
