package com.xypower.mpapp.v2;

import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Insets;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.net.Uri;
import android.opengl.GLException;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.provider.MediaStore;
import android.text.TextUtils;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.View;
import android.view.Window;
import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.WindowMetrics;
import android.widget.FrameLayout;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;

import com.xypower.gpuv.camerarecorder.CameraRecordListener;
import com.xypower.gpuv.camerarecorder.GPUCameraRecorder;
import com.xypower.gpuv.camerarecorder.GPUCameraRecorderBuilder;
import com.xypower.gpuv.egl.filter.GlWatermarkFilter;
import com.xypower.mpapp.v2.AutoFitGLView;
import com.xypower.mpapp.MicroPhotoService;
import com.xypower.mpapp.R;

import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLContext;
import javax.microedition.khronos.opengles.GL10;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.IntBuffer;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class Camera2VideoActivity extends AppCompatActivity {

    public static final String ACTION_FINISH = "com.xypower.mvapp.ACT_FINISH";
    public static final String ACTION_MP_VIDEO_FINISHED = "com.xypower.mpapp.ACT_V_FINISHED";

    public static final int RESULT_TYPE_NONE = 0;
    public static final int RESULT_TYPE_ACTIVITY_RESULT = 1;
    public static final int RESULT_TYPE_BROADCAST = 2;

    public final static int REQUEST_CODE_RECORDING = 2;

    private static final int DEFAULT_FONT_SIZE = 32;
    private static final float DEFAULT_STROKE_WIDTH = 0.8f;
    private AutoFitGLView mPreviewView;
    protected GPUCameraRecorder mGPUCameraRecorder;

    // protected LensFacing lensFacing = LensFacing.BACK;
    protected int mCameraWidth = 1280;
    protected int mCameraHeight = 720;
    protected int mVideoWidth = 1280;
    protected int mVideoHeight = 720;

    protected int mPreviewWidth = 1280;
    protected int mPreviewHeight = 720;

    private int mCameraId;
    private long mVideoId = 0;
    private long mDuration = 0;

    private int mOrientation = -1;

    private String mNextVideoAbsolutePath;
    private String mOutputPath;

    private String mOSDLeftTop = null;
    private String mOSDRightTop = null;
    private String mOSDRightBottom = null;
    private String mOSDLeftBottom = null;

    private int mOSDMargin = 0;
    private Paint mPaint;
    private Paint mPaintStroker;

    private GlWatermarkFilter mOSDFilter = null;

    private SimpleDateFormat mDateFormater;
    // SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss a");

    private final String TIME_MICRO_TS = "$$TS$$";
    private final String TIME_MICRO_DT = "$$DATETIME$$";

    private int mTimeMask = 0;
    private int mStatusBarHeight = -1;

    private int mResultType = RESULT_TYPE_NONE;

    private static class OSD_ITEM
    {
        String text;
        int mask;
        Point origin;
        Rect previousRect;
    }

    private List<OSD_ITEM> mOSDItems = new ArrayList<>();

    private final static int TIME_MASK_LT_TS = 1;
    private final static int TIME_MASK_LT_DT = 2;
    private final static int TIME_MASK_LT_ML = 4;
    private final static int TIME_MASK_LT = TIME_MASK_LT_TS | TIME_MASK_LT_DT | TIME_MASK_LT_ML;
    private final static int TIME_MASK_RT_TS = 8;
    private final static int TIME_MASK_RT_DT = 16;
    private final static int TIME_MASK_RT_ML = 32;
    private final static int TIME_MASK_RT = TIME_MASK_RT_TS | TIME_MASK_RT_DT | TIME_MASK_RT_ML;
    private final static int TIME_MASK_RB_TS = 64;
    private final static int TIME_MASK_RB_DT = 128;
    private final static int TIME_MASK_RB_ML = 256;
    private final static int TIME_MASK_RB = TIME_MASK_RB_TS | TIME_MASK_RB_DT | TIME_MASK_RB_ML;
    private final static int TIME_MASK_LB_TS = 512;
    private final static int TIME_MASK_LB_DT = 1024;
    private final static int TIME_MASK_LB_ML = 2048;
    private final static int TIME_MASK_LB = TIME_MASK_LB_TS | TIME_MASK_LB_DT | TIME_MASK_LB_ML;

    private Handler mHandler = null;

    private Thread mOsdThread = new Thread(new Runnable() {
        @Override
        public void run() {

            try {
                long ts = System.currentTimeMillis();
                Bitmap bm = Bitmap.createBitmap(mPreviewWidth, mPreviewHeight, Bitmap.Config.ARGB_8888);
                Bitmap oldBm = null;
                Canvas canvas = new Canvas(bm);

                long zeroTs = ts - (ts % 1000);
                long nextTs = zeroTs + 1000;

                while (true) {
                    try {
                        if (nextTs > ts) {
                            Log.i("OSD", "Sleep " + Long.toString(nextTs - ts));
                            Thread.sleep(nextTs - ts);
                        }
                    } catch (InterruptedException ex){
                        break;
                    } catch(Exception ex){
                    }

                    canvas.setBitmap(bm);
                    canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
                    updateOSD(bm, nextTs);

                    oldBm = mOSDFilter.updateBitmap(bm);
                    bm = oldBm;

                    ts = System.currentTimeMillis();
                    zeroTs = ts - (ts % 1000);
                    if (zeroTs + 1000 > nextTs) {
                        nextTs = zeroTs + 1000;
                    }
                }
            } catch (Exception ex) {
            }
        }
    });

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);

        Window win = getWindow();
        int flags = /*WindowManager.LayoutParams.FLAG_FULLSCREEN
                | */WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
                | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
                | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
                | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD;
        int mask = /*WindowManager.LayoutParams.FLAG_FULLSCREEN |
                | */WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
                | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
                | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
                | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD;
        win.setFlags(flags, mask);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
            setShowWhenLocked(true);
        }

        setContentView(R.layout.activity_camera2_video);

        getSupportActionBar().hide();

        // mStatusBarHeight = getStatusBarHeight(this);
        onCreateActivity();

        getWindow().getDecorView().setOnApplyWindowInsetsListener((v, insets) -> {
            mStatusBarHeight = px2dip(Camera2VideoActivity.this, insets.getStableInsetTop());

            return insets;
        });
    }

    public static int px2dip(Context context, float pxValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (pxValue / scale + 0.5f);
    }

    public int getStatusBarHeight(Context context) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {

            WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
            WindowMetrics windowMetrics = wm.getCurrentWindowMetrics();
            WindowInsets windowInsets = windowMetrics.getWindowInsets();
            Insets insets = windowInsets.getInsetsIgnoringVisibility(WindowInsets.Type.statusBars() | WindowInsets.Type.displayCutout());
            return px2dip(this, insets.top);
        }

        Rect frame = new Rect();
        getWindow().getDecorView().getWindowVisibleDisplayFrame(frame);
        int statusBarHeight = frame.top;

        if (statusBarHeight == 0) {
            int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
            if (resourceId > 0) {
                statusBarHeight = getResources().getDimensionPixelSize(resourceId);
            }
        }

        return px2dip(context, statusBarHeight);
    }

    protected void onCreateActivity() {

        Intent intent = getIntent();

        mCameraId = intent.getIntExtra("cameraId", 0);
        mVideoId = intent.getLongExtra("videoId", 0);
        mDuration = intent.getIntExtra("duration", 0);
        mVideoWidth = intent.getIntExtra("width", 0);
        mVideoHeight = intent.getIntExtra("height", 0);
        mOrientation = intent.getIntExtra("orientation", -1);
        mOSDLeftTop = intent.getStringExtra("leftTopOsd");
        mOSDLeftBottom = intent.getStringExtra("leftBottomOsd");
        mOSDRightBottom = intent.getStringExtra("rightBottomOsd");
        mOSDRightTop = intent.getStringExtra("rightTopOsd");
        mOSDMargin = intent.getIntExtra("margin", 12);

        mResultType = intent.getIntExtra("resultType", RESULT_TYPE_BROADCAST);
        String path = intent.getStringExtra("path");
        if (!TextUtils.isEmpty(path)) {
            mOutputPath = path;
        }

        mCameraWidth = mVideoWidth;
        mCameraHeight = mVideoHeight;

        AspectRatioFrameLayout frameLayout = (AspectRatioFrameLayout)findViewById(R.id.wrap_view);

        frameLayout.setOnSizeChangedListener(new AspectRatioFrameLayout.OnSizeChangeListener() {
            @Override
            public void onSizeChanged(int width, int height) {
                Log.i("OSD", "width=" + Integer.toString(width));
            }
        });

        frameLayout.setAspectRatio((float)mVideoWidth / (float)mVideoHeight);

        mTimeMask = 0;
        if (!TextUtils.isEmpty(mOSDLeftTop)) {
            if (mOSDLeftTop.indexOf(TIME_MICRO_TS) != 0) {
                mTimeMask |= TIME_MASK_LT_TS;
            }
            if (mOSDLeftTop.indexOf(TIME_MICRO_DT) != 0) {
                mTimeMask |= TIME_MASK_LT_DT;
            }
            if (mOSDLeftTop.indexOf("\n") != 0) {
                mTimeMask |= TIME_MASK_LT_ML;
            }
        }
        if (!TextUtils.isEmpty(mOSDRightTop)) {
            if (mOSDRightTop.indexOf(TIME_MICRO_TS) != 0) {
                mTimeMask |= TIME_MASK_RT_TS;
            }
            if (mOSDRightTop.indexOf(TIME_MICRO_DT) != 0) {
                mTimeMask |= TIME_MASK_RT_DT;
            }
            if (mOSDRightTop.indexOf("\n") != 0) {
                mTimeMask |= TIME_MASK_RT_ML;
            }
        }
        if (!TextUtils.isEmpty(mOSDRightBottom)) {
            if (mOSDRightBottom.indexOf(TIME_MICRO_TS) != 0) {
                mTimeMask |= TIME_MASK_RB_TS;
            }
            if (mOSDRightBottom.indexOf(TIME_MICRO_DT) != 0) {
                mTimeMask |= TIME_MASK_RB_DT;
            }
            if (mOSDRightBottom.indexOf("\n") != 0) {
                mTimeMask |= TIME_MASK_RB_ML;
            }
        }

        if (!TextUtils.isEmpty(mOSDLeftBottom)) {
            if (mOSDLeftBottom.indexOf(TIME_MICRO_TS) != 0) {
                mTimeMask |= TIME_MASK_LB_TS;
            }
            if (mOSDLeftBottom.indexOf(TIME_MICRO_DT) != 0) {
                mTimeMask |= TIME_MASK_LB_DT;
            }
            if (mOSDLeftBottom.indexOf("\n") != 0) {
                mTimeMask |= TIME_MASK_LB_ML;
            }
        }

        mHandler = new Handler();

        mHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                setUpCamera();

                mHandler.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        if (TextUtils.isEmpty(mNextVideoAbsolutePath)) {
                            mNextVideoAbsolutePath = getVideoFilePath();
                        }
                        if (mGPUCameraRecorder != null) {
                            mGPUCameraRecorder.start(mNextVideoAbsolutePath);
                        } else {
                            int aa = 0;
                        }
                    }
                }, 0);
            }
        }, 100);

        // getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
    }

    @Override
    protected void onResume() {
        super.onResume();
        Log.i("OSD", "onResume");

    }

    @Override
    protected void onStop() {
        super.onStop();

        Log.i("OSD", "onStop");

    }

    @Override
    protected void onDestroy() {
        Log.i("OSD", "onDestroy");
        releaseCamera();
        super.onDestroy();
    }

    private void initOSD(Bitmap bm, long ts) {

        Log.i("OSD", "INIT OSD " + Long.toString(ts / 1000));

        if (mStatusBarHeight == -1) {
            mStatusBarHeight = getStatusBarHeight(this);
        }
        int statusHeight = mStatusBarHeight;

        int bmWidth = bm.getWidth();
        int bmHeight = bm.getHeight();

        int margin = mOSDMargin;
        int x = 0;
        int y = 0;

        Canvas canvas = new Canvas(bm);
        Rect textBounds = new Rect();

        canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);

        if (!TextUtils.isEmpty(mOSDLeftTop)) {
            String[] items = mOSDLeftTop.split("\n");
            float charWidth = mPaint.measureText("00");
            Point origin = new Point(margin + (int)charWidth, margin + statusHeight);

            for (String item : items) {

                int mask = 0;
                if (item.indexOf(TIME_MICRO_TS) != 0) {
                    mask |= TIME_MASK_LT_TS;
                }
                if (item.indexOf(TIME_MICRO_DT) != 0) {
                    mask |= TIME_MASK_LT_DT;
                }

                OSD_ITEM osdItem = new OSD_ITEM();
                osdItem.text = item;
                osdItem.mask = mask;
                osdItem.origin = new Point(origin);

                if (mask == 0) {
                    canvas.drawText(item, origin.x, origin.y, mPaint);
                    canvas.drawText(item, origin.x, origin.y, mPaintStroker);

                    mPaintStroker.getTextBounds(item, 0, item.length(), textBounds);
                } else {
                    String newText = updateOSDTime(item, ts);

                    canvas.drawText(newText, origin.x, origin.y, mPaint);
                    canvas.drawText(newText, origin.x, origin.y, mPaintStroker);

                    mPaintStroker.getTextBounds(newText, 0, item.length(), textBounds);
                }

                osdItem.previousRect = new Rect(origin.x, origin.y, origin.x + textBounds.width(), origin.y + textBounds.height());

                mOSDItems.add(osdItem);
                origin.y += (textBounds.height() * 3) >> 1;
            }
        }

        if (!TextUtils.isEmpty(mOSDLeftBottom)) {

            String[] items = mOSDLeftBottom.split("\n");
            Point origin = new Point(margin, bmHeight - margin);

            for(int idx = items.length-1; idx >= 0; idx--) {

                int mask = 0;
                String item = items[idx];
                if (item.indexOf(TIME_MICRO_TS) != 0) {
                    mask |= TIME_MASK_LB_TS;
                }
                if (item.indexOf(TIME_MICRO_DT) != 0) {
                    mask |= TIME_MASK_LB_DT;
                }

                OSD_ITEM osdItem = new OSD_ITEM();
                osdItem.text = item;
                osdItem.mask = mask;
                osdItem.origin = new Point(origin);
                if (mask == 0) {
                    mPaintStroker.getTextBounds(item, 0, item.length(), textBounds);
                    y = origin.y - textBounds.height();

                    canvas.drawText(item, origin.x, y, mPaint);
                    canvas.drawText(item, origin.x, y, mPaintStroker);
                } else {
                    String newText = updateOSDTime(item, ts);

                    mPaintStroker.getTextBounds(newText, 0, newText.length(), textBounds);
                    y = origin.y - textBounds.height();

                    canvas.drawText(newText, origin.x, y, mPaint);
                    canvas.drawText(newText, origin.x, y, mPaintStroker);
                }

                osdItem.previousRect = new Rect(origin.x, y, origin.x + textBounds.width(), y + textBounds.height());
                mOSDItems.add(osdItem);
                origin.y -= (textBounds.height() * 3) >> 1;
            }
        }

        if (!TextUtils.isEmpty(mOSDRightTop)) {

            String[] items = mOSDRightTop.split("\n");
            Point origin = new Point(bmWidth - margin, margin + statusHeight);

            for (String item : items) {

                int mask = 0;
                if (item.indexOf(TIME_MICRO_TS) != 0) {
                    mask |= TIME_MASK_RT_TS;
                }
                if (item.indexOf(TIME_MICRO_DT) != 0) {
                    mask |= TIME_MASK_RT_DT;
                }

                OSD_ITEM osdItem = new OSD_ITEM();
                osdItem.text = item;
                osdItem.origin = new Point(origin);
                osdItem.mask = mask;

                if (mask == 0) {
                    mPaintStroker.getTextBounds(item, 0, item.length(), textBounds);

                    canvas.drawText(item, origin.x - textBounds.width(), origin.y, mPaint);
                    canvas.drawText(item, origin.x - textBounds.width(), origin.y, mPaintStroker);
                } else {

                    String newText = updateOSDTime(item, ts);
                    mPaintStroker.getTextBounds(newText, 0, item.length(), textBounds);

                    canvas.drawText(newText, origin.x - textBounds.width(), origin.y, mPaint);
                    canvas.drawText(newText, origin.x - textBounds.width(), origin.y, mPaintStroker);
                }
                osdItem.previousRect = new Rect(origin.x - textBounds.width(), origin.y, origin.x, origin.y + textBounds.height());
                mOSDItems.add(osdItem);

                origin.y += (textBounds.height() * 3) >> 1;
            }
        }

        if (!TextUtils.isEmpty(mOSDRightBottom)) {

            String[] items = mOSDRightBottom.split("\n");
            Point origin = new Point(bmWidth - margin, bmHeight - margin);

            for(int idx = items.length - 1; idx >= 0; idx--) {

                int mask = 0;
                String item = items[idx];
                if (item.indexOf(TIME_MICRO_TS) != 0) {
                    mask |= TIME_MASK_RB_TS;
                }
                if (item.indexOf(TIME_MICRO_DT) != 0) {
                    mask |= TIME_MASK_RB_DT;
                }

                OSD_ITEM osdItem = new OSD_ITEM();
                osdItem.text = item;
                osdItem.origin = new Point(origin);
                osdItem.mask = mask;
                if (mask == 0) {
                    mPaintStroker.getTextBounds(item, 0, item.length(), textBounds);

                    canvas.drawText(item, origin.x - textBounds.width(), origin.y - textBounds.height(), mPaint);
                    canvas.drawText(item, origin.x - textBounds.width(), origin.y - textBounds.height(), mPaintStroker);
                } else {
                    String newText = updateOSDTime(item, ts);
                    mPaintStroker.getTextBounds(newText, 0, item.length(), textBounds);

                    canvas.drawText(newText, origin.x - textBounds.width(), origin.y - textBounds.height(), mPaint);
                    canvas.drawText(newText, origin.x - textBounds.width(), origin.y - textBounds.height(), mPaintStroker);
                }

                osdItem.previousRect = new Rect(origin.x - textBounds.width(), origin.y - textBounds.height(), origin.x, origin.y);
                mOSDItems.add(osdItem);

                origin.y -= (textBounds.height() * 3) >> 1;
            }
        }
    }

    private void updateOSD(Bitmap bm, long ts) {

        Log.i("OSD", "prepareOSD " + Long.toString(ts / 1000));
        if (mStatusBarHeight == -1) {
            mStatusBarHeight = getStatusBarHeight(this);
        }
        int statusHeight = mStatusBarHeight;

        Canvas canvas = new Canvas(bm);
        // boolean aa = canvas.isHardwareAccelerated();
        Rect textBounds = new Rect();

        canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);

        for (OSD_ITEM osdItem : mOSDItems) {
            String text = updateOSDTime(osdItem.text, ts);

            int x = osdItem.previousRect.left;
            int y = osdItem.previousRect.top;

            if ((osdItem.mask & TIME_MASK_LT) != 0) {
                mPaintStroker.getTextBounds(text, 0, text.length(), textBounds);

                // Log.d("OSD", "UPD OSD x=" + x + " y=" + y);
                canvas.drawText(text, x, y, mPaint);
                canvas.drawText(text, x, y, mPaintStroker);
                osdItem.previousRect.set(x, y, x + textBounds.width(), y + textBounds.height());

            } else if ((osdItem.mask & TIME_MASK_LB) != 0) {
                mPaintStroker.getTextBounds(text, 0, text.length(), textBounds);

                y = osdItem.origin.y - textBounds.height();
                canvas.drawText(text, x, y, mPaint);
                canvas.drawText(text, x, y, mPaintStroker);
                osdItem.previousRect.set(x, y, x + textBounds.width(), y + textBounds.height());

            } else if ((osdItem.mask & TIME_MASK_RT) != 0) {
                mPaintStroker.getTextBounds(text, 0, text.length(), textBounds);

                x = osdItem.origin.x - textBounds.width();
                canvas.drawText(text, x, osdItem.origin.y, mPaint);
                canvas.drawText(text, x, osdItem.origin.y, mPaintStroker);
                osdItem.previousRect.set(x, osdItem.origin.y, x + textBounds.width(), osdItem.origin.y + textBounds.height());

            } else if ((osdItem.mask & TIME_MASK_RB) != 0) {
                mPaintStroker.getTextBounds(text, 0, text.length(), textBounds);

                x = osdItem.origin.x - textBounds.width();
                y = osdItem.origin.y - textBounds.height();
                canvas.drawText(text, x, y, mPaint);
                canvas.drawText(text, x, y, mPaintStroker);
                osdItem.previousRect.set(x, y, x + textBounds.width(), y + textBounds.height());
            } else {
                canvas.drawText(text, x, y, mPaint);
                canvas.drawText(text, x, y, mPaintStroker);
            }
        }

    }

    private String updateOSDTime(String osd, long ts) {
        String newOSD = osd;
        if (newOSD.indexOf(TIME_MICRO_TS) != -1) {
            newOSD =  newOSD.replace(TIME_MICRO_TS, Long.toString(ts / 1000));
        }
        if (newOSD.indexOf(TIME_MICRO_DT) != -1) {
            if (mDateFormater == null) {
                mDateFormater = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            }
            Date dt = new Date(ts);
            newOSD = newOSD.replace(TIME_MICRO_DT, mDateFormater.format(dt));
        }

        return newOSD;
    }

    private void releaseCamera() {
        if (mPreviewView != null) {
            mPreviewView.onPause();
        }

        if (mGPUCameraRecorder != null) {
            mGPUCameraRecorder.stop();
            mGPUCameraRecorder.release();
            mGPUCameraRecorder = null;
            Log.i("OSD", "mGPUCameraRecorder destroyed");
        }

        if (mPreviewView != null) {
            ((FrameLayout) findViewById(R.id.wrap_view)).removeView(mPreviewView);
            mPreviewView = null;
        }
    }

    private void setUpCameraView() {

        runOnUiThread(() -> {

            final AspectRatioFrameLayout frameLayout = (AspectRatioFrameLayout)findViewById(R.id.wrap_view);
            frameLayout.removeAllViews();

            mPreviewView = null;
            mPreviewView = new AutoFitGLView(getApplicationContext());
            mPreviewView.setTouchListener((event, width, height) -> {
                if (mGPUCameraRecorder == null) return;
                mGPUCameraRecorder.changeManualFocusPoint(event.getX(), event.getY(), width, height);
            });

            mPreviewView.getHolder().addCallback(new SurfaceHolder.Callback() {
                @Override
                public void surfaceCreated(@NonNull SurfaceHolder holder) {

                }

                @Override
                public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {
                    if (width > 0 && height > 0) {

                    }
                }

                @Override
                public void surfaceDestroyed(@NonNull SurfaceHolder holder) {

                }
            });

            frameLayout.addView(mPreviewView);

            frameLayout.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
                @Override
                public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {

                    int width = right - left;
                    int height = bottom - top;

                    if (width <= 0 || height <= 0) {
                        return;
                    }
                    frameLayout.removeOnLayoutChangeListener(this);

                    if (!TextUtils.isEmpty(mOSDLeftTop) || !TextUtils.isEmpty(mOSDLeftTop) || !TextUtils.isEmpty(mOSDLeftTop) || !TextUtils.isEmpty(mOSDLeftTop)) {
                        mPreviewWidth = frameLayout.getMeasuredWidth();
                        mPreviewHeight = frameLayout.getMeasuredHeight();

                        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
                        mPaint.setStyle(Paint.Style.FILL);
                        mPaint.setColor(Color.WHITE);
                        int fontSize = DEFAULT_FONT_SIZE * mPreviewHeight / 1024;
                        float strokeWidth = DEFAULT_STROKE_WIDTH * mPreviewHeight / 1024;
                        mPaint.setTextSize(fontSize);

                        mPaintStroker = new Paint(Paint.ANTI_ALIAS_FLAG);
                        mPaintStroker.setStyle(Paint.Style.STROKE);
                        mPaintStroker.setColor(Color.BLACK);
                        mPaintStroker.setTextSize(fontSize);
                        mPaintStroker.setStrokeWidth(strokeWidth);

                        Bitmap bm = Bitmap.createBitmap(mPreviewWidth, mPreviewHeight, Bitmap.Config.ARGB_8888);
                        Canvas canvas = new Canvas(bm);
                        canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);

                        long ts = System.currentTimeMillis();
                        long zeroTs = ts - (ts % 1000);
                        initOSD(bm, zeroTs);
                        mOSDFilter = new GlWatermarkFilter(bm);

                        if (mGPUCameraRecorder != null) {
                            mGPUCameraRecorder.setFilter(mOSDFilter);
                        }

                        mOsdThread.start();
                    }

                }
            });



        });
    }

    private void setUpCamera() {
        setUpCameraView();

        if (TextUtils.isEmpty(mNextVideoAbsolutePath)) {
            mNextVideoAbsolutePath = getVideoFilePath(this);
        }

        mGPUCameraRecorder = new GPUCameraRecorderBuilder(this, mPreviewView)
                //.recordNoFilter(true)
                .cameraRecordListener(new CameraRecordListener() {
                    @Override
                    public void onGetFlashSupport(boolean flashSupport) {
                    }

                    @Override
                    public void onRecordComplete() {
                        // mHandler.removeCallbacks(mTimerRunnable);
                    }

                    @Override
                    public void onRecordStart() {
                        Log.i("OSD", "Record Start ");
                        mHandler.postDelayed(new Runnable() {
                            @Override
                            public void run() {
                                Log.i("OSD", "Record Stop " + Long.toString(mDuration));
                                mGPUCameraRecorder.stop();

                                int aa = 0;
                            }
                        }, 1200L + mDuration * 1000);
                    }

                    @Override
                    public void onError(Exception exception) {
                        Log.e("GPUCameraRecorder", exception.toString());
                        mOsdThread.interrupt();
                        broadcastVideoFile(false, mNextVideoAbsolutePath);
                    }

                    @Override
                    public void onCameraThreadFinish() {
                    }

                    @Override
                    public void onVideoFileReady() {
						// exportMp4ToGallery(getApplicationContext(), mNextVideoAbsolutePath);
                        mOsdThread.interrupt();
                        broadcastVideoFile(true, mNextVideoAbsolutePath);
                        mHandler.postDelayed(new Runnable() {
                            @Override
                            public void run() {
                                Camera2VideoActivity.this.finish();
                            }
                        }, 1000);
                    }

                    @Override
                    public void onDurationArrived() {
                    }
                })
                .videoSize(mVideoWidth, mVideoHeight)
                .cameraSize(mCameraWidth, mCameraHeight)
                .cameraId(Integer.toString(mCameraId))
                .mute(true)
                .duration(mDuration * 1000)
                .build();

        Log.i("OSD", "mGPUCameraRecorder created");
        if (mOSDFilter != null) {
            mGPUCameraRecorder.setFilter(mOSDFilter);
        }
    }

//    private void changeFilter(Filters filters) {
//        GPUCameraRecorder.setFilter(Filters.getFilterInstance(filters, getApplicationContext()));
//    }


    private interface BitmapReadyCallbacks {
        void onBitmapReady(Bitmap bitmap);
    }

    private void captureBitmap(final BitmapReadyCallbacks bitmapReadyCallbacks) {
        mPreviewView.queueEvent(() -> {
            EGL10 egl = (EGL10) EGLContext.getEGL();
            GL10 gl = (GL10) egl.eglGetCurrentContext().getGL();
            Bitmap snapshotBitmap = createBitmapFromGLSurface(mPreviewView.getMeasuredWidth(), mPreviewView.getMeasuredHeight(), gl);

            runOnUiThread(() -> {
                bitmapReadyCallbacks.onBitmapReady(snapshotBitmap);
            });
        });
    }

    private void broadcastVideoFile(boolean result, String path) {
        if (mDuration <= 0) {
            return;
        }

        if (!TextUtils.isEmpty(mOutputPath)) {
            File file = new File(path);
            file.renameTo(new File(mOutputPath));
            path = mOutputPath;
        }

        if (mResultType == RESULT_TYPE_NONE) {
            return;
        }

        Context context = getApplicationContext();
        String receiverName = MicroPhotoService.AlarmReceiver.class.getName();
        String packageName = context.getPackageName();

        Intent intent = new Intent(ACTION_MP_VIDEO_FINISHED);
        // intent.setPackage(packageName);
        intent.putExtra("photoOrVideo", false);
        intent.putExtra("result", result);
        intent.putExtra("path", path);
        intent.putExtra("videoId", mVideoId);

        // intent.setComponent(new ComponentName(packageName, receiverName));

        if (mResultType == RESULT_TYPE_ACTIVITY_RESULT) {
            setResult(RESULT_OK, intent);
        } else {
            // Log.i(TAG, "Notify recording videoId=" + Long.toString(mVideoId) + " " + path);
            LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(getApplicationContext());
            localBroadcastManager.sendBroadcast(intent);

            context.sendBroadcast(intent);
        }
    }

    private String getVideoFilePath(Context context) {
        // final File dir = context.getExternalFilesDir(null);
        String path = Environment.getExternalStorageDirectory().getAbsolutePath();
        if (!path.endsWith(File.separator)) {
            path += File.separator;
        }
        path += context.getPackageName() + File.separator;
        File file = new File(path);
        if (!file.exists()) {
            file.mkdirs();
        }
        path += System.currentTimeMillis() + ".mp4";
        return path;
    }

    private Bitmap createBitmapFromGLSurface(int w, int h, GL10 gl) {

        int bitmapBuffer[] = new int[w * h];
        int bitmapSource[] = new int[w * h];
        IntBuffer intBuffer = IntBuffer.wrap(bitmapBuffer);
        intBuffer.position(0);

        try {
            gl.glReadPixels(0, 0, w, h, GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE, intBuffer);
            int offset1, offset2, texturePixel, blue, red, pixel;
            for (int i = 0; i < h; i++) {
                offset1 = i * w;
                offset2 = (h - i - 1) * w;
                for (int j = 0; j < w; j++) {
                    texturePixel = bitmapBuffer[offset1 + j];
                    blue = (texturePixel >> 16) & 0xff;
                    red = (texturePixel << 16) & 0x00ff0000;
                    pixel = (texturePixel & 0xff00ff00) | red | blue;
                    bitmapSource[offset2 + j] = pixel;
                }
            }
        } catch (GLException e) {
            Log.e("CreateBitmap", "createBitmapFromGLSurface: " + e.getMessage(), e);
            return null;
        }

        return Bitmap.createBitmap(bitmapSource, w, h, Bitmap.Config.ARGB_8888);
    }

    public void saveAsPngImage(Bitmap bitmap, String filePath) {
        try {
            File file = new File(filePath);
            FileOutputStream outStream = new FileOutputStream(file);
            bitmap.compress(Bitmap.CompressFormat.PNG, 100, outStream);
            outStream.close();

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void exportMp4ToGallery(Context context, String filePath) {
        final ContentValues values = new ContentValues(2);
        values.put(MediaStore.Video.Media.MIME_TYPE, "video/mp4");
        values.put(MediaStore.Video.Media.DATA, filePath);
        context.getContentResolver().insert(MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
                values);
        context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE,
                Uri.parse("file://" + filePath)));
    }

    public static String getVideoFilePath() {
        return getAndroidMoviesFolder().getAbsolutePath() + "/" + new SimpleDateFormat("yyyyMM_dd-HHmmss").format(new Date()) + "GPUCameraRecorder.mp4";
    }

    public static File getAndroidMoviesFolder() {
        return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES);
    }

    private static void exportPngToGallery(Context context, String filePath) {
        Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
        File f = new File(filePath);
        Uri contentUri = Uri.fromFile(f);
        mediaScanIntent.setData(contentUri);
        context.sendBroadcast(mediaScanIntent);
    }

    public static String getImageFilePath() {
        return getAndroidImageFolder().getAbsolutePath() + "/" + new SimpleDateFormat("yyyyMM_dd-HHmmss").format(new Date()) + "GPUCameraRecorder.png";
    }

    public static File getAndroidImageFolder() {
        return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
    }

}
