package de.frajul.particlelab.rendering;

import android.content.Context;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import de.frajul.particlelab.data.Color;
import de.frajul.particlelab.data.Vector;
import de.frajul.particlelab.entities.Entity;
import de.frajul.particlelab.entities.particles.ParticleEffect;
import de.frajul.particlelab.entities.particles.ParticleSource;
import de.frajul.particlelab.entities.particles.ParticleSystem;
import de.frajul.particlelab.entities.textures.TexturePack;
import de.frajul.particlelab.main.GameLog;
import de.frajul.particlelab.main.Scene;

/**
 * Created by Julian on 22.11.2015.
 */
public class GameRenderer implements GLSurfaceView.Renderer {

    private Rendering rendering;
    private MatrixCreator matrixCreator;
    private boolean additiveBlending;

    private Color emptyColor;
    private Context context;
    private Quad quad;
    //GL Context
    private ShaderProgram shaderProgram;
    private TexturePack texturePack;
    private Timer timer;

    public GameRenderer(Context context, Rendering rendering) {
        this.context = context;
        this.rendering = rendering;
        emptyColor = new Color(-1, -1, -1);
        matrixCreator = new MatrixCreator();
        quad = new Quad();
    }

    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        GameLog.d("onSurfaceCreated");
        GLES20.glClearColor(0, 0, 0, 1.0f);
        gl.glEnable(GL10.GL_BLEND);
        gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);
        try {
            shaderProgram = new ShaderProgram(context);
            texturePack = new TexturePack(context);
            timer = new Timer();
        } catch (Exception e) {
            rendering.onException(e);
        }
        rendering.initiate(texturePack, timer);
    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        GameLog.d("onSurfaceChanged: width=" + width + ", height=" + height);
        GLES20.glViewport(0, 0, width, height);
        matrixCreator.setMVPMSize(width, height);
        rendering.setScreenSize(width, height);
    }

    @Override
    public void onDrawFrame(GL10 gl) {
        timer.update();
        rendering.update();
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);

        Scene scene = rendering.getScene();
        ParticleSystem particleSystem = scene.getParticleSystem();

//        GLES20.glClearColor(0, 0, 0, 0.0f);
//        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
//
//        shaderProgram.start();
//        shaderProgram.loadMVPMatrix(matrixCreator, scene.cameraX);
//        synchronized (scene.getParticleSystem().getEffects()) {
//            for (ParticleEffect effect : scene.getParticleSystem().getEffects()) {
//                gl.glActiveTexture(GL10.GL_TEXTURE0);
//                gl.glBindTexture(GL10.GL_TEXTURE_2D, effect.getTexture().getId());
//                shaderProgram.loadTextureAtlasInfos(effect.getTexture());
//
//                switchAdditiveBlending(gl, effect.getOptions().isAdditive());
//                synchronized (effect.getSources()) {
//                    for (ParticleSource source : effect.getSources()) {
//                        source.getActiveParticleLock().lock();
//                        for (Entity particle : source.getActiveParticles())
//                            renderParticle(gl, particle);
//                        source.getActiveParticleLock().unlock();
//                    }
//                }
//            }
//        }
//        disableAdditiveBlending(gl);
//
//        shaderProgram.stop();

        shaderProgram.start();
        shaderProgram.loadMVPMatrix(matrixCreator, scene.cameraX);
        for (ParticleEffect effect : particleSystem.getEffects()) {
            gl.glActiveTexture(GL10.GL_TEXTURE0);
            gl.glBindTexture(GL10.GL_TEXTURE_2D, effect.getTexture().getId());
            shaderProgram.loadTextureAtlasInfos(effect.getTexture());

            if (effect.getOptions().isAdditive() && !additiveBlending)
                enableAdditiveBlending(gl);
            if (!effect.getOptions().isAdditive() && additiveBlending)
                disableAdditiveBlending(gl);
            for (ParticleSource source : effect.getSources()) {
                source.getActiveParticleLock().lock();
                for (Entity particle : source.getActiveParticles())
                    renderParticle(gl, particle);
                source.getActiveParticleLock().unlock();
            }
        }
        shaderProgram.stop();
    }

    private void switchAdditiveBlending(GL10 gl, boolean effectAdditive) {
        if (effectAdditive && !additiveBlending)
            enableAdditiveBlending(gl);
        if (effectAdditive && additiveBlending)
            disableAdditiveBlending(gl);
    }

    private void renderEntity(GL10 gl, Entity entity) {
        gl.glActiveTexture(GL10.GL_TEXTURE0);
        gl.glBindTexture(GL10.GL_TEXTURE_2D, entity.getTexture().getId());
        shaderProgram.loadTransformationMatrix(matrixCreator, entity);
        shaderProgram.loadAlpha(entity.getAlpha());
        shaderProgram.loadTextureAtlasInfos(entity.getTexture());
        shaderProgram.loadColor(entity.getColor() != null ? entity.getColor() : emptyColor);
        quad.draw();
    }

    private void renderParticle(GL10 gl, Entity entity) {
        shaderProgram.loadTransformationMatrix(matrixCreator, entity);
        shaderProgram.loadAlpha(entity.getAlpha());
        shaderProgram.loadColor(entity.getColor() != null ? entity.getColor() : emptyColor);
        quad.draw();
    }

    private void enableAdditiveBlending(GL10 gl) {
        additiveBlending = true;
        gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE);
    }

    private void disableAdditiveBlending(GL10 gl) {
        additiveBlending = false;
        gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);
    }
}
