package electrosphere.server.datacell.physics;

import org.joml.Vector3d;
import org.ode4j.ode.DContactGeom;
import org.ode4j.ode.DGeom;

import electrosphere.collision.CollisionEngine.CollisionResolutionCallback;
import electrosphere.collision.collidable.Collidable;
import electrosphere.entity.Entity;
import electrosphere.entity.state.attack.ServerAttackTree;
import electrosphere.entity.state.hitbox.HitboxCollectionState;
import electrosphere.entity.state.hitbox.HitboxCollectionState.HitboxState;
import electrosphere.entity.state.hitbox.HitboxCollectionState.HitboxType;
import electrosphere.entity.state.life.ServerLifeTree;
import electrosphere.entity.types.attach.AttachUtils;
import electrosphere.entity.types.item.ItemUtils;

/**
 * Callback for managing collisions on the server
 */
public class ServerHitboxResolutionCallback implements CollisionResolutionCallback {

    @Override
    public void resolve(DContactGeom contactGeom, Collidable impactor, Collidable receiver, Vector3d normal, Vector3d localPosition, Vector3d worldPos, float magnitude) {
        Entity impactorParent = impactor.getParent();
        Entity receiverParent = receiver.getParent();
        HitboxCollectionState impactorState = HitboxCollectionState.getHitboxState(impactorParent);
        HitboxCollectionState receiverState = HitboxCollectionState.getHitboxState(receiverParent);
        DGeom impactorGeom = contactGeom.g1;
        DGeom receiverGeom = contactGeom.g2;
        HitboxState impactorShapeStatus = impactorState.getShapeStatus(impactorGeom);
        HitboxState receiverShapeStatus = receiverState.getShapeStatus(receiverGeom);

        
        boolean impactorShapeStatusIsNull = impactorShapeStatus == null;
        boolean receiverShapeStatusIsNull = receiverShapeStatus == null;
        boolean impactorIsHit = impactorShapeStatus != null && impactorShapeStatus.getType() == HitboxType.HIT;
        boolean receiverIsHurt = receiverShapeStatus != null && receiverShapeStatus.getType() == HitboxType.HURT;
        boolean receiverIsBlock = receiverShapeStatus != null && (receiverShapeStatus.getType() == HitboxType.BLOCK || (receiverShapeStatus.getType() == HitboxType.HIT && receiverShapeStatus.isBlockOverride()));
        boolean parentsAreDifferent = AttachUtils.getParent(impactorParent) != receiverParent;
        boolean impactorCollisionBlocked = false;

        //check if the impactor thinks it can collide with the receiver
        if(impactorParent != null && ServerAttackTree.getServerAttackTree(impactorParent) != null){
            ServerAttackTree impactorAttackTree = ServerAttackTree.getServerAttackTree(impactorParent);
            //if we collide with the creature directly
            if(!impactorAttackTree.canCollideEntity(receiverParent)){
                impactorCollisionBlocked = true;
            }
            //if we collide with an item attached to the creature
            if(AttachUtils.hasParent(receiverParent) && !impactorAttackTree.canCollideEntity(AttachUtils.getParent(receiverParent))){
                impactorCollisionBlocked = true;
            }
        } else if(impactorParent != null && AttachUtils.hasParent(impactorParent) && AttachUtils.getParent(impactorParent) != null && ServerAttackTree.getServerAttackTree(AttachUtils.getParent(impactorParent)) != null){
            ServerAttackTree impactorAttackTree = ServerAttackTree.getServerAttackTree(AttachUtils.getParent(impactorParent));
            //if we collide with the creature directly
            if(!impactorAttackTree.canCollideEntity(receiverParent)){
                impactorCollisionBlocked = true;
            }
            //if we collide with an item attached to the creature
            if(AttachUtils.hasParent(receiverParent) && !impactorAttackTree.canCollideEntity(AttachUtils.getParent(receiverParent))){
                impactorCollisionBlocked = true;
            }
        }

        //check if is damage event
        boolean isDamageEvent = 
            !impactorShapeStatusIsNull &&
            !receiverShapeStatusIsNull &&
            impactorIsHit &&
            receiverIsHurt &&
            parentsAreDifferent &&
            !impactorCollisionBlocked
        ;

        //check if is block event
        boolean isBlockEvent = 
            !impactorShapeStatusIsNull &&
            !receiverShapeStatusIsNull &&
            impactorIsHit &&
            receiverIsBlock &&
            parentsAreDifferent &&
            !impactorCollisionBlocked
        ;
        

        if(!impactorShapeStatusIsNull){
            impactorShapeStatus.setHadCollision(true);
        }
        if(!receiverShapeStatusIsNull){
            receiverShapeStatus.setHadCollision(true);
        }

        if(isDamageEvent){
            //if the entity is attached to is an item, we need to compare with the parent of the item
            //to make sure you don't stab yourself for instance
            boolean isItem = ItemUtils.isItem(impactorParent);//hitboxParent.containsKey(EntityDataStrings.ITEM_IS_ITEM);
            Entity hitboxAttachParent = AttachUtils.getParent(impactorParent);

            //
            //handle receiver
            if(isItem){
                if(hitboxAttachParent != receiverParent){
                    ServerLifeTree serverLifeTree = ServerLifeTree.getServerLifeTree(receiverParent);
                    serverLifeTree.addCollisionEvent(impactorParent, impactorShapeStatus, receiverShapeStatus, localPosition, isDamageEvent, isBlockEvent);
                }
            }

            //
            //handle attacker
            this.handleAttackerCollision(impactorParent,receiverParent);
        }

        if(isBlockEvent){
            //
            //handle receiver
            boolean receiverIsItem = ItemUtils.isItem(receiverParent);
            boolean receiverHasParent = AttachUtils.hasParent(receiverParent);
            if(receiverIsItem && receiverHasParent){
                //item is equipped to something
                ServerLifeTree serverLifeTree = ServerLifeTree.getServerLifeTree(AttachUtils.getParent(receiverParent));
                if(serverLifeTree != null){
                    serverLifeTree.addCollisionEvent(impactorParent, impactorShapeStatus, receiverShapeStatus, localPosition, isDamageEvent, isBlockEvent);
                }
            } else {
                //attacking an item that is not equipped to anything
                ServerLifeTree serverLifeTree = ServerLifeTree.getServerLifeTree(receiverParent);
                if(serverLifeTree != null){
                    serverLifeTree.addCollisionEvent(impactorParent, impactorShapeStatus, receiverShapeStatus, localPosition, isDamageEvent, isBlockEvent);
                }
            }
            
            //
            //handle attacker
            this.handleAttackerCollision(impactorParent,receiverParent);
        }
    }

    /**
     * Handles collision tracking from the impactor's side
     * @param impactorParent The impactor hitbox's parent entity
     * @param receiverParent The receiver hitbox's parent entity
     */
    private void handleAttackerCollision(Entity impactorParent, Entity receiverParent){
        boolean receiverIsItem = ItemUtils.isItem(receiverParent);
        boolean receiverHasParent = AttachUtils.hasParent(receiverParent);

        if(impactorParent != null && ServerAttackTree.getServerAttackTree(impactorParent) != null){
            ServerAttackTree impactorAttackTree = ServerAttackTree.getServerAttackTree(impactorParent);
            impactorAttackTree.collideEntity(receiverParent);
            //if the receiver is an item that is equipped, collide with parent too
            if(receiverIsItem && receiverHasParent){
                impactorAttackTree.collideEntity(AttachUtils.getParent(receiverParent));
            }
        } else if(impactorParent != null && AttachUtils.hasParent(impactorParent) && AttachUtils.getParent(impactorParent) != null && ServerAttackTree.getServerAttackTree(AttachUtils.getParent(impactorParent)) != null){
            ServerAttackTree impactorAttackTree = ServerAttackTree.getServerAttackTree(AttachUtils.getParent(impactorParent));
            impactorAttackTree.collideEntity(receiverParent);
            //if the receiver is an item that is equipped, collide with parent too
            if(receiverIsItem && receiverHasParent){
                impactorAttackTree.collideEntity(AttachUtils.getParent(receiverParent));
            }
        }
    }
    
}
