/**
 * 
 * PixelFlow | Copyright (C) 2017 Thomas Diewald (www.thomasdiewald.com)
 * 
 * src  - www.github.com/diwi/PixelFlow
 * 
 * A Processing/Java library for high performance GPU-Computing.
 * MIT License: https://opensource.org/licenses/MIT
 * 
 */

package com.thomasdiewald.pixelflow.java.antialiasing.SMAA;

import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;

import com.jogamp.opengl.GL2;
import com.jogamp.opengl.GL2ES2;
import com.thomasdiewald.pixelflow.java.DwPixelFlow;
import com.thomasdiewald.pixelflow.java.dwgl.DwGLSLProgram;
import com.thomasdiewald.pixelflow.java.dwgl.DwGLTexture;
import com.thomasdiewald.pixelflow.java.utils.DwUtils;

import processing.opengl.PGraphicsOpenGL;
import processing.opengl.Texture;

/**
 * 
 * SMAA - Enhances Subpixel Morphological AntiAliasing
 * 
 * PostProcessing, 3 passes, nice quality
 * 
 * SMAA.h, Version 2.8: 
 * http://www.iryoku.com/smaa/
 * 
 * copyright-note, see SMAA.h:
 *   Uses SMAA. Copyright (C) 2011 by Jorge Jimenez, Jose I. Echevarria,
 *   Tiago Sousa, Belen Masia, Fernando Navarro and Diego Gutierrez.
 * 
 * 
 * 
 * @author Thomas Diewald
 *
 */
public class SMAA {

  static public class Param {
    // no params
  }
  
  public DwPixelFlow context;
  
  public Param param = new Param();
  
  public DwGLSLProgram shader_edges;
  public DwGLSLProgram shader_blend;
  public DwGLSLProgram shader_smaa;
  
  public DwGLTexture tex_edges  = new DwGLTexture();
  public DwGLTexture tex_blend  = new DwGLTexture();
  
  private DwGLTexture tex_area   = new DwGLTexture();
  private DwGLTexture tex_search = new DwGLTexture();
  
  public SMAA(DwPixelFlow context){
    this.context = context;
    
    String dir = DwPixelFlow.SHADER_DIR+"antialiasing/SMAA/";
    this.shader_edges = context.createShader(dir+"smaa1_edges.vert", dir+"smaa1_edges.frag");
    this.shader_blend = context.createShader(dir+"smaa2_blend.vert", dir+"smaa2_blend.frag");
    this.shader_smaa  = context.createShader(dir+"smaa3_final.vert", dir+"smaa3_final.frag");
    
    ByteBuffer bbuffer_tex_search = readTexData(dir+"TexSearch_66x33_R8.bin" , new byte[ 66 *  33 * 1]);
    ByteBuffer bbuffer_tex_area   = readTexData(dir+"TexArea_160x560_RG8.bin", new byte[160 * 560 * 2]);
   
    tex_search.resize(context, GL2ES2.GL_R8 ,  66,  33, GL2ES2.GL_RED, GL2ES2.GL_UNSIGNED_BYTE, GL2ES2.GL_NEAREST, 1, 1, bbuffer_tex_search);
    tex_area  .resize(context, GL2ES2.GL_RG8, 160, 560, GL2ES2.GL_RG , GL2ES2.GL_UNSIGNED_BYTE, GL2ES2.GL_NEAREST, 2, 1, bbuffer_tex_area);
  }
  
  public void resize(int w, int h){
    tex_edges.resize(context, GL2ES2.GL_RGBA8, w, h, GL2ES2.GL_RGBA, GL2ES2.GL_UNSIGNED_BYTE, GL2ES2.GL_LINEAR, 4, 1);
    tex_blend.resize(context, GL2ES2.GL_RGBA8, w, h, GL2ES2.GL_RGBA, GL2ES2.GL_UNSIGNED_BYTE, GL2ES2.GL_LINEAR, 4, 1);
  }
  
  
  private ByteBuffer readTexData(String path, byte[] data){
    InputStream is = new DwUtils(context).createInputStream(path);
    try {
      is.read(data);
      is.close();
    } catch (IOException e) {
      e.printStackTrace();
    }
    return ByteBuffer.wrap(data);
  }
  
  

  
  public void apply(PGraphicsOpenGL src, PGraphicsOpenGL dst) {
    if(src == dst){
      System.out.println("MSAA error: read-write race");
      return;
    }
    
    Texture tex_src = src.getTexture(); if(!tex_src.available())  return;
    Texture tex_dst = dst.getTexture(); if(!tex_dst.available())  return;

    int w = src.width;
    int h = src.height;
    
    resize(w, h);
     
    context.begin();
    
    setLinearTextureFiltering(tex_src.glName);

    // PASS 1 - Edges
    context.beginDraw(tex_edges);
    context.gl.glClearColor(0,0,0,0);
    context.gl.glClear(GL2ES2.GL_COLOR_BUFFER_BIT);
    shader_edges.begin();
    shader_edges.uniform2f     ("wh_rcp"   , 1f/w, 1f/h);
    shader_edges.uniformTexture("tex_color", tex_src.glName);
    shader_edges.drawFullScreenQuad();
    shader_edges.end();
    context.endDraw("smaa - pass1");
    
    // PASS 2 - blend
    context.beginDraw(tex_blend);
    context.gl.glClearColor(0,0,0,0);
    context.gl.glClear(GL2ES2.GL_COLOR_BUFFER_BIT);
    shader_blend.begin();
    shader_blend.uniform2f     ("wh_rcp"   , 1f/w, 1f/h);
    shader_blend.uniformTexture("tex_edges", tex_edges .HANDLE[0]);
    shader_blend.uniformTexture("tex_area" , tex_area  .HANDLE[0]);
    shader_blend.uniformTexture("tex_search",tex_search.HANDLE[0]);
    shader_blend.drawFullScreenQuad();
    shader_blend.end();
    context.endDraw("smaa - pass2");
    
    // PASS 3 - blend
    context.beginDraw(dst);
    context.gl.glClearColor(0,0,0,0);
    context.gl.glClear(GL2ES2.GL_COLOR_BUFFER_BIT);
    shader_smaa.begin();
    shader_smaa.uniform2f     ("wh_rcp"   , 1f/w, 1f/h);
    shader_smaa.uniformTexture("tex_color", tex_src.glName);
    shader_smaa.uniformTexture("tex_blend", tex_blend.HANDLE[0]);
    shader_smaa.drawFullScreenQuad();
    shader_smaa.end();
    context.endDraw("smaa - pass3");

    
    resetTextureFiltering(tex_src.glName);
    
    context.end("SMAA.apply");
  }
  

 
  
  
  
  
  
  
  
  protected int[] filter_minmag = new int[2];
  protected int tex_target = GL2.GL_TEXTURE_2D;
  protected void setLinearTextureFiltering(int handle_tex){
    context.begin();
    context.gl.glBindTexture      (tex_target, handle_tex);
    context.gl.glGetTexParameteriv(tex_target, GL2ES2.GL_TEXTURE_MIN_FILTER, filter_minmag, 0);
    context.gl.glGetTexParameteriv(tex_target, GL2ES2.GL_TEXTURE_MAG_FILTER, filter_minmag, 1);
    context.gl.glTexParameteri    (tex_target, GL2ES2.GL_TEXTURE_MIN_FILTER, GL2.GL_LINEAR);
    context.gl.glTexParameteri    (tex_target, GL2ES2.GL_TEXTURE_MAG_FILTER, GL2.GL_LINEAR);
    context.gl.glBindTexture      (tex_target, 0);
    context.end();
  }
  
  protected void resetTextureFiltering(int handle_tex){
    context.begin();
    context.gl.glBindTexture      (tex_target, handle_tex);
    context.gl.glTexParameteri    (tex_target, GL2ES2.GL_TEXTURE_MIN_FILTER, filter_minmag[0]);
    context.gl.glTexParameteri    (tex_target, GL2ES2.GL_TEXTURE_MAG_FILTER, filter_minmag[1]);
    context.gl.glBindTexture      (tex_target, 0);
    context.end();
  }
  
  
}