
Rect :: #type,distinct Vec(4, RECT_TYPE);

Circle :: struct {
   x,y: float;
   r: float;
   #place x;
   pos: Vec2;
}

Line :: struct {
   x0,y0,x1,y1: float;
   #place x0;
   a: Vec2;
   #place x1;
   b: Vec2;
}

make_rect :: (x: RECT_TYPE, y: RECT_TYPE, w: RECT_TYPE, h: RECT_TYPE) -> Rect {
   r: Rect = ---;
   r.x = x;
   r.y = y;
   r.width = w;
   r.height = h;
   return r;
}

cut_left :: (rect: *Rect, want: RECT_TYPE) -> Rect {
   amnt := basic.min(want, rect.width);
   r := rect.*;
   r.width = amnt;
   rect.x += amnt;
   rect.width -= amnt;
   return r;
}

cut_right :: (rect: *Rect, want: RECT_TYPE) -> Rect {
   amnt := basic.min(want, rect.width);
   r := rect.*;
   r.x += rect.width - amnt;
   r.width = amnt;
   rect.width -= r.width;
   return r;
}

cut_top :: (rect: *Rect, want: RECT_TYPE) -> Rect {
   amnt := basic.min(rect.height, want);
   r := rect.*;
   r.height -= amnt;
   rect.y += amnt;
   rect.height -= amnt;
   return r;
}

cut_bottom :: (rect: *Rect, want: RECT_TYPE) -> Rect {
   amnt := basic.min(want, rect.height);
   r := rect.*;
   r.y += r.height - amnt;
   r.height = amnt;
   rect.height -= amnt;
   return r;
}

area :: (r: Rect) -> float {
   return r.width*r.height;
}

area :: (c: Circle) -> float {
   return PI*c.r*c.r;
}

inside :: (r: Rect, p: Vec2) -> bool #symmetric {
   return p.x >= r.x && p.x <= r.x + r.width &&
          p.y >= r.y && p.y <= r.y + r.height;
}

inside :: (c: Circle, p: Vec2) -> bool #symmetric {
   dp := p - c.pos;
   return dp.x*dp.x + dp.y*dp.y <= c.r*c.r;
}

collides :: (c1: Circle, c2: Circle) -> bool {
   dp := c2.pos - c1.pos;
   return c1.r + c2.r >= length(dp);
}

// Note(Jesse): This is using sdfs. Very elegant
collides :: (c: Circle, r: Rect) -> bool #symmetric {
   // We need to 'transpose' all the math to the origin (centered at 0,0)
   r_center: Vec2 = get_center(r);
   p := r_center - c.pos;
   d: Vec2 = abs(p) - v2f(r.width/2, r.height/2);
   dist := length(max(d, 0.0)) + min(max(d.x, d.y), 0.0);
   dist -= c.r; // 'adding' the distance of the circle
   return dist <= 0.0;
}

// Note(Jesse): Minkowski difference
collides :: (r1: Rect, r2: Rect) -> bool {
   r: Rect = make_rect(r1.x - r2.width/2, r1.y - r2.width/2, r1.width + r2.width, r1.height + r2.height);
   return inside(r, get_center(r2));
}

collides :: (c: Circle, seg: Line) -> bool #symmetric {
   pa := c.pos - seg.a;
   ba := seg.b - seg.a;
   // Note(Jesse): Using clamp here will cause small segments to collide even when they don't
   h: float = min(max(dot(pa,ba)/dot(ba,ba), 0.0), 1.0);
   dist := length(pa - ba*h);
   return dist - c.r <= 0.0;
}

collides :: (s1: Line, s2: Line) -> bool {
   denom := ((s2.y1 - s2.y0)*(s1.x1 - s1.x0) - (s2.x1 - s2.x0)*(s1.y1 - s1.y0));

   a := ((s2.x1 - s2.x0)*(s1.y0 - s2.y0) - (s2.y1 - s2.y0)*(s1.x0 - s2.x0));
   b := ((s1.x1 - s1.x0)*(s1.y0 - s2.y0) - (s1.y1 - s1.y0)*(s1.x0 - s2.x0));

   if denom == 0.0
      return false;

   a /= denom;
   b /= denom;

   return (a >= 0 && a <= 1) && (b >= 0 && b <= 1);
}

// Todo(Jesse): I want to find a better line segment rectangle algorithm.
//    one option is a line clipping algorithm. Cohen-Sutherland algorithm
//    would be the best option. It does accepts/rejects fastest
//    but still has a while in it, and a few ifs.
collides :: (r: Rect, seg: Line) -> bool #symmetric {
   p1 := Vec2.{r.x,           r.y};
   p2 := Vec2.{r.x + r.width, r.y};
   p3 := Vec2.{r.x,           r.y + r.height};
   p4 := Vec2.{r.x + r.width, r.y + r.height};

   l1 := Line.{a=p1, b=p2};
   l2 := Line.{a=p1, b=p3};
   l3 := Line.{a=p3, b=p4};
   l4 := Line.{a=p2, b=p4};

   return collides(seg, l1) || collides(seg, l2) || collides(seg, l3) || collides(seg, l4);
}

///////
// Helpers and operators

get_center :: (r: Rect) -> Vec2 {
   return v2f(r.x + r.width/2, r.y + r.height/2);
}

#scope_file

basic :: #import "Basic";

#if RUN_TESTS #run,stallable {
   test.run(basic.tprint("%: Vec2", UNITS), t => {
   });
}
