/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2016 Tellerva, Marc Lawrence
 *
 * Permission is hereby granted, free of charge, to any
 * person obtaining a copy of this software and associated
 * documentation files (the "Software"), to deal in the
 * Software without restriction, including without
 * limitation the rights to use, copy, modify, merge,
 * publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software
 * is furnished to do so, subject to the following
 * conditions:
 *
 * The above copyright notice and this permission notice
 * shall be included in all copies or substantial portions
 * of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
 * KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
 * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
 * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

package me.scarlet.undertailor.engine.overworld;

import com.badlogic.gdx.math.Vector2;
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.World;

import me.scarlet.undertailor.engine.Collider;
import me.scarlet.undertailor.engine.Destructible;
import me.scarlet.undertailor.gfx.MultiRenderer;
import me.scarlet.undertailor.util.Pair;

/**
 * Handles collision between {@link Collider}s within an
 * Overworld.
 */
public class CollisionHandler implements Destructible {

    public static final float PHYSICS_STEP = 1F / 60F;

    static final Pair<Collider> RETURN_PAIR;
    static final ContactListener LISTENER;

    static {
        RETURN_PAIR = new Pair<>();
        LISTENER = new ContactListener() {

            @Override
            public void preSolve(Contact contact, Manifold oldManifold) {
                Pair<Collider> pair = checkObjects(contact);
                if (pair != null) {
                    if (!pair.getA().canCollide() || !pair.getB().canCollide()
                        || (pair.getA().getGroupId() < 0
                            && pair.getA().getGroupId() == pair.getB().getGroupId())) {
                        contact.setEnabled(false);
                    }
                }
            }

            @Override
            public void postSolve(Contact contact, ContactImpulse impulse) {}

            @Override
            public void endContact(Contact contact) {
                Pair<Collider> pair = checkObjects(contact);
                if (pair != null) {
                    pair.getA().endCollision(pair.getB());
                    pair.getB().endCollision(pair.getA());
                }
            }

            @Override
            public void beginContact(Contact contact) {
                Pair<Collider> pair = checkObjects(contact);
                if (pair != null) {
                    pair.getA().startCollision(pair.getB());
                    pair.getB().startCollision(pair.getA());
                }
            }
        };
    }

    /**
     * Internal method.
     * 
     * <p>Quick check method to ensure eligibility for
     * intereference by this {@link CollisionHandler}.</p>
     */
    private static Pair<Collider> checkObjects(Contact contact) {
        Object a = contact.getFixtureA().getBody().getUserData();
        Object b = contact.getFixtureB().getBody().getUserData();
        if (a instanceof Collider && b instanceof Collider) {
            CollisionHandler.RETURN_PAIR.setItems((Collider) a, (Collider) b);
            return CollisionHandler.RETURN_PAIR;
        }

        return null;
    }

    // ---------------- object ----------------

    private World world;
    private float timeAccumulator;
    private MultiRenderer mRenderer;
    private Box2DDebugRenderer renderer;

    public CollisionHandler(MultiRenderer mRenderer, boolean renderCollision) {
        this.mRenderer = mRenderer;
        this.reset();

        this.renderer = new Box2DDebugRenderer(true, true, true, true, false, true);
    }

    @Override
    public void destroy() {
        this.world.dispose();
        this.renderer.dispose();

        this.world = null;
        this.renderer = null;
    }

    /**
     * Resets this {@link CollisionHandler}, disposing of
     * the old {@link World} and creating a new one.
     */
    public void reset() {
        this.timeAccumulator = 0F;
        if (this.world != null) {
            this.world.dispose();
        }

        this.world = new World(new Vector2(0F, 0F), true);
        this.world.setContactListener(CollisionHandler.LISTENER);
    }

    /**
     * Returns the {@link World} currently being used by
     * this {@link CollisionHandler}.
     * 
     * @return za warudo
     */
    public World getWorld() {
        return this.world;
    }

    /**
     * Steps the physics simulation by the provided amount
     * of time.
     * 
     * @param delta the time since the last frame
     */
    public void step(float delta) {
        this.timeAccumulator += delta;
        while (this.timeAccumulator > PHYSICS_STEP) {
            this.world.step(PHYSICS_STEP, 6, 2);
            this.timeAccumulator -= PHYSICS_STEP;
        }
    }

    /**
     * Draws the debug renderer, showing debug data for
     * physics objects currently in the owning Overworld.
     */
    public void render() {
        this.renderer.render(world, mRenderer.getBatchProjectionMatrix());
    }
}

@@ -2456,39 +2456,12 @@
 d() 
-%3C 0%0A                           
+%3E= 0
  && 

Fix improper group ID checks.