/*
 * FXGL - JavaFX Game Library. The MIT License (MIT).
 * Copyright (c) AlmasB (almaslvl@gmail.com).
 * See LICENSE for details.
 */

package com.almasb.fxgl.physics.box2d.collision.shapes;

import com.almasb.fxgl.core.math.Vec2;
import com.almasb.fxgl.physics.box2d.collision.AABB;
import com.almasb.fxgl.physics.box2d.collision.RayCastInput;
import com.almasb.fxgl.physics.box2d.collision.RayCastOutput;
import com.almasb.fxgl.physics.box2d.common.JBoxSettings;
import com.almasb.fxgl.physics.box2d.common.JBoxUtils;
import com.almasb.fxgl.physics.box2d.common.Rotation;
import com.almasb.fxgl.physics.box2d.common.Transform;

/**
 * A circle shape.
 */
public final class CircleShape extends Shape {

    public final Vec2 center = new Vec2();

    public CircleShape(float radius) {
        this(0, 0, radius);
    }

    public CircleShape(Vec2 center, float radius) {
        this(center.x, center.y, radius);
    }

    public CircleShape(float centerX, float centerY, float radius) {
        super(ShapeType.CIRCLE, radius);
        center.set(centerX, centerY);
    }

    @Override
    public Shape clone() {
        return new CircleShape(center, getRadius());
    }

    @Override
    public int getChildCount() {
        return 1;
    }

    @Override
    public boolean containsPoint(final Transform transform, final Vec2 point) {
        // Rot.mulToOutUnsafe(transform.q, m_p, center);
        // center.addLocal(transform.p);
        //
        // final Vec2 d = center.subLocal(p).negateLocal();
        // return Vec2.dot(d, d) <= radius * radius;
        final Rotation q = transform.q;
        final Vec2 tp = transform.p;
        float centerx = -(q.c * center.x - q.s * center.y + tp.x - point.x);
        float centery = -(q.s * center.x + q.c * center.y + tp.y - point.y);

        return centerx * centerx + centery * centery <= getRadius() * getRadius();
    }

    @Override
    public float computeDistanceToOut(Transform xf, Vec2 p, int childIndex, Vec2 normalOut) {
        final Rotation xfq = xf.q;
        float centerx = xfq.c * center.x - xfq.s * center.y + xf.p.x;
        float centery = xfq.s * center.x + xfq.c * center.y + xf.p.y;
        float dx = p.x - centerx;
        float dy = p.y - centery;
        float d1 = JBoxUtils.sqrt(dx * dx + dy * dy);
        normalOut.x = dx * 1 / d1;
        normalOut.y = dy * 1 / d1;
        return d1 - getRadius();
    }

    // Collision Detection in Interactive 3D Environments by Gino van den Bergen
    // From Section 3.1.2
    // x = s + a * r
    // norm(x) = radius
    @Override
    public boolean raycast(RayCastOutput output, RayCastInput input, Transform transform,
                                 int childIndex) {

        final Vec2 inputp1 = input.p1;
        final Vec2 inputp2 = input.p2;
        final Rotation tq = transform.q;
        final Vec2 tp = transform.p;

        // Rot.mulToOutUnsafe(transform.q, m_p, position);
        // position.addLocal(transform.p);
        final float positionx = tq.c * center.x - tq.s * center.y + tp.x;
        final float positiony = tq.s * center.x + tq.c * center.y + tp.y;

        final float sx = inputp1.x - positionx;
        final float sy = inputp1.y - positiony;
        // final float b = Vec2.dot(s, s) - radius * radius;
        final float b = sx * sx + sy * sy - getRadius() * getRadius();

        // Solve quadratic equation.
        final float rx = inputp2.x - inputp1.x;
        final float ry = inputp2.y - inputp1.y;
        // final float c = Vec2.dot(s, r);
        // final float rr = Vec2.dot(r, r);
        final float c = sx * rx + sy * ry;
        final float rr = rx * rx + ry * ry;
        final float sigma = c * c - rr * b;

        // Check for negative discriminant and short segment.
        if (sigma < 0.0f || rr < JBoxSettings.EPSILON) {
            return false;
        }

        // Find the point of intersection of the line with the circle.
        float a = -(c + JBoxUtils.sqrt(sigma));

        // Is the intersection point on the segment?
        if (0.0f <= a && a <= input.maxFraction * rr) {
            a /= rr;
            output.fraction = a;
            output.normal.x = rx * a + sx;
            output.normal.y = ry * a + sy;
            output.normal.getLengthAndNormalize();
            return true;
        }

        return false;
    }

    @Override
    public void computeAABB(final AABB aabb, final Transform transform, int childIndex) {
        final Rotation tq = transform.q;
        final Vec2 tp = transform.p;
        final float px = tq.c * center.x - tq.s * center.y + tp.x;
        final float py = tq.s * center.x + tq.c * center.y + tp.y;

        aabb.lowerBound.x = px - getRadius();
        aabb.lowerBound.y = py - getRadius();
        aabb.upperBound.x = px + getRadius();
        aabb.upperBound.y = py + getRadius();
    }

    @Override
    public void computeMass(final MassData massData, final float density) {
        massData.mass = density * JBoxSettings.PI * getRadius() * getRadius();
        massData.center.x = center.x;
        massData.center.y = center.y;

        // inertia about the local origin
        // massData.I = massData.mass * (0.5f * radius * radius + Vec2.dot(m_p, m_p));
        massData.I = massData.mass * (0.5f * getRadius() * getRadius() + center.x * center.x + center.y * center.y);
    }
}

@@ -4379,22 +4379,16 @@
 uteAABB(
-final 
 AABB aab
@@ -4389,22 +4389,16 @@
 BB aabb,
- final
  Transfo
@@ -4442,142 +4442,40 @@
    f
-inal Rotation tq = transform.q;%0A        final Vec2 tp = transform.p;%0A        final float px = tq.c * center.x - tq.s * center.y + tp.x
+loat px = transform.mulX(center)
 ;%0A  
@@ -4476,30 +4476,24 @@
 r);%0A        
-final 
 float py = t
@@ -4496,47 +4496,29 @@
  = t
-q.s * center.x + tq.c * center.y + tp.y
+ransform.mulY(center)
 ;%0A%0A 

refactor computeAABB()