package electrosphere.renderer.pipelines;

import org.joml.Matrix4d;
import org.joml.Quaterniond;
import org.joml.Vector3d;
import org.joml.Vector3f;
import org.joml.Vector4d;
import org.lwjgl.opengl.GL40;

import electrosphere.engine.Globals;
import electrosphere.entity.Entity;
import electrosphere.entity.EntityUtils;
import electrosphere.entity.state.client.firstPerson.FirstPersonTree;
import electrosphere.entity.state.equip.ClientEquipState;
import electrosphere.entity.types.attach.AttachUtils;
import electrosphere.entity.types.camera.CameraEntityUtils;
import electrosphere.renderer.OpenGLState;
import electrosphere.renderer.RenderPipelineState;
import electrosphere.renderer.actor.Actor;

/**
 * Renders content that should only be rendered in first person (ie the view model/hands/whatever)
 */
public class FirstPersonItemsPipeline implements RenderPipeline {

    //internal model matrix
    Matrix4d modelTransformMatrix = new Matrix4d();

    @Override
    public void render(OpenGLState openGLState, RenderPipelineState renderPipelineState) {

        if(Globals.firstPersonEntity != null && !Globals.controlHandler.cameraIsThirdPerson()){
            //update logic
            if(Globals.cameraHandler.getTrackPlayerEntity()){
                updateFirstPersonModelPosition(Globals.firstPersonEntity);
            }

            //setup opengl state
            renderPipelineState.setUseBones(true);
            renderPipelineState.setUseLight(false);
            renderPipelineState.setUseMaterial(true);
            renderPipelineState.setUseMeshShader(true);
            renderPipelineState.setUseShadowMap(false);
            renderPipelineState.setBufferStandardUniforms(true);
            renderPipelineState.setFrustumCheck(false);

            openGLState.glDepthTest(true);
            openGLState.glDepthFunc(GL40.GL_LESS);
            GL40.glDepthMask(true);

            //render

            //
            //Draw viewmodel
            {
                Vector3d position = EntityUtils.getPosition(Globals.firstPersonEntity);
                Actor actor = EntityUtils.getActor(Globals.firstPersonEntity);
                //calculate camera-modified vector3f
                Vector3f cameraModifiedPosition = new Vector3f((float)position.x,(float)position.y,(float)position.z).sub(CameraEntityUtils.getCameraCenter(Globals.playerCamera));
                //calculate and apply model transform
                modelTransformMatrix.identity();
                modelTransformMatrix.translate(cameraModifiedPosition);
                modelTransformMatrix.rotate(EntityUtils.getRotation(Globals.firstPersonEntity));
                modelTransformMatrix.scale(new Vector3d(EntityUtils.getScale(Globals.firstPersonEntity)));
                actor.applySpatialData(modelTransformMatrix,position);
                //draw
                actor.draw(renderPipelineState, openGLState);
            }

            //draw children of viewmodel
            if(AttachUtils.hasChildren(Globals.firstPersonEntity)){
                for(Entity child : AttachUtils.getChildrenList(Globals.firstPersonEntity)){
                    Vector3d position = EntityUtils.getPosition(child);
                    Actor actor = EntityUtils.getActor(child);
                    //calculate camera-modified vector3f
                    Vector3f cameraModifiedPosition = new Vector3f((float)position.x,(float)position.y,(float)position.z).sub(CameraEntityUtils.getCameraCenter(Globals.playerCamera));
                    //calculate and apply model transform
                    modelTransformMatrix.identity();
                    modelTransformMatrix.translate(cameraModifiedPosition);
                    modelTransformMatrix.rotate(new Quaterniond(EntityUtils.getRotation(child)));
                    modelTransformMatrix.scale(new Vector3d(EntityUtils.getScale(child)));
                    actor.applySpatialData(modelTransformMatrix,position);
                    //draw
                    actor.draw(renderPipelineState, openGLState);
                }
            }


        }

    }

    /**
     * Updates the position and rotation of the first person model
     */
    private void updateFirstPersonModelPosition(Entity target){

        FirstPersonTree tree = FirstPersonTree.getTree(target);

        Matrix4d rotationMat = CameraEntityUtils.getRotationMat(Globals.playerCamera);
        EntityUtils.getRotation(Globals.firstPersonEntity).set(CameraEntityUtils.getRotationQuat(Globals.playerCamera));

        Vector3d playerPos = EntityUtils.getPosition(Globals.playerEntity);
        Vector4d behindCameraOffsetRaw = rotationMat.transform(new Vector4d(0,tree.getCameraViewDirOffsetY(),tree.getCameraViewDirOffsetZ(),1)); //pushes the model behind the camera
        Vector3d behindCameraOffset = new Vector3d(behindCameraOffsetRaw.x,behindCameraOffsetRaw.y,behindCameraOffsetRaw.z);
        EntityUtils.getPosition(Globals.firstPersonEntity).set(playerPos).add(0.0f,tree.getHeightFromOrigin(),0.0f).add(behindCameraOffset);

        if(ClientEquipState.hasEquipState(Globals.playerEntity)){
            ClientEquipState clientEquipState = ClientEquipState.getClientEquipState(Globals.playerEntity);
            clientEquipState.evaluatePlayerAttachments();
        }
    }
    
}
