package electrosphere.renderer.pipelines;

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

import electrosphere.engine.Globals;
import electrosphere.entity.Entity;
import electrosphere.entity.EntityDataStrings;
import electrosphere.entity.EntityTags;
import electrosphere.entity.EntityUtils;
import electrosphere.entity.types.camera.CameraEntityUtils;
import electrosphere.renderer.OpenGLState;
import electrosphere.renderer.RenderPipelineState;
import electrosphere.renderer.RenderingEngine;
import electrosphere.renderer.actor.Actor;

public class NormalsForOutlinePipeline implements RenderPipeline {

    @Override
    public void render(OpenGLState openGLState, RenderPipelineState renderPipelineState) {
        Globals.profiler.beginCpuSample("NormalsForOutlinePipeline.render");
        /*
        gameImageNormalsTexture;
    static Framebuffer gameImageNormalsFramebuffer;
    static ShaderProgram renderNormalsShader;
        */

        //bind screen fbo
        RenderingEngine.gameImageNormalsFramebuffer.bind(openGLState);
        openGLState.glDepthTest(true);
        openGLState.glBlend(false);
        openGLState.glBlendFunc(GL40.GL_SRC_ALPHA, GL40.GL_ONE_MINUS_SRC_ALPHA);
        openGLState.glDepthFunc(GL40.GL_LESS);
        GL40.glDepthMask(true);
        
        openGLState.glViewport(Globals.userSettings.getRenderResolutionX(), Globals.userSettings.getRenderResolutionY());
        
        ///
        ///     R E N D E R I N G      S T U F F
        ///
        //Sets the background color.
        GL40.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
        GL40.glClear(GL40.GL_COLOR_BUFFER_BIT | GL40.GL_DEPTH_BUFFER_BIT);



        //
        // Set render pipeline state
        //
        renderPipelineState.setUseMeshShader(false);
        renderPipelineState.setBufferStandardUniforms(true);
        renderPipelineState.setBufferNonStandardUniforms(false);
        renderPipelineState.setUseMaterial(true);
        renderPipelineState.setUseShadowMap(true);
        renderPipelineState.setUseBones(true);
        renderPipelineState.setUseLight(true);



        Matrix4d modelTransformMatrix = new Matrix4d();

        openGLState.setActiveShader(renderPipelineState, RenderingEngine.renderNormalsShader);

        for(Entity currentEntity : Globals.clientScene.getEntitiesWithTag(EntityTags.DRAWABLE)){
            Vector3d position = EntityUtils.getPosition(currentEntity);
            if(shouldDraw(currentEntity)){
                //fetch actor
                Actor currentActor = EntityUtils.getActor(currentEntity);
                //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(currentEntity));
                modelTransformMatrix.scale(new Vector3d(EntityUtils.getScale(currentEntity)));
                currentActor.applySpatialData(modelTransformMatrix,position);
                //draw
                currentActor.draw(renderPipelineState,openGLState);
            }
        }

        Globals.profiler.endCpuSample();
    }

    /**
     * Checks if the entity should be drawn
     * @param entity The entity
     * @return true if should draw, false otherwise
     */
    static boolean shouldDraw(Entity entity){
        return
        (
            (boolean)entity.getData(EntityDataStrings.DATA_STRING_DRAW) && 
            entity.getData(EntityDataStrings.DRAW_SOLID_PASS) != null && 
            entity.getData(EntityDataStrings.DRAW_OUTLINE) != null
        ) &&
        (
            !entityBlacklist(entity)
        )
        ;
    }

    /**
     * Checks whether the entity is on the blacklist for drawing in main pipeline or not
     * @param entity The entity
     * @return True if in blacklist, false otherwise
     */
    static boolean entityBlacklist(Entity entity){
        return 
        //don't draw first person view in this pipeline ever
        entity == Globals.firstPersonEntity ||

        //don't draw third person view if camera is first person
        (
            !Globals.controlHandler.cameraIsThirdPerson() &&
            entity == Globals.playerEntity
        )
        ;
    }
    
}
