package com.jan.Collision;

import java.util.ArrayList;

import com.badlogic.gdx.maps.MapLayer;
import com.badlogic.gdx.maps.MapObject;
import com.badlogic.gdx.maps.objects.RectangleMapObject;
import com.badlogic.gdx.maps.tiled.TiledMap;
import com.badlogic.gdx.math.Rectangle;
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.Box2DDebugRenderer;
import com.badlogic.gdx.physics.box2d.Contact;
import com.badlogic.gdx.physics.box2d.ContactImpulse;
import com.badlogic.gdx.physics.box2d.ContactListener;
import com.badlogic.gdx.physics.box2d.Manifold;
import com.badlogic.gdx.physics.box2d.PolygonShape;
import com.badlogic.gdx.physics.box2d.World;
import com.badlogic.gdx.physics.box2d.BodyDef.BodyType;
import com.jan.Collision.CollisionEvent.COLLISION_STATE;
import com.jan.game.Display;
import com.jan.game.SceneManager;
import com.jan.scene.Entity;

public class Collision {
    public static final String GEOMETRY_LAYER = "Geometry";
    private String[] collisionTypes = {"Actor", "Trigger"};
    
    private Box2DDebugRenderer debugRenderer = new Box2DDebugRenderer();

    private float accumulator = 0;
    private World physicsWorld;

    private ArrayList<ColiderInterface> colliders;

    public Collision(TiledMap world){
        debugRenderer = new Box2DDebugRenderer();
        physicsWorld = new World(new Vector2(0, -98f), true);
        MapLayer geometryLayer = world.getLayers().get(GEOMETRY_LAYER);
        GeometryLoader loader = new GeometryLoader(geometryLayer);
        physicsWorld = loader.getWorld();
        loader = null;
        colliders = new ArrayList<>();
        physicsWorld.setContactListener(new ListenerClass());
    }

    public void loadColiders(ArrayList<Entity> renderables){
        for(int i = 0; i < renderables.size(); i++){
            if(hasCollider(renderables.get(i).getEntityClass())){
                ColiderInterface actor = (ColiderInterface) renderables.get(i);
                actor.getColider().loadBody(physicsWorld);
                colliders.add(actor);
            }
        }
    }

    private boolean hasCollider(String entityType){
        for(int i = 0; i < collisionTypes.length; i++){
            if(entityType.equals(collisionTypes[i]))
                return true;  
        }
        return false;
    }

    public void update(float deltaTime){
        float frameTime = Math.min(deltaTime, 0.25f);
        accumulator += frameTime;
        while (accumulator >= 1/60f) {
            physicsWorld.step(1/60f, 6, 2);
            accumulator -= 1/60f;
        }
        updateBody();
        debugRenderer.render(physicsWorld, Display.getCamera().combined);
    }

    private void updateBody(){
        for(int i = 0; i < colliders.size(); i++)
            colliders.get(i).getColider().updatePos();
    }
}

class GeometryLoader{
    private World collWorld;

    public GeometryLoader(MapLayer collisionData){
        collWorld = new World(new Vector2(0, -98f), true);
        for(int i = 0; i < collisionData.getObjects().getCount(); i++)
            loadBody(collisionData.getObjects().get(i));
    }

    private void loadBody(MapObject curr){
        if(curr instanceof RectangleMapObject){
            RectangleMapObject a = (RectangleMapObject)curr;
            loadRectangle(a.getRectangle());
        }
    }

    private void loadRectangle(Rectangle objRect){
            BodyDef bodyDef = new BodyDef();
            bodyDef.type = BodyType.StaticBody;
            float bodyX = (objRect.getX() + (objRect.getWidth() / 2)) / Display.getScale();
            float bodyY = (objRect.getY() + (objRect.getHeight() / 2)) / Display.getScale();
            bodyDef.position.set(bodyX, bodyY);

            Body groundBody = collWorld.createBody(bodyDef);
            PolygonShape groundBox = new PolygonShape();  
            groundBox.setAsBox((objRect.width /2) / Display.getScale(), (objRect.height /2) / Display.getScale());
            groundBody.createFixture(groundBox, 0.0f);
            groundBody.setUserData(0);
            groundBox.dispose();
    }

    public World getWorld(){ return collWorld; }
}

class ListenerClass implements ContactListener {

        public void beginContact(Contact contact){
           determenCollision(contact.getFixtureA().getBody().getUserData(), contact.getFixtureB().getBody().getUserData(), COLLISION_STATE.BEGIN, contact);
        }

        public void endContact(Contact contact) {
           determenCollision(contact.getFixtureA().getBody().getUserData(), contact.getFixtureB().getBody().getUserData(), COLLISION_STATE.END, contact);
        }

        public void preSolve(Contact contact, Manifold oldManifold) {}
        public void postSolve(Contact contact, ContactImpulse impulse) {}

        public void determenCollision(Object targetA, Object targetB, COLLISION_STATE state, Contact contact){
            if(regulairCollision(targetA, targetB, state, contact))
                return;
            ObjectCollision(targetA, targetB, state, contact);
        }

        public boolean regulairCollision(Object targetA, Object targetB, COLLISION_STATE state, Contact contact){
            if(targetA instanceof Integer && targetB instanceof Colider){
                SceneManager.fireEvent(new CollisionEvent((int) targetA, (Colider) targetB, state, contact));
                return true;
            }
            return false;
        }

        public void ObjectCollision(Object targetA, Object targetB, COLLISION_STATE state, Contact contact){
            if(targetA instanceof Colider && targetB instanceof Colider){
                SceneManager.fireEvent(new CollisionEvent((Colider) targetA, (Colider) targetB, state, contact));
            }
        }
}