/*
 * Copyright (C) 2009 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.wallpaper.nexus;

import static android.renderscript.Element.RGBA_8888;
import static android.renderscript.Element.RGB_565;
import static android.renderscript.ProgramStore.DepthFunc.ALWAYS;
import static android.renderscript.Sampler.Value.LINEAR;
import static android.renderscript.Sampler.Value.CLAMP;
import static android.renderscript.Sampler.Value.WRAP;

import com.android.wallpaper.R;
import com.android.wallpaper.RenderScriptScene;

import android.app.WallpaperManager;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Rect;
import android.os.Bundle;
import android.renderscript.*;
import android.renderscript.ProgramStore.BlendDstFunc;
import android.renderscript.ProgramStore.BlendSrcFunc;
import android.view.SurfaceHolder;

import java.util.TimeZone;

class NexusRS extends RenderScriptScene {
    private final BitmapFactory.Options mOptionsARGB = new BitmapFactory.Options();

    private ProgramVertexFixedFunction.Constants mPvOrthoAlloc;

    private float mXOffset;
    private ScriptC_nexus mScript;

    public NexusRS(int width, int height) {
        super(width, height);

        mOptionsARGB.inScaled = false;
        mOptionsARGB.inPreferredConfig = Bitmap.Config.ARGB_8888;
    }

    @Override
    public void setOffset(float xOffset, float yOffset, int xPixels, int yPixels) {
        mXOffset = xOffset;
        mScript.set_gXOffset(xOffset);
    }

    @Override
    public void start() {
        super.start();
    }

    @Override
    public void resize(int width, int height) {
        super.resize(width, height); // updates mWidth, mHeight

        // android.util.Log.d("NexusRS", String.format("resize(%d, %d)", width, height));
    }

    @Override
    protected ScriptC createScript() {
        mScript = new ScriptC_nexus(mRS, mResources, R.raw.nexus);

        createProgramFragmentStore();
        createProgramFragment();
        createProgramVertex();
        createState();

        mScript.set_gTBackground(loadTexture(R.drawable.pyramid_background));
        mScript.set_gTPulse(loadTextureARGB(R.drawable.pulse));
        mScript.set_gTGlow(loadTextureARGB(R.drawable.glow));
        mScript.setTimeZone(TimeZone.getDefault().getID());
        mScript.invoke_initPulses();
        return mScript;
    }

    private void createState() {
        int mode;
        try {
            mode = mResources.getInteger(R.integer.nexus_mode);
        } catch (Resources.NotFoundException exc) {
            mode = 0; // standard nexus mode
        }

        mScript.set_gIsPreview(isPreview() ? 1 : 0);
        mScript.set_gMode(mode);
        mScript.set_gXOffset(0.f);
    }

    private Allocation loadTexture(int id) {
        return Allocation.createFromBitmapResource(mRS, mResources, id);
    }

    private Allocation loadTextureARGB(int id) {
        Bitmap b = BitmapFactory.decodeResource(mResources, id, mOptionsARGB);
        return Allocation.createFromBitmap(mRS, b);
    }


    private void createProgramFragment() {
        // sampler and program fragment for pulses
        ProgramFragmentFixedFunction.Builder builder = new ProgramFragmentFixedFunction.Builder(mRS);
        builder.setTexture(ProgramFragmentFixedFunction.Builder.EnvMode.MODULATE,
                           ProgramFragmentFixedFunction.Builder.Format.RGBA, 0);
        ProgramFragment pft = builder.create();
        pft.bindSampler(Sampler.WRAP_LINEAR(mRS), 0);
        mScript.set_gPFTexture(pft);

        // sampler and program fragment for background image
        builder = new ProgramFragmentFixedFunction.Builder(mRS);
        builder.setTexture(ProgramFragmentFixedFunction.Builder.EnvMode.MODULATE,
                           ProgramFragmentFixedFunction.Builder.Format.RGB, 0);
        ProgramFragment pft565 = builder.create();
        pft565.bindSampler(Sampler.CLAMP_NEAREST(mRS), 0);
        mScript.set_gPFTexture565(pft565);
    }

    private void createProgramFragmentStore() {
        ProgramStore.Builder builder = new ProgramStore.Builder(mRS);
        builder.setDepthFunc(ALWAYS);
        builder.setBlendFunc(BlendSrcFunc.ONE, BlendDstFunc.ONE);
        builder.setDitherEnabled(false);
        ProgramStore solid = builder.create();
        mRS.bindProgramStore(solid);

        builder.setBlendFunc(BlendSrcFunc.SRC_ALPHA, BlendDstFunc.ONE);
        mScript.set_gPSBlend(builder.create());
    }

    private void createProgramVertex() {
        mPvOrthoAlloc = new ProgramVertexFixedFunction.Constants(mRS);
        Matrix4f proj = new Matrix4f();
        proj.loadOrthoWindow(mWidth, mHeight);
        mPvOrthoAlloc.setProjection(proj);

        ProgramVertexFixedFunction.Builder pvb = new ProgramVertexFixedFunction.Builder(mRS);
        pvb.setTextureMatrixEnable(true);
        ProgramVertex pv = pvb.create();
        ((ProgramVertexFixedFunction)pv).bindConstants(mPvOrthoAlloc);
        mRS.bindProgramVertex(pv);
    }

    @Override
    public Bundle onCommand(String action, int x, int y, int z, Bundle extras,
            boolean resultRequested) {

        if (mWidth < mHeight) {
            // nexus.rs ignores the xOffset when rotated; we shall endeavor to do so as well
            x = (int) (x + mXOffset * mWidth);
        }

        // android.util.Log.d("NexusRS", String.format(
        //     "dw=%d, bw=%d, xOffset=%g, x=%d",
        //     dw, bw, mWorldState.xOffset, x));

        if (WallpaperManager.COMMAND_TAP.equals(action)
                || WallpaperManager.COMMAND_SECONDARY_TAP.equals(action)
                || WallpaperManager.COMMAND_DROP.equals(action)) {
            mScript.invoke_addTap(x, y);
        }
        return null;
    }
}
