#include "physics/collide.hpp"

namespace physics {

  int collision_detector::sphere_and_sphere(const collision_sphere& a,
                                            const collision_sphere& b,
                                            collision_data& data)
  {
    if (data.ix >= data.length)
      return 0;

    vec3 a_position = col(a.transform, 3);
    vec3 b_position = col(b.transform, 3);

    vec3 mid = a_position - b_position;
    float length = magnitude(mid);

    if (length >= (a.radius + b.radius)) {
      return 0;
    }

    vec3 normal = mid * (1.0f / length);
    contact * contact = data.add_contact();
    contact->contact_normal = normal;
    contact->contact_point = a_position + mid * 0.5f;
    contact->penetration = a.radius + b.radius - length;
    contact->body[0] = a.body;
    contact->body[1] = b.body;
    contact->restitution = data.restitution;
    contact->friction = data.friction;

    return 1;
  }

  int collision_detector::sphere_and_halfspace(const collision_sphere& sphere,
                                               const collision_plane& plane,
                                               collision_data& data)
  {
    if (data.ix >= data.length)
      return 0;

    vec3 position = col(sphere.transform, 3);
    float distance = dot(plane.normal, position) - sphere.radius - plane.distance;

    if (distance >= 0)
      return 0;

    contact * contact = data.add_contact();
    contact->contact_normal = plane.normal;
    contact->penetration = -distance;
    contact->contact_point = position - plane.normal * (distance + sphere.radius);
    contact->body[0] = sphere.body;
    contact->body[1] = nullptr;
    contact->restitution = data.restitution;
    contact->friction = data.friction;

    return 1;
  }

  int collision_detector::sphere_and_plane(const collision_sphere& sphere,
                                           const collision_plane& plane,
                                           collision_data& data)
  {
    if (data.ix >= data.length)
      return 0;

    vec3 position = col(sphere.transform, 3);

    float distance = dot(plane.normal, position) - plane.distance;

    if (distance * distance > sphere.radius * sphere.radius) {
      return 0;
    }

    vec3 normal;
    float penetration;
    if (distance < 0) {
      normal = -plane.normal;
      penetration = distance;
    } else {
      normal = plane.normal;
      penetration = -distance;
    }
    penetration += sphere.radius;

    contact * contact = data.add_contact();
    contact->contact_normal = normal;
    contact->penetration = penetration;
    contact->contact_point = position - plane.normal * distance;
    contact->body[0] = sphere.body;
    contact->body[1] = nullptr;
    contact->restitution = data.restitution;
    contact->friction = data.friction;

    return 1;
  }

  int collision_detector::box_and_halfspace(const collision_box& box,
                                            const collision_plane& plane,
                                            collision_data& data)
  {
    if (data.ix >= data.length)
      return 0;

    return 0;
  }
}
