package nz.ac.massey.javaecs.examples.Systems;

import java.util.BitSet;

import nz.ac.massey.javaecs.ECSSystem;
import nz.ac.massey.javaecs.Engine;
import nz.ac.massey.javaecs.Entity;
import nz.ac.massey.javaecs.examples.Components.Collider;
import nz.ac.massey.javaecs.examples.Components.RidgidBody;
import nz.ac.massey.javaecs.examples.Components.Vec2D;

public class CollisionSystem extends ECSSystem {
    Engine gameEngine;

    public CollisionSystem(Engine gameEngine) {
        this.gameEngine = gameEngine;
        // Set registrations
        registrationSet = new BitSet();
        registrationSet.set(gameEngine.getComponentIndex(Vec2D.class));
        registrationSet.set(gameEngine.getComponentIndex(Collider.class));
        registrationSet.set(gameEngine.getComponentIndex(RidgidBody.class));
    }

    @Override
    public void init() {
    }

    // This is a quick & naive physics implementation.
    // I expect it is highly inefficient, though it is able to run it with 500
    // subscribed entities
    // with no major performance issues
    // Replace this with a proper system as required - I.e. Box2D or etc.
    @Override
    public void update(double dt) {
        for (Entity entity : entities) {
            Vec2D pos = (Vec2D) gameEngine.getComponentData(entity, Vec2D.class);
            RidgidBody ridgidBody = (RidgidBody) gameEngine.getComponentData(entity, RidgidBody.class);
            Collider collider = (Collider) gameEngine.getComponentData(entity, Collider.class);
            if (ridgidBody.mass >= 0) {
                // Only ignore non-static colliders
                for (Entity entity2 : entities) {
                    // skip entities that have already been processed
                    Vec2D otherPos = (Vec2D) gameEngine.getComponentData(entity2, Vec2D.class);
                    RidgidBody otherRidgidBody = (RidgidBody) gameEngine.getComponentData(entity2, RidgidBody.class);
                    Collider otherCollider = (Collider) gameEngine.getComponentData(entity2, Collider.class);

                    if (otherRidgidBody.mass < 0) {
                        // Evaluate a collision in the top-left
                        boolean tlCollide = pos.x > otherPos.x && pos.x < otherPos.x + otherCollider.x
                                && pos.y > otherPos.y && pos.y < otherPos.y + otherCollider.y;
                        // Evaluate a collision in the bottom-left
                        boolean blCollide = pos.x > otherPos.x && pos.x < otherPos.x + otherCollider.x
                                && pos.y + collider.y > otherPos.y && pos.y + collider.y < otherPos.y + otherCollider.y;
                        // Evaluate a collision in the bottom-right
                        boolean brCollide = pos.x + collider.x > otherPos.x
                                && pos.x + collider.x < otherPos.x + otherCollider.x && pos.y + collider.y > otherPos.y
                                && pos.y + collider.y < otherPos.y + otherCollider.y;
                        // Evaluate a collision in the top-right
                        boolean trCollide = pos.x + collider.x > otherPos.x
                                && pos.x + collider.x < otherPos.x + otherCollider.x && pos.y > otherPos.y
                                && pos.y < otherPos.y + otherCollider.y;
                        if (tlCollide || blCollide || brCollide || trCollide) {
                            // Get the vels
                            double totalXVel = Math.abs(ridgidBody.xVel) + Math.abs(otherRidgidBody.xVel);
                            double totalYVel = Math.abs(ridgidBody.yVel) + Math.abs(otherRidgidBody.yVel);
                            double xMult = 0;
                            double yMult = 0;
                            double multTot = 1;
                            boolean fullEdge = false;
                            // left-hand collision
                            if (tlCollide && blCollide) {
                                fullEdge = true;
                                pos.x = otherPos.x + otherCollider.x;
                                xMult = multTot;
                            }
                            // right-hand collision
                            else if (brCollide && trCollide) {
                                fullEdge = true;
                                pos.x = otherPos.x - collider.x;
                                xMult = -multTot;
                            }
                            // Bottom collision
                            else if (blCollide && brCollide) {
                                fullEdge = true;
                                // pos.y = otherPos.y + otherCollider.y;
                                pos.y = otherPos.y - collider.y;
                                yMult = -multTot;
                            }
                            // top collision
                            else if (trCollide && tlCollide) {
                                fullEdge = true;
                                // pos.y = otherPos.y - collider.y;
                                pos.y = otherPos.y + otherCollider.y;
                                yMult = multTot;
                            }
                            if (fullEdge) {
                                    double newXVel = totalXVel * (xMult
                                            * (Math.abs(xMult) + (ridgidBody.bounciness * otherRidgidBody.bounciness)));
                                    double newYVel = totalYVel * (yMult
                                            * (Math.abs(yMult) + (ridgidBody.bounciness * otherRidgidBody.bounciness)));
                                    ridgidBody.xVel += newXVel;
                                    ridgidBody.yVel += newYVel;
                            }
                        }
                    }
                }
            }
        }
    }
}
