/*
 * Copyright (C) 2011 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


package com.android.testapp;

import java.util.ArrayList;

import com.android.scenegraph.*;

import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.renderscript.*;
import android.renderscript.Allocation.MipmapControl;
import android.renderscript.Element.Builder;
import android.renderscript.Font.Style;
import android.renderscript.Program.TextureType;
import android.renderscript.ProgramStore.DepthFunc;
import android.util.Log;

class FullscreenBlur {

    static TextureRenderTarget sRenderTargetBlur0Color;
    static TextureRenderTarget sRenderTargetBlur0Depth;
    static TextureRenderTarget sRenderTargetBlur1Color;
    static TextureRenderTarget sRenderTargetBlur1Depth;
    static TextureRenderTarget sRenderTargetBlur2Color;
    static TextureRenderTarget sRenderTargetBlur2Depth;

    static FragmentShader mPF_BlurH;
    static FragmentShader mPF_BlurV;
    static FragmentShader mPF_SelectColor;
    static FragmentShader mPF_Texture;
    static VertexShader mPV_Paint;
    static VertexShader mPV_Blur;

    static int targetWidth;
    static int targetHeight;

    // This is only used when full screen blur is enabled
    // Basically, it's the offscreen render targets
    static void createRenderTargets(RenderScriptGL rs, int w, int h) {
        targetWidth = w/8;
        targetHeight = h/8;
        Type.Builder b = new Type.Builder(rs, Element.RGBA_8888(rs));
        Type renderType = b.setX(targetWidth).setY(targetHeight).create();
        int usage = Allocation.USAGE_GRAPHICS_TEXTURE | Allocation.USAGE_GRAPHICS_RENDER_TARGET;
        sRenderTargetBlur0Color = new TextureRenderTarget(Allocation.createTyped(rs, renderType, usage));
        sRenderTargetBlur1Color = new TextureRenderTarget(Allocation.createTyped(rs, renderType, usage));
        sRenderTargetBlur2Color = new TextureRenderTarget(Allocation.createTyped(rs, renderType, usage));

        b = new Type.Builder(rs, Element.createPixel(rs, Element.DataType.UNSIGNED_16,
                                                     Element.DataKind.PIXEL_DEPTH));
        renderType = b.setX(targetWidth).setY(targetHeight).create();
        usage = Allocation.USAGE_GRAPHICS_RENDER_TARGET;
        sRenderTargetBlur0Depth = new TextureRenderTarget(Allocation.createTyped(rs, renderType, usage));
        sRenderTargetBlur1Depth = new TextureRenderTarget(Allocation.createTyped(rs, renderType, usage));
        sRenderTargetBlur2Depth = new TextureRenderTarget(Allocation.createTyped(rs, renderType, usage));
    }

    static void addOffsets(Renderable quad, float advance) {
        quad.appendSourceParams(new Float4Param("blurOffset0", - advance * 2.5f));
        quad.appendSourceParams(new Float4Param("blurOffset1", - advance * 0.5f));
        quad.appendSourceParams(new Float4Param("blurOffset2", advance * 1.5f));
        quad.appendSourceParams(new Float4Param("blurOffset3", advance * 3.5f));
    }

    static RenderPass addPass(Scene scene, Camera cam, TextureRenderTarget color, TextureRenderTarget depth) {
        RenderPass pass = new RenderPass();
        pass.setColorTarget(color);
        pass.setDepthTarget(depth);
        pass.setShouldClearColor(false);
        pass.setShouldClearDepth(false);
        pass.setCamera(cam);
        scene.appendRenderPass(pass);
        return pass;
    }

    static void addBlurPasses(Scene scene, RenderScriptGL rs, Camera cam) {
        SceneManager sceneManager = SceneManager.getInstance();
        ArrayList<RenderableBase> allDraw = scene.getRenderables();
        int numDraw = allDraw.size();

        ProgramRaster cullNone = ProgramRaster.CULL_NONE(rs);
        ProgramStore blendAdd = SceneManager.BLEND_ADD_DEPTH_NONE(rs);
        ProgramStore blendNone = ProgramStore.BLEND_NONE_DEPTH_NONE(rs);

        RenderState drawTex = new RenderState(mPV_Blur, mPF_Texture, blendAdd, cullNone);
        RenderState selectCol = new RenderState(mPV_Blur, mPF_SelectColor, blendNone, cullNone);
        RenderState hBlur = new RenderState(mPV_Blur, mPF_BlurH, blendNone, cullNone);
        RenderState vBlur = new RenderState(mPV_Blur, mPF_BlurV, blendNone, cullNone);

        // Renders the scene off screen
        RenderPass blurSourcePass = addPass(scene, cam,
                                            sRenderTargetBlur0Color,
                                            sRenderTargetBlur0Depth);
        blurSourcePass.setClearColor(new Float4(1.0f, 1.0f, 1.0f, 1.0f));
        blurSourcePass.setShouldClearColor(true);
        blurSourcePass.setClearDepth(1.0f);
        blurSourcePass.setShouldClearDepth(true);
        for (int i = 0; i < numDraw; i ++) {
            blurSourcePass.appendRenderable((Renderable)allDraw.get(i));
        }

        // Pass for selecting bright colors
        RenderPass selectColorPass = addPass(scene, cam,
                                             sRenderTargetBlur2Color,
                                             sRenderTargetBlur2Depth);
        Renderable quad = sceneManager.getRenderableQuad("ScreenAlignedQuadS", selectCol);
        quad.appendSourceParams(new TextureParam("color", sRenderTargetBlur0Color));
        selectColorPass.appendRenderable(quad);

        // Horizontal blur
        RenderPass horizontalBlurPass = addPass(scene, cam,
                                                sRenderTargetBlur1Color,
                                                sRenderTargetBlur1Depth);
        quad = sceneManager.getRenderableQuad("ScreenAlignedQuadH", hBlur);
        quad.appendSourceParams(new TextureParam("color", sRenderTargetBlur2Color));
        addOffsets(quad, 1.0f / (float)targetWidth);
        horizontalBlurPass.appendRenderable(quad);

        // Vertical Blur
        RenderPass verticalBlurPass = addPass(scene, cam,
                                              sRenderTargetBlur2Color,
                                              sRenderTargetBlur2Depth);
        quad = sceneManager.getRenderableQuad("ScreenAlignedQuadV", vBlur);
        quad.appendSourceParams(new TextureParam("color", sRenderTargetBlur1Color));
        addOffsets(quad, 1.0f / (float)targetHeight);
        verticalBlurPass.appendRenderable(quad);
    }

    // Additively renders the blurred colors on top of the scene
    static void addCompositePass(Scene scene, RenderScriptGL rs, Camera cam) {
        SceneManager sceneManager = SceneManager.getInstance();
        RenderState drawTex = new RenderState(mPV_Blur, mPF_Texture,
                                              SceneManager.BLEND_ADD_DEPTH_NONE(rs),
                                              ProgramRaster.CULL_NONE(rs));

        RenderPass compositePass = addPass(scene, cam, null, null);
        Renderable quad = sceneManager.getRenderableQuad("ScreenAlignedQuadComposite", drawTex);
        quad.appendSourceParams(new TextureParam("color", sRenderTargetBlur2Color));
        compositePass.appendRenderable(quad);
    }

    static private FragmentShader getShader(Resources res, RenderScriptGL rs,
                                            int resID, Type constants) {
        FragmentShader.Builder fb = new FragmentShader.Builder(rs);
        fb.setShader(res, resID);
        fb.addTexture(TextureType.TEXTURE_2D, "color");
        if (constants != null) {
            fb.setObjectConst(constants);
        }
        FragmentShader prog = fb.create();
        prog.getProgram().bindSampler(Sampler.CLAMP_LINEAR(rs), 0);
        return prog;
    }

    static void initShaders(Resources res, RenderScriptGL rs) {
        ScriptField_BlurOffsets blurConst = new ScriptField_BlurOffsets(rs, 1);
        VertexShader.Builder vb = new VertexShader.Builder(rs);
        vb.addInput(ScriptField_VertexShaderInputs.createElement(rs));
        vb.setShader(res, R.raw.blur_vertex);
        mPV_Blur = vb.create();

        mPF_Texture = getShader(res, rs, R.raw.texture, null);
        mPF_Texture.getProgram().bindSampler(Sampler.WRAP_LINEAR_MIP_LINEAR(rs), 0);
        mPF_BlurH = getShader(res, rs, R.raw.blur_h, blurConst.getAllocation().getType());
        mPF_BlurV = getShader(res, rs, R.raw.blur_v, blurConst.getAllocation().getType());
        mPF_SelectColor = getShader(res, rs, R.raw.select_color, null);
    }

}





