package com.eitchsyh.instrument.eitc_erm_dental_flutter;

import android.content.ContentValues;
import android.content.Context;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.provider.MediaStore;
import android.text.TextUtils;
import android.view.PixelCopy;
import android.view.Surface;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.eitchsyh.instrument.Video;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Arrays;
import java.util.List;

import io.flutter.Log;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.view.TextureRegistry;

/**
 * 视频控制器
 */
public class VideoController implements MethodChannel.MethodCallHandler
{
    private final String TAG = "VideoController";

    private final Context context;
    private final Video video;
    private Surface surface;
    private TextureRegistry.SurfaceTextureEntry surfaceTextureEntry;
    private final FlutterEngine flutterEngine;
    private PixelCopy.OnPixelCopyFinishedListener pixelCopyFinishedListener;
    private final Handler pixelCopyHandler;
    private final HandlerThread pixelCopyThread;
    private boolean isRecording = false;
    private File recordingFile;
    private boolean isListenShot = false;
    private ServerSocket listenShotSocket = null;
    private MethodChannel channel;
    private Handler mainHandler;

    public VideoController(@NonNull Context context, @NonNull FlutterEngine flutterEngine)
    {
        this.context = context;
        this.flutterEngine = flutterEngine;
        channel = new MethodChannel(flutterEngine.getDartExecutor()
                                            .getBinaryMessenger(), "videoControl");
        channel.setMethodCallHandler(this);
        this.video = new Video();
        this.pixelCopyThread = new HandlerThread("pixelCopyThread");
        this.pixelCopyThread.start();
        this.pixelCopyHandler = new Handler(this.pixelCopyThread.getLooper());
        this.mainHandler = new Handler(Looper.getMainLooper());
        //监听抓拍
        this.listenShot();
    }

    public void release()
    {
        channel.setMethodCallHandler(null);
        this.pixelCopyHandler.removeCallbacksAndMessages(null);
        try
        {
            this.pixelCopyThread.interrupt();
        }
        catch (Exception e)
        {
            Log.e(TAG, "", e);
        }
        mainHandler.removeCallbacksAndMessages(null);
        isListenShot = false;
        if (listenShotSocket != null)
        {
            try
            {
                listenShotSocket.close();
            }
            catch (Exception e)
            {
                Log.e(TAG, "关闭抓拍监听服务器异常");
            }
        }
        listenShotSocket = null;
    }

    @Override
    public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result)
    {
        switch (call.method)
        {
            case "getConnectStatus":
                onGetConnectStatus(result);
                break;
            case "startVideo":
                onStartVideo(result);
                break;
            case "stopVideo":
                onStopVideo(result);
                break;
            case "takePhoto":
                onTakePhoto(call.arguments, result);
                break;
            case "savePhoto":
                onSavePhoto(call.arguments, result);
                break;
            case "startRecord":
                onStartRecord(call.arguments, result);
                break;
            case "stopRecord":
                onStopRecord(result);
                break;
            case "saveRecord":
                onSaveRecord(call.arguments, result);
                break;
            default:
                result.notImplemented();
                break;
        }
    }

    private void listenShot()
    {
        new Thread(() -> {
            try
            {
                listenShotSocket = new ServerSocket(40004);
                byte[] bytes = new byte[22];
                isListenShot = true;
                int length = 0;
                while (isListenShot)
                {
                    Socket socket = listenShotSocket.accept();
                    InputStream inputStream = socket.getInputStream();
                    length = inputStream.read(bytes);
                    Log.d(TAG, "listenShotSocket: length = " + length + Arrays.toString(bytes));
                    //通知flutter端设备拍照
                    mainHandler.post(() -> channel.invokeMethod("onDeviceTakePhoto", null));

                    inputStream.close();
                    socket.close();
                }
            }
            catch (IOException e)
            {
                Log.e(TAG, "监听抓拍异常", e);
            }
        }).start();
    }

    /**
     * 获取连接状态
     *
     * @param result
     */
    private void onGetConnectStatus(@NonNull MethodChannel.Result result)
    {
        new Thread(() -> {
            int status = video.getVersion();
            Log.d(TAG, "获取连接状态=" + status);
            result.success(status);
        }).start();
    }

    /**
     * 开始视频
     *
     * @param result
     */
    private void onStartVideo(@NonNull MethodChannel.Result result)
    {
        if (this.surface != null)
        {
            this.onStopVideo(null);
        }
        try
        {
            Log.d(TAG, "开始视频");
            this.surfaceTextureEntry = this.flutterEngine.getRenderer()
                    .createSurfaceTexture();
            this.surface = new Surface(this.surfaceTextureEntry.surfaceTexture());
            this.video.startVideo(surface);
            result.success(this.surfaceTextureEntry.id());
        }
        catch (Exception e)
        {
            Log.e(TAG, "开始视频异常", e);
            result.error("100", "开始视频异常", e);
        }
    }

    /**
     * 停止视频
     *
     * @param result
     */
    private void onStopVideo(@Nullable MethodChannel.Result result)
    {
        if (this.surface == null)
        {
            return;
        }
        try
        {
            Log.d(TAG, "停止视频");
            this.video.stopVideo();
            this.surface.release();
            this.surface = null;
            if (this.surfaceTextureEntry != null)
            {
                this.surfaceTextureEntry.release();
            }
            this.surfaceTextureEntry = null;
            if (result != null)
            {
                result.success(null);
            }
        }
        catch (Exception e)
        {
            Log.e(TAG, "停止视频异常", e);
            if (result != null)
            {
                result.error("101", "停止视频异常", e);
            }
        }
    }

    /**
     * 拍照
     *
     * @param arguments 参数
     * @param result    结果
     */
    private void onTakePhoto(@Nullable Object arguments, @Nullable MethodChannel.Result result)
    {
        if (surface == null)
        {
            return;
        }
        try
        {
            String prefix;
            if (arguments instanceof String)
            {
                prefix = (String) arguments;
            }
            else
            {
                prefix = "";
            }

            Bitmap bitmap = Bitmap.createBitmap(1920, 1080, Bitmap.Config.ARGB_8888);
            PixelCopy.request(surface, bitmap, copyResult -> {
                if (copyResult == PixelCopy.SUCCESS)
                {
                    try
                    {
                        String path = savePhotoToTemp(context, prefix, bitmap);
                        bitmap.recycle();
                        if (result != null)
                        {
                            if (!TextUtils.isEmpty(path))
                            {
                                result.success(path);
                            }
                            else
                            {
                                result.error("203", "拍照失败", new Exception("保存照片失败"));
                            }
                        }
                    }
                    catch (IOException e)
                    {
                        Log.e(TAG, "拍照异常", e);
                        if (result != null)
                        {
                            result.error("202", "拍照异常", e);
                        }
                    }
                }
                else
                {
                    if (result != null)
                    {
                        result.error("201", "拍照异常", new Exception("像素拷贝失败"));
                    }
                }
            }, pixelCopyHandler);
        }
        catch (Exception e)
        {
            Log.e(TAG, "拍照异常", e);
            if (result != null)
            {
                result.error("200", "拍照异常", e);
            }
        }
    }

    /**
     * 保存照片到临时目录
     *
     * @param context
     * @param bitmap
     * @return
     * @throws IOException
     */
    @Nullable
    private String savePhotoToTemp(Context context, String prefix, Bitmap bitmap) throws IOException
    {
        long now = System.currentTimeMillis();
        //保存到临时目录
        File dir = Utils.getPhotoTempDir(context);
        try
        {
            File file = new File(dir.getAbsoluteFile(), prefix + now + ".jpg");
            FileOutputStream fos = new FileOutputStream(file);
            bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos);
            fos.close();
            return file.getAbsolutePath();
        }
        catch (Exception e)
        {
            Log.e(TAG, "保存照片异常", e);
        }
        return null;
    }

    /**
     * 保存照片
     *
     * @param arguments
     * @param result
     */
    private void onSavePhoto(@Nullable Object arguments, @NonNull MethodChannel.Result result)
    {
        this.moveFile(Utils.getPhotoDir(context), arguments, result);
    }

    /**
     * 开始录像
     *
     * @param result
     */
    private void onStartRecord(@Nullable Object arguments, @NonNull MethodChannel.Result result)
    {
        if (this.isRecording)
        {
            result.error("301", "正在录像中", new Exception("正在录像中"));
            return;
        }
        String prefix;
        if (arguments instanceof String)
        {
            prefix = (String) arguments;
        }
        else
        {
            prefix = "";
        }
        File dir = Utils.getRecordTempDir(context);
        long now = System.currentTimeMillis();
        File file = new File(dir.getAbsoluteFile(), prefix + now + ".avi");
        int r = this.video.startRecord(file.getAbsolutePath());
        if (r != 0)
        {
            this.isRecording = false;
            result.error("300", "开始录像失败", new Exception("开始录像失败"));
            return;
        }
        this.isRecording = true;
        this.recordingFile = file;
        result.success(null);
    }

    /**
     * 停止录像
     *
     * @param result
     */
    private void onStopRecord(@NonNull MethodChannel.Result result)
    {
        if (!this.isRecording)
        {
            result.error("302", "没有在录像中", new Exception("没有在录像中"));
            return;
        }
        this.video.stopRecord();
        result.success(this.recordingFile == null ? "" : this.recordingFile.getAbsolutePath());
        this.isRecording = false;
        this.recordingFile = null;
    }

    /**
     * 保存录像
     *
     * @param arguments
     * @param result
     */
    private void onSaveRecord(@Nullable Object arguments, @NonNull MethodChannel.Result result)
    {
        this.moveFile(Utils.getRecordDir(context), arguments, result);
    }

    /**
     * 移动文件
     *
     * @param arguments
     * @param result
     */
    private void moveFile(@NonNull File baseDir, @Nullable Object arguments,
                          @NonNull MethodChannel.Result result)
    {
        if (!(arguments instanceof List))
        {
            result.error("903", "文件列表参数类型错误", new Exception("文件列表参数类型错误"));
            return;
        }
        List<String> paths = (List<String>) arguments;
        if (paths.size() != 2)
        {
            Log.d(TAG, "保存文件,文件列表参数长度错误,长度=" + paths.size());
            result.error("904", "文件列表参数长度错误", new Exception("文件列表参数长度错误"));
            return;
        }
        if (!((paths.get(0)
                .endsWith(".jpg") && paths.get(1)
                .endsWith(".jpg")) || (paths.get(0)
                .endsWith(".avi") && paths.get(1)
                .endsWith(".avi"))))
        {
            Log.d(TAG, "文件后缀名错误");
            result.error("905", "文件后缀名错误", new Exception("文件后缀名错误"));
            return;
        }
        Log.d(TAG, "保存文件,临时路径=" + paths.get(0) + ",文件路径=" + paths.get(1));
        new Thread(() -> {
            File newRecord = new File(paths.get(1));
            if (!newRecord.exists())
            {
                Log.d(TAG, "保存文件,但是要保存的文件不存在");
                result.error("901", "要保存的文件不存在", new Exception("要保存的文件不存在"));
            }
            /*File destRecord = new File(
                    baseDir.getAbsoluteFile() + File.separator + newRecord.getName());
            Log.d(TAG, "保存文件,目标路径=" + destRecord.getAbsolutePath());
            try
            {
                boolean bo = newRecord.renameTo(destRecord);
                Log.d(TAG, "移动文件结果=" + bo);
                if (!bo)
                {
                    result.error("902", "重命名文件失败", new Exception("重命名文件失败"));
                }
                else
                {
                    Utils.notifyScanFile(context, destRecord);
                    result.success(destRecord.getAbsolutePath());
                }
            }
            catch (Exception e)
            {
                Log.e(TAG, "重命名文件失败", e);
                result.error("904", "重命名文件失败", e);
            }*/

            boolean isJpg = newRecord.getAbsolutePath()
                    .endsWith(".jpg");
            ContentValues values = new ContentValues();
            values.put(MediaStore.MediaColumns.DISPLAY_NAME, newRecord.getName());
            values.put(MediaStore.MediaColumns.RELATIVE_PATH,
                       isJpg ? Utils.getPhotoMediaStoreRelativePath() : Utils.getVideoMediaStoreRelativePath());

            Uri insertUri;
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
            {
                insertUri = isJpg ? MediaStore.Images.Media.getContentUri(
                        MediaStore.VOLUME_EXTERNAL_PRIMARY) : MediaStore.Video.Media.getContentUri(
                        MediaStore.VOLUME_EXTERNAL_PRIMARY);
            }
            else
            {
                insertUri = isJpg ? MediaStore.Images.Media.EXTERNAL_CONTENT_URI : MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
            }
            try
            {
                Uri uri = context.getContentResolver()
                        .insert(insertUri, values);
                if (uri == null)
                {
                    Log.d(TAG, "插入到媒体库失败,返回uri为null");
                    result.error("902", "插入到媒体库失败", new Exception("插入到媒体库失败"));
                }
                else
                {
                    OutputStream os = null;
                    FileInputStream fis = null;
                    try
                    {
                        os = context.getContentResolver()
                                .openOutputStream(uri);
                        if (os != null)
                        {
                            fis = new FileInputStream(newRecord);
                            int len = -1;
                            byte[] bytes = new byte[1024];
                            while ((len = fis.read(bytes)) != -1)
                            {
                                os.write(bytes, 0, len);
                            }
                            Log.d(TAG, "写入到媒体库完成");
                            result.success(null);
                        }
                        else
                        {
                            result.error("903", "写保存文件到媒体库失败,输出流为null",
                                         new Exception("写保存文件到媒体库失败,输出流为null"));
                        }
                    }
                    catch (Exception e)
                    {
                        Log.e(TAG, "写保存文件到媒体库异常", e);
                        result.error("904", "写保存文件到媒体库异常", e);
                    }
                    finally
                    {
                        if (os != null)
                        {
                            try
                            {
                                os.flush();
                                os.close();
                            }
                            catch (Exception e)
                            {
                                Log.e(TAG, "关闭os异常", e);
                            }
                        }
                        if (fis != null)
                        {
                            try
                            {
                                fis.close();
                            }
                            catch (Exception e)
                            {
                                Log.e(TAG, "关闭fis异常", e);
                            }
                        }
                    }
                }
            }
            catch (Exception e)
            {
                Log.e(TAG, "写入到媒体库失败");
            }

            //删除临时文件
            File tempRecord = new File(paths.get(0));
            if (tempRecord.exists())
            {
                boolean bo = tempRecord.delete();
                Log.d(TAG, "删除临时文件结果=" + bo);
            }
            //删除保存文件
            if (newRecord.exists())
            {
                boolean bo = newRecord.delete();
                Log.d(TAG, "删除保存文件结果=" + bo);
            }
        }).start();
    }
}
