package com.homescreenarcade.pinball.elements;

import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.Body;
import com.badlogic.gdx.physics.box2d.BodyDef;
import com.badlogic.gdx.physics.box2d.CircleShape;
import com.badlogic.gdx.physics.box2d.FixtureDef;
import com.badlogic.gdx.physics.box2d.PolygonShape;
import com.badlogic.gdx.physics.box2d.World;

/**
 * Methods to create Box2D shapes.
 */

public class Box2DFactory {

    /** Creates a circle object with the given position and radius. Resitution defaults to 0.6. */
    public static Body createCircle(World world, float x, float y, float radius, boolean isStatic) {
        CircleShape sd = new CircleShape();
        sd.setRadius(radius);

        FixtureDef fdef = new FixtureDef();
        fdef.shape = sd;
        fdef.density = 1.0f;
        fdef.friction = 0.3f;
        fdef.restitution = 0.6f;

        BodyDef bd = new BodyDef();
        bd.allowSleep = true;
        bd.position.set(x, y);
        Body body = world.createBody(bd);
        body.createFixture(fdef);
        if (isStatic) {
            body.setType(BodyDef.BodyType.StaticBody);
        }
        else {
            body.setType(BodyDef.BodyType.DynamicBody);
        }
        return body;
    }

    /**
     * Creates a wall by constructing a rectangle whose corners are (xmin,ymin) and (xmax,ymax),
     * and rotating the box counterclockwise through the given angle. Restitution defaults to 0.
     */
    public static Body createWall(World world, float xmin, float ymin, float xmax, float ymax, float angle) {
        return createWall(world, xmin, ymin, xmax, ymax, angle, 0f);
    }

    /**
     * Creates a wall by constructing a rectangle whose corners are (xmin,ymin) and (xmax,ymax),
     * and rotating the box counterclockwise through the given angle, with specified restitution.
     */
    public static Body createWall(World world, float xmin, float ymin, float xmax, float ymax,
            float angle, float restitution) {
        float cx = (xmin + xmax) / 2;
        float cy = (ymin + ymax) / 2;
        float hx = Math.abs((xmax - xmin) / 2);
        float hy = Math.abs((ymax - ymin) / 2);
        PolygonShape wallshape = new PolygonShape();
        // Don't set the angle here; instead call setTransform on the body below. This allows future
        // calls to setTransform to adjust the rotation as expected.
        wallshape.setAsBox(hx, hy, new Vector2(0f, 0f), 0f);

        FixtureDef fdef = new FixtureDef();
        fdef.shape = wallshape;
        fdef.density = 1.0f;
        if (restitution>0) fdef.restitution = restitution;

        BodyDef bd = new BodyDef();
        bd.position.set(cx, cy);
        Body wall = world.createBody(bd);
        wall.createFixture(fdef);
        wall.setType(BodyDef.BodyType.StaticBody);
        wall.setTransform(cx, cy, angle);
        return wall;
    }

    /** Creates a segment-like thin wall with 0.05 thickness going from (x1,y1) to (x2,y2) */
    public static Body createThinWall(World world, float x1, float y1, float x2, float y2, float restitution) {
        // Determine center point and rotation angle for createWall.
        float cx = (x1 + x2) / 2;
        float cy = (y1 + y2) / 2;
        float angle = (float)Math.atan2(y2-y1, x2-x1);
        float mag = (float)Math.hypot(y2-y1, x2-x1);
        return createWall(world, cx - mag/2, cy-0.05f, cx + mag/2, cy+0.05f, angle, restitution);
    }

}