package com.megvii.idcardlib;

import android.app.Activity;
import android.content.Intent;
import android.graphics.SurfaceTexture;
import android.hardware.Camera;
import android.hardware.Camera.CameraInfo;
import android.hardware.Camera.PreviewCallback;
import android.hardware.Camera.Size;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.view.TextureView;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.TextView;

import com.megvii.idcardlib.util.ConUtil;
import com.megvii.idcardlib.util.DialogUtil;
import com.megvii.idcardlib.util.FaceCamera;
import com.megvii.idcardlib.util.IDetection;
import com.megvii.idcardlib.util.IMediaPlayer;
import com.megvii.idcardlib.util.Screen;
import com.megvii.idcardlib.util.SensorUtil;
import com.megvii.idcardlib.view.CircleProgressBar;
import com.megvii.livenessdetection.DetectionConfig;
import com.megvii.livenessdetection.DetectionFrame;
import com.megvii.livenessdetection.Detector;
import com.megvii.livenessdetection.Detector.DetectionFailedType;
import com.megvii.livenessdetection.Detector.DetectionListener;
import com.megvii.livenessdetection.Detector.DetectionType;
import com.megvii.livenessdetection.FaceQualityManager;
import com.megvii.livenessdetection.FaceQualityManager.FaceQualityErrorType;
import com.megvii.livenessdetection.bean.FaceIDDataStruct;
import com.megvii.livenessdetection.bean.FaceInfo;

import org.json.JSONException;
import org.json.JSONObject;

import java.io.Serializable;
import java.util.List;
import java.util.Map;

public class LivenessActivity extends Activity
        implements PreviewCallback, DetectionListener, TextureView.SurfaceTextureListener {

    private TextureView camerapreview;
    private FaceMask mFaceMask;// 画脸位置的类(调试时会用到)
    private ProgressBar mProgressBar;// 网络上传请求验证时出现的ProgressBar
    private LinearLayout headViewLinear;// "请在光线充足的情况下进行检测"这个视图
    private RelativeLayout rootView;// 根视图
    private TextView timeOutText;
    private RelativeLayout timeOutRel;
    private CircleProgressBar mCircleProgressBar;
    private Detector mDetector;// 活体检测器
    private FaceCamera mICamera;// 照相机工具类
    private Handler mainHandler;
    private HandlerThread mHandlerThread = new HandlerThread("videoEncoder");
    private Handler mHandler;
    private JSONObject jsonObject;
    private IMediaPlayer mIMediaPlayer;// 多媒体工具类
    private IDetection mIDetection;
    private DialogUtil mDialogUtil;
    private TextView promptText;
    private boolean isHandleStart;// 是否开始检测
    private FaceQualityManager mFaceQualityManager;
    private SensorUtil sensorUtil;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.liveness_layout);
        init();
        initData();
    }

    private void init() {
        sensorUtil = new SensorUtil(this);
        Screen.initialize(this);
        mainHandler = new Handler();
        mHandlerThread.start();
        mHandler = new Handler(mHandlerThread.getLooper());
        mIMediaPlayer = new IMediaPlayer(this);
        mDialogUtil = new DialogUtil(this);
        rootView = (RelativeLayout) findViewById(R.id.liveness_layout_rootRel);
        mIDetection = new IDetection(this, rootView);
        mFaceMask = (FaceMask) findViewById(R.id.liveness_layout_facemask);
        mICamera = new FaceCamera();
        promptText = (TextView) findViewById(R.id.liveness_layout_promptText);
        camerapreview = (TextureView) findViewById(R.id.liveness_layout_textureview);
        camerapreview.setSurfaceTextureListener(this);
        mProgressBar = (ProgressBar) findViewById(R.id.liveness_layout_progressbar);
        mProgressBar.setVisibility(View.INVISIBLE);
        headViewLinear = (LinearLayout) findViewById(R.id.liveness_layout_bottom_tips_head);
        headViewLinear.setVisibility(View.VISIBLE);
        timeOutRel = (RelativeLayout) findViewById(R.id.detection_step_timeoutRel);
        timeOutText = (TextView) findViewById(R.id.detection_step_timeout_garden);
        mCircleProgressBar = (CircleProgressBar) findViewById(R.id.detection_step_timeout_progressBar);

        mIDetection.viewsInit();
    }

    /**
     * 初始化数据
     */
    private void initData() {
        // 初始化活体检测器
        DetectionConfig config = new DetectionConfig.Builder().build();
        mDetector = new Detector(this, config);
        boolean initSuccess = mDetector.init(this, ConUtil.readModel(this), "");
        if (!initSuccess) {
            mDialogUtil.showDialog(getString(R.string.meglive_detect_initfailed));
        }
        // 初始化动画
        new Thread(new Runnable() {
            @Override
            public void run() {
                mIDetection.animationInit();
            }
        }).start();
    }

    @Override
    protected void onResume() {
        super.onResume();
        isHandleStart = false;
        int cameraID = 1;
        if (!FaceCamera.hasFrontFacingCamera())
            cameraID = 0;
        // 打开照相机
        Camera mCamera = mICamera.openCamera(this, cameraID);
        if (mCamera != null) {
            CameraInfo cameraInfo = new CameraInfo();
            Camera.getCameraInfo(cameraID, cameraInfo);
            mFaceMask.setFrontal(cameraInfo.facing == CameraInfo.CAMERA_FACING_FRONT);
            // 获取到相机分辨率对应的显示大小,并把这个值复制给camerapreview
            RelativeLayout.LayoutParams layout_params = mICamera.getLayoutParam();
            camerapreview.setLayoutParams(layout_params);
            mFaceMask.setLayoutParams(layout_params);
            // 初始化人脸质量检测管理类
            mFaceQualityManager = new FaceQualityManager(1 - 0.5f, 0.5f);
            mIDetection.mCurShowIndex = -1;

        } else {
            mDialogUtil.showDialog(getString(R.string.meglive_camera_initfailed));
        }
    }

    /**
     * 开始检测
     */
    private void handleStart() {
        if (isHandleStart)
            return;
        isHandleStart = true;
        // 开始动画
        Animation animationIN = AnimationUtils.loadAnimation(LivenessActivity.this, R.anim.liveness_rightin);
        Animation animationOut = AnimationUtils.loadAnimation(LivenessActivity.this, R.anim.liveness_leftout);
        headViewLinear.startAnimation(animationOut);
        mIDetection.mAnimViews[0].setVisibility(View.VISIBLE);
        mIDetection.mAnimViews[0].startAnimation(animationIN);
        animationOut.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {
            }

            @Override
            public void onAnimationRepeat(Animation animation) {
            }

            @Override
            public void onAnimationEnd(Animation animation) {
                timeOutRel.setVisibility(View.VISIBLE);
            }
        });
        // 开始活体检测
        mainHandler.post(mTimeoutRunnable);

        jsonObject = new JSONObject();
    }

    private Runnable mTimeoutRunnable = new Runnable() {
        @Override
        public void run() {
            // 倒计时开始
            initDetecteSession();
            if (mIDetection.mDetectionSteps != null)
                changeType(mIDetection.mDetectionSteps.get(0), 10);
        }
    };

    private void initDetecteSession() {
        if (mICamera.mCamera == null)
            return;

        mProgressBar.setVisibility(View.INVISIBLE);
        mIDetection.detectionTypeInit();

        mCurStep = 0;
        mDetector.reset();
        mDetector.changeDetectionType(mIDetection.mDetectionSteps.get(0));
    }

    /**
     * 照相机预览数据回调 (PreviewCallback的接口回调方法)
     */
    @Override
    public void onPreviewFrame(final byte[] data, Camera camera) {
        Size previewsize = camera.getParameters().getPreviewSize();
        int angle = 360 - mICamera.getCameraAngle(this);
        if (mICamera.cameraId == 0)
            angle = angle - 180;
        // 活体检测器检测
        mDetector.doDetection(data, previewsize.width, previewsize.height, angle);
    }

    /**
     * 活体验证成功 (DetectionListener的接口回调方法)
     */
    @Override
    public DetectionType onDetectionSuccess(final DetectionFrame validFrame) {
        mIMediaPlayer.reset();
        mCurStep++;
        mFaceMask.setFaceInfo(null);

        if (mCurStep == mIDetection.mDetectionSteps.size()) {
            mProgressBar.setVisibility(View.VISIBLE);
            getLivenessData();
        } else
            changeType(mIDetection.mDetectionSteps.get(mCurStep), 10);

        // 检测器返回值:如果不希望检测器检测则返回DetectionType.DONE,如果希望检测器检测动作则返回要检测的动作
        return mCurStep >= mIDetection.mDetectionSteps.size() ? DetectionType.DONE
                : mIDetection.mDetectionSteps.get(mCurStep);
    }

    private void getLivenessData() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                final FaceIDDataStruct idDataStruct = mDetector.getFaceIDDataStruct();
                final String delta = idDataStruct.delta;
                final Map<String, byte[]> images = idDataStruct.images;
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        handleResult(R.string.verify_success, delta, images);
                    }
                });
//                for (String key : idDataStruct.images.keySet()) {
//                    byte[] data = idDataStruct.images.get(key);
//                    if (key.equals("image_best")) {
//                        byte[] imageBestData = data;// 这是最好的一张图片
//                    } else if (key.equals("image_env")) {
//                        byte[] imageEnvData = data;// 这是一张全景图
//                    } else {
//                        // 其余为其他图片,根据需求自取
//                    }
//                }
            }
        }).start();
    }

    /**
     * 活体检测失败 (DetectionListener的接口回调方法)
     */
    @Override
    public void onDetectionFailed(final DetectionFailedType type) {
        //除下述类型外,还存在其它类型错误,但是不建议提示用户,详细可打印type.toString(),参考活体接口文档
        int resourceID = R.string.liveness_detection_failed;
        switch (type) {
            case ACTIONBLEND:
                resourceID = R.string.liveness_detection_failed_action_blend;
                break;
            case NOTVIDEO:
                resourceID = R.string.liveness_detection_failed_not_video;
                break;
            case TIMEOUT:
                resourceID = R.string.liveness_detection_failed_timeout;
                break;

        }
        handleResult(resourceID, null, null);
    }

    /**
     * 活体验证中(这个方法会持续不断的回调,返回照片detection信息) (DetectionListener的接口回调方法)
     */
    @Override
    public void onFrameDetected(long timeout, DetectionFrame detectionFrame) {
        // 如果需要适配加速度传感有问题的机型,可以加入超时自动过垂直判断机制,使用判断条件: sensorUtil.isVertical()||sensorUtil.isSensorFault()
        if (sensorUtil.isVertical()||sensorUtil.isSensorFault()) {
            faceOcclusion(detectionFrame);
            handleNotPass(timeout);
            mFaceMask.setFaceInfo(detectionFrame);
        } else {
            if (sensorUtil.Y == 0 && Build.MANUFACTURER.equals("HUAWEI"))
                promptText.setText(R.string.meglive_getpermission_motion);
            else
                promptText.setText(R.string.meglive_phone_vertical);
        }
    }

    /**
     * 照镜子环节
     * 流程:1,先从返回的DetectionFrame中获取FaceInfo。在FaceInfo中可以先判断这张照片上的人脸是否有被遮挡的状况
     * ,入股有直接return
     * 2,如果没有遮挡就把SDK返回的DetectionFramed传入人脸质量检测管理类mFaceQualityManager中获取FaceQualityErrorType的list
     * 3.通过返回的list来判断这张照片上的人脸是否合格。
     * 如果返回list为空或list中FaceQualityErrorType的对象数量为0则表示这张照片合格开始进行活体检测
     */
    private void faceOcclusion(DetectionFrame detectionFrame) {
        mFailFrame++;
        if (detectionFrame != null) {
            FaceInfo faceInfo = detectionFrame.getFaceInfo();
            if (faceInfo != null) {
                if (faceInfo.eyeLeftOcclusion > 0.5 || faceInfo.eyeRightOcclusion > 0.5) {
                    if (mFailFrame > 10) {
                        mFailFrame = 0;
                        promptText.setText(R.string.meglive_keep_eyes_open);
                    }
                    return;
                }
                if (faceInfo.mouthOcclusion > 0.5) {
                    if (mFailFrame > 10) {
                        mFailFrame = 0;
                        promptText.setText(R.string.meglive_keep_mouth_open);
                    }
                    return;
                }
                boolean faceTooLarge = faceInfo.faceTooLarge;
                mIDetection.checkFaceTooLarge(faceTooLarge);
            }
        }
        // 从人脸质量检测管理类中获取错误类型list
        faceInfoChecker(mFaceQualityManager.feedFrame(detectionFrame));
    }

    private int mFailFrame = 0;

    public void faceInfoChecker(List<FaceQualityErrorType> errorTypeList) {
        if (errorTypeList == null || errorTypeList.size() == 0)
            handleStart();
        else {
            String infoStr = "";
            FaceQualityErrorType errorType = errorTypeList.get(0);
            if (errorType == FaceQualityErrorType.FACE_NOT_FOUND) {
                infoStr = getString(R.string.face_not_found);
            } else if (errorType == FaceQualityErrorType.FACE_POS_DEVIATED) {
                infoStr = getString(R.string.face_not_found);
            } else if (errorType == FaceQualityErrorType.FACE_NONINTEGRITY) {
                infoStr = getString(R.string.face_not_found);
            } else if (errorType == FaceQualityErrorType.FACE_TOO_DARK) {
                infoStr = getString(R.string.face_too_dark);
            } else if (errorType == FaceQualityErrorType.FACE_TOO_BRIGHT) {
                infoStr = getString(R.string.face_too_bright);
            } else if (errorType == FaceQualityErrorType.FACE_TOO_SMALL) {
                infoStr = getString(R.string.face_too_small);
            } else if (errorType == FaceQualityErrorType.FACE_TOO_LARGE) {
                infoStr = getString(R.string.face_too_large);
            } else if (errorType == FaceQualityErrorType.FACE_TOO_BLURRY) {
                infoStr = getString(R.string.face_too_blurry);
            } else if (errorType == FaceQualityErrorType.FACE_OUT_OF_RECT) {
                infoStr = getString(R.string.face_out_of_rect);
            }

            if (mFailFrame > 10) {
                mFailFrame = 0;
                promptText.setText(infoStr);
            }
        }
    }

    /**
     * 跳转Activity传递信息
     */
    public void handleResult(final int resID, String delta, Map<String, byte[]> images) {
        String resultString = getResources().getString(resID);
        try {
            jsonObject.put("result", resultString);
            jsonObject.put("resultcode", resID);
        } catch (JSONException e) {
            e.printStackTrace();
        }
        Intent intent = new Intent();
        Bundle bundle = new Bundle();
        bundle.putString("result", jsonObject.toString());
        bundle.putString("delta", delta);
        bundle.putSerializable("images", (Serializable) images);
        intent.putExtras(bundle);
        setResult(RESULT_OK, intent);

        finish();
    }

    private int mCurStep = 0;// 检测动作的次数

    public void changeType(final Detector.DetectionType detectiontype, long timeout) {
        // 动画切换
        mIDetection.changeType(detectiontype, timeout);
        mFaceMask.setFaceInfo(null);

        // 语音播放
        if (mCurStep == 0) {
            mIMediaPlayer.doPlay(mIMediaPlayer.getSoundRes(detectiontype));
        } else {
            mIMediaPlayer.doPlay(R.raw.meglive_well_done);
            mIMediaPlayer.setOnCompletionListener(detectiontype);
        }
    }

    public void handleNotPass(final long remainTime) {
        if (remainTime > 0) {
            mainHandler.post(new Runnable() {
                @Override
                public void run() {
                    timeOutText.setText(remainTime / 1000 + "");
                    mCircleProgressBar.setProgress((int) (remainTime / 100));
                }
            });
        }
    }

    private boolean mHasSurface = false;

    /**
     * TextureView启动成功后 启动相机预览和添加活体检测回调
     * (TextureView.SurfaceTextureListener的接口回调方法)
     */
    @Override
    public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
        mHasSurface = true;
        doPreview();

        // 添加活体检测回调 (本Activity继承了DetectionListener)
        mDetector.setDetectionListener(this);
        // 添加相机预览回调(本Activity继承了PreviewCallback)
        mICamera.actionDetect(this);
    }

    @Override
    public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
    }

    /**
     * TextureView销毁后 (TextureView.SurfaceTextureListener的接口回调方法)
     */
    @Override
    public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
        mHasSurface = false;
        return false;
    }

    @Override
    public void onSurfaceTextureUpdated(SurfaceTexture surface) {
    }

    private void doPreview() {
        if (!mHasSurface)
            return;

        mICamera.startPreview(camerapreview.getSurfaceTexture());
    }

    @Override
    protected void onPause() {
        super.onPause();
        mainHandler.removeCallbacksAndMessages(null);
        mICamera.closeCamera();
        mIMediaPlayer.close();
        finish();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mDetector != null)
            mDetector.release();
        mDialogUtil.onDestory();
        mIDetection.onDestroy();
        sensorUtil.release();
        if (mHandlerThread != null) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
                mHandlerThread.quitSafely();
            } else {
                mHandlerThread.quit();
            }
        }

    }
}