package com.android.testingcamera;

import android.content.res.Resources;
import android.graphics.ImageFormat;
import android.graphics.PixelFormat;
import android.os.AsyncTask;
import android.os.SystemClock;
import android.renderscript.Allocation;
import android.renderscript.Element;
import android.renderscript.Matrix4f;
import android.renderscript.RenderScript;
import android.renderscript.Script;
import android.renderscript.ScriptGroup;
import android.renderscript.ScriptIntrinsicColorMatrix;
import android.renderscript.Type;
import android.util.Log;
import android.view.Surface;
import android.view.SurfaceView;

/**
 *  Process preview callback data for display.
 *  This is done by constructing a two-step Renderscript group,
 *  the first of which converts from various YUV formats to 8bpp YUV, and
 *  the second of which converts from YUV to RGB.
 *
 *  The processing is done in a background thread, and the result is produced
 *  into an Allocation that's backed by a SurfaceView
 */
class CallbackProcessor {
    private SurfaceView mCallbackView;
    private Surface mCallbackSurface;

    private Object mTaskLock = new Object();

    private RenderScript mRS;
    private Allocation mAllocationIn;
    private Allocation mAllocationOut;
    private ScriptGroup mConverter;

    private int mWidth;
    private int mHeight;
    private int mFormat;

    private boolean mDone = false;
    private boolean mTaskInProgress = false;

    static final private int kStopTimeout = 2000; // ms

    private static final String TAG = "CallbackProcessor";

    public CallbackProcessor(int width, int height, int format,
            Resources res, SurfaceView callbackView,
            int viewWidth, int viewHeight,
            RenderScript rs) {
        mWidth = width;
        mHeight = height;
        mFormat = format;
        mRS = rs;
        mCallbackView = callbackView;

        int inputSize = TestingCamera.getCallbackBufferSize(mWidth, mHeight,
                mFormat);
        mAllocationIn = Allocation.createSized(mRS, Element.U8(mRS), inputSize);

        Type.Builder tb = new Type.Builder(mRS, Element.RGBA_8888(mRS));
        tb.setX(viewWidth);
        tb.setY(viewHeight);
        Type outType = tb.create();

        mAllocationOut = Allocation.createTyped(mRS, outType,
                Allocation.USAGE_IO_OUTPUT | Allocation.USAGE_SCRIPT);

        ScriptC_callback swizzleScript = new ScriptC_callback(mRS);
        swizzleScript.bind_yuv_in(mAllocationIn);
        swizzleScript.invoke_init_convert(mWidth, mHeight,
            mFormat, viewWidth, viewHeight);
        Script.KernelID swizzleId;

        switch (mFormat) {
        case ImageFormat.NV21:
            swizzleId = swizzleScript.getKernelID_convert_semiplanar();
            break;
        case ImageFormat.YV12:
            swizzleId = swizzleScript.getKernelID_convert_planar();
            break;
        case ImageFormat.YUY2:
            swizzleId = swizzleScript.getKernelID_convert_interleaved();
            break;
        case ImageFormat.UNKNOWN:
        default:
            swizzleId = swizzleScript.getKernelID_convert_unknown();
        }

        ScriptGroup.Builder b = new ScriptGroup.Builder(rs);
        b.addKernel(swizzleId);
        mConverter = b.create();

        mConverter.setOutput(swizzleId, mAllocationOut);
    }

    public boolean stop() {
        synchronized(mTaskLock) {
            mDone = true;
            long startTime = SystemClock.elapsedRealtime();
            while (mTaskInProgress) {
                try {
                    mTaskLock.wait(kStopTimeout);
                } catch (InterruptedException e) {
                    // ignored, keep waiting
                }
                long endTime = SystemClock.elapsedRealtime();
                if (endTime - startTime > kStopTimeout) {
                    return false;
                }
            }
        }
        mAllocationOut.setSurface(null);
        return true;
    }

    public void displayCallback(byte[] data) {
        synchronized(mTaskLock) {
            if (mTaskInProgress || mDone) return;
            mTaskInProgress = true;
        }
        if (mCallbackSurface == null) {
            mCallbackView.getHolder().setFormat(PixelFormat.RGBA_8888);
            mCallbackSurface = mCallbackView.getHolder().getSurface();
            if (mCallbackSurface == null) return;
            mAllocationOut.setSurface(mCallbackSurface);
        }
        new ProcessCallbackTask().execute(data);
    }

    private class ProcessCallbackTask extends AsyncTask<byte[], Void, Boolean> {

        @Override
        protected Boolean doInBackground(byte[]... datas) {
            byte[] data = datas[0];

            mAllocationIn.copyFrom(data);
            mConverter.execute();
            mAllocationOut.ioSend();

            synchronized(mTaskLock) {
                mTaskInProgress = false;
                mTaskLock.notify();
            }
            return true;
        }

        @Override
        protected void onPostExecute(Boolean result) {
        }
    }

}