package com.example.myapplication;

import org.opencv.android.CameraGLSurfaceView;
import org.opencv.android.Utils;
import org.opencv.calib3d.Calib3d;
import org.opencv.core.Mat;
import org.opencv.core.MatOfPoint2f;
import org.opencv.core.Point;
import org.opencv.core.Size;
import org.opencv.imgproc.Imgproc;

import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.drawable.BitmapDrawable;
import android.os.Handler;
import android.os.Looper;
import android.util.AttributeSet;
import android.util.Log;
import android.view.SurfaceHolder;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import java.util.List;

public class MyGLSurfaceView extends CameraGLSurfaceView implements CameraGLSurfaceView.CameraTextureListener {

    static final String LOGTAG = "MyGLSurfaceView";
    protected int  frameCounter;
    protected long lastNanoTime;
    protected boolean frontFacing = true;
    protected int mSurfaceWidth;
    protected int mSurfaceHeight;
    protected Mat mHomography, mThermal;
    TextView mFpsText = null;

    public Bitmap mLastFrame;
    public Paint overlayPaint = new Paint();

    public MyGLSurfaceView(Context context, AttributeSet attrs)
    {
        super(context, attrs);
        overlayPaint.setAlpha(150);
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder)
    {
        super.surfaceCreated(holder);
        mHomography = new Mat();
        mThermal = new Mat();
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h)
    {
        super.surfaceChanged(holder, format, w, h);
        mSurfaceWidth = w;
        mSurfaceHeight = h;
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        super.surfaceDestroyed(holder);
    }

    @Override
    public void onCameraViewStarted(int width, int height)
    {
        ((Activity) getContext()).runOnUiThread(new Runnable() {
            public void run() {
                Toast.makeText(getContext(), "onCameraViewStarted", Toast.LENGTH_SHORT).show();
            }
        });
        frameCounter = 0;
        lastNanoTime = System.nanoTime();
    }

    @Override
    public void onCameraViewStopped() {
        ((Activity) getContext()).runOnUiThread(new Runnable() {
            public void run() {
                Toast.makeText(getContext(), "onCameraViewStopped", Toast.LENGTH_SHORT).show();
            }
        });
    }

    public void setFrontFacing(boolean frontFacing) {
        this.frontFacing = frontFacing;
    }

    @Override
    public boolean onCameraTexture(int texIn, int texOut, int width, int height)
    {
        // FPS
        frameCounter++;
        if(frameCounter >= 30)
        {
            final int fps = (int) (frameCounter * 1e9 / (System.nanoTime() - lastNanoTime));
            //Log.i(LOGTAG, "drawFrame() FPS: "+fps);
            if(mFpsText != null)
            {
                Runnable fpsUpdater = new Runnable() {
                    public void run()
                    {
                        mFpsText.setText(""+fps);
                    }
                };
                new Handler(Looper.getMainLooper()).post(fpsUpdater);
            }
            else
            {
                mFpsText = (TextView)((Activity) getContext()).findViewById(R.id.fps_text_view);
            }
            frameCounter = 0;
            lastNanoTime = System.nanoTime();
        }

        byte[] gl_mat = processFrame(texIn, texOut, width, height, frontFacing);

        mLastFrame = Bitmap.createBitmap(convertByteArr2IntArr(gl_mat, width, height), width, height, Bitmap.Config.ARGB_8888);
        final Bitmap bMapScaled = Bitmap.createScaledBitmap(mLastFrame, mSurfaceWidth, mSurfaceHeight, true);

        Canvas canvas = new Canvas(bMapScaled);
        BitmapDrawable drawable = (BitmapDrawable)((ImageView)((Activity) getContext()).findViewById(R.id.imageView_Thermal)).getDrawable();
        if(drawable != null)
        {
            Bitmap thermalBitmap = drawable.getBitmap().copy(Bitmap.Config.ARGB_8888, true);
            if (thermalBitmap != null && !mHomography.empty())
            {
                Utils.bitmapToMat(thermalBitmap, mThermal);
                Mat mTransformed = new Mat();
                Imgproc.warpPerspective(mThermal, mTransformed, mHomography, new Size(mThermal.width(), mThermal.height()));
                Utils.matToBitmap(mTransformed, thermalBitmap);
                canvas.drawBitmap(thermalBitmap, 0f, 0f, overlayPaint);

                ((Activity) getContext()).runOnUiThread(new Runnable() {
                    public void run() {
                        ((ImageView) ((Activity) getContext()).findViewById(R.id.overlappedView)).setImageBitmap(bMapScaled);
                    }
                });
            }
        }
        return true;
    }

    private static native byte[] processFrame(int tex1, int tex2, int w, int h, boolean frontFacing);

    private static int[] convertByteArr2IntArr(final byte[] input, final int width, final int height) {
        int[] result = new int[height * width];

        if(input.length > 0) {
            int A, R, G, B;
            for (int i = 0; i < height * width; i++) {
                R = input[4*i];
                G = input[4*i + 1];
                B = input[4*i + 2];
                A = input[4*i + 3];
                result[i] = (A & 0xFF) << 24 | (R & 0xFF) << 16 | (G & 0xFF) << 8 | (B & 0xFF);     // No transparency
            }
        }

        return result;
    }

    public void setPerspectiveTransform(List<Point> ptsOnRGB, List<Point> ptsOnThermal)  {
        MatOfPoint2f ptsRGBMat = new MatOfPoint2f();
        ptsRGBMat.fromList(ptsOnRGB);

        MatOfPoint2f ptsThermalMat = new MatOfPoint2f();
        ptsThermalMat.fromList(ptsOnThermal);

        if(ptsOnRGB.size() > 2) mHomography = Calib3d.findHomography(ptsThermalMat, ptsRGBMat);
    }
}
