package cn.tee3.manager.view;

import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.drawable.AnimationDrawable;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;

import cn.tee3.avd.AVDEngine;
import cn.tee3.avd.MVideo;
import cn.tee3.avd.Tlog;
import cn.tee3.avd.VideoRenderer;
import cn.tee3.manager.R;
import cn.tee3.manager.service.MeetingManager;
import cn.tee3.manager.util.RxSyncTask;
import cn.tee3.manager.util.StringUtils;
import tee3.webrtc.EglRenderer;
import tee3.webrtc.RendererCommon;
import tee3.webrtc.TextureViewRenderer;

import static tee3.webrtc.RendererCommon.ScalingType.SCALE_ASPECT_BALANCED;
import static tee3.webrtc.RendererCommon.ScalingType.SCALE_ASPECT_FILL;
import static tee3.webrtc.RendererCommon.ScalingType.SCALE_ASPECT_FIT;

public class T3VideoView extends FrameLayout {
    private static final String TAG = "T3VideoView";
    private final Context context;

//    public SurfaceViewRenderer glView;
    public TextureViewRenderer glView;
    private TextView tvVideoInfo;
    private ImageView background;
    private ImageView ivLoadVideoAnim;
    private VideoRenderer mRenderer;

    private int backgroundResId;
    private Bitmap backgroundBitmap;

    private MeetingManager.VideoParams videoParams = new MeetingManager.VideoParams("", "", "");

    private boolean isShowLoadAnim;
    private boolean isStartLoadAnim;
    private boolean isShowVideoInfo;
    private boolean setZOrderOnTop;
    private RendererCommon.ScalingType scalingType = SCALE_ASPECT_FILL;

    private AnimationDrawable animationDrawable;

    private FirstFrameComingListener firstFrameListener;
    private boolean isDispose;
    private boolean isUpdatedRender;

    private int screenWidth;
    private int screenHeight;
    private boolean enableMovement = false;
    private boolean enableTouchEvent = true;

    public T3VideoView(Context context) {
        this(context, null);
    }

    public T3VideoView(Context context, boolean enableZOrderOnTop) {
        super(context);
        this.context = context;
        setZOrderOnTop = enableZOrderOnTop;
        isShowLoadAnim = true;
        isStartLoadAnim = true;
        isShowVideoInfo = false;
        init(context);
    }

    public T3VideoView(Context context, RendererCommon.ScalingType scalingType, boolean setZOrderOnTop) {
        super(context);
        this.context = context;
        this.setZOrderOnTop = setZOrderOnTop;
        isShowLoadAnim = true;
        isStartLoadAnim = true;
        isShowVideoInfo = false;
        this.scalingType = scalingType;
        init(context);
    }

    public T3VideoView(Context context, RendererCommon.ScalingType scalingType, boolean setZOrderOnTop, boolean isShowLoadAnim, boolean isStartLoadAnim) {
        super(context);
        this.context = context;
        this.setZOrderOnTop = setZOrderOnTop;
        this.isShowLoadAnim = isShowLoadAnim;
        this.isStartLoadAnim = isStartLoadAnim;
        isShowVideoInfo = false;
        this.scalingType = scalingType;
        init(context);
    }

    public T3VideoView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public T3VideoView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.context = context;
        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.T3VideoView);
        isShowLoadAnim = array.getBoolean(R.styleable.T3VideoView_enableLoadAnim, true);
        isStartLoadAnim = array.getBoolean(R.styleable.T3VideoView_startLoadAnim, true);
        isShowVideoInfo = array.getBoolean(R.styleable.T3VideoView_showVideoInfo, false);
        setZOrderOnTop = array.getBoolean(R.styleable.T3VideoView_setZOrderOnTop, false);
        int type = array.getInt(R.styleable.T3VideoView_scalingType, 0);
        switch (type) {
            case 0:
                scalingType = SCALE_ASPECT_FILL;
                break;
            case 1:
                scalingType = SCALE_ASPECT_FIT;
                break;
            case 2:
                scalingType = SCALE_ASPECT_BALANCED;
                break;
        }
        array.recycle();
        init(context);
    }

    int width;
    int height;

    private void init(Context context) {
        View view = LayoutInflater.from(context).inflate(R.layout.t3video_view, this, true);
        glView = view.findViewById(R.id.gl_view);
//        glView.setZOrderOnTop(setZOrderOnTop);
//        glView.setZOrderMediaOverlay(setZOrderOnTop);

        Tlog.w(TAG, "init() -> T3VideoView: " + hashCode());
        background = view.findViewById(R.id.iv_bg_img);
        ivLoadVideoAnim = view.findViewById(R.id.iv_anim);
        tvVideoInfo = view.findViewById(R.id.tv_video_info);
        showVideoInfo(isShowVideoInfo);
        DisplayMetrics displayMetrics = getDisplayMetrics();
        screenWidth = displayMetrics.widthPixels;
        screenHeight = displayMetrics.heightPixels;

        updateRender();
        WindowManager wm = (WindowManager) getContext()
                .getSystemService(Context.WINDOW_SERVICE);

        width = wm.getDefaultDisplay().getWidth();
        height = wm.getDefaultDisplay().getHeight();

//        scaleGestureDetector = new ScaleGestureDetector(getContext(), new simpleScaleGestureListener());
        animationDrawable = (AnimationDrawable) ivLoadVideoAnim.getDrawable();
        if (isShowLoadAnim) {
            if (isStartLoadAnim) {
                animationDrawable.start();
                ivLoadVideoAnim.setVisibility(VISIBLE);
            } else {
                ivLoadVideoAnim.setVisibility(GONE);
            }
        } else {
            ivLoadVideoAnim.setVisibility(GONE);
        }
    }

    public void updateRender() {
        if (!AVDEngine.instance().isWorking()) {
            isUpdatedRender = false;
            Tlog.i(TAG, "updateRender() -> AVDEngine is not working!");
            return;
        }
        if (mRenderer != null) {
            Tlog.i(TAG, "updateRender() -> has disposed!");
            mRenderer.dispose();
        }
        mRenderer = new VideoRenderer(this.glView);
        mRenderer.setScalingType(scalingType);
        mRenderer.setAutoRotation(MVideo.isAutoRotation());

        Tlog.w(TAG, "updateRender() -> T3VideoView: " + hashCode() + ", render: " + mRenderer.hashCode() + ", glView: " + glView.hashCode());
        isDispose = false;
        isUpdatedRender = true;
    }

    public int setRenderZone(EglRenderer.RenderZone zone) {
        return mRenderer.setRenderZone(zone);
    }

    public boolean frameSampleCapture(EglRenderer.FrameListener listener) {
        if (mRenderer != null) {
            return mRenderer.frameSampleCapture(listener);
        }
        return false;
    }

    public boolean isUpdatedRender() {
        return isUpdatedRender;
    }

    public void dispose() {
        Tlog.w(TAG, "dispose() -> T3VideoView: " + hashCode() + ", render: " + (mRenderer == null ? "" : mRenderer.hashCode()) + ", glView: " + glView.hashCode());
        if (mRenderer != null) {
            mRenderer.dispose();
        }
        isDispose = true;
        isUpdatedRender = false;
    }

    public void startLodaAnim() {
        if (animationDrawable != null) {
            ivLoadVideoAnim.setVisibility(VISIBLE);
            animationDrawable.start();
        }
    }

    public void stopLoadAnim() {
        if (animationDrawable != null) {
            ivLoadVideoAnim.setVisibility(GONE);
            animationDrawable.stop();
        }
    }

    public boolean hasVideo() {
        return StringUtils.isNotEmpty(mRenderer == null ? null : mRenderer.getVideoId());
    }

    public boolean hasBindUser() {
        return StringUtils.isNotEmpty(videoParams.getUserId());
    }

    public boolean hasBindDevice() {
        return StringUtils.isNotEmpty(videoParams.getDeviceId());
    }

    public String getBindUserId() {
        return TextUtils.isEmpty(videoParams.getUserId()) ? "" : videoParams.getUserId();
    }

    public String getBindDeviceId() {
        return TextUtils.isEmpty(videoParams.getDeviceId()) ? "" : videoParams.getDeviceId();
    }

    public String getVideoId() {
        return mRenderer == null ? "" : (TextUtils.isEmpty(mRenderer.getVideoId()) ? "" : mRenderer.getVideoId());
    }

    public void setVideo(String deviceId) {
        mRenderer.setFirstFrameCallback(new VideoRenderer.FirstFrameCallback() {
            @Override
            public void onFirstFrameArrived(VideoRenderer render) {
                RxSyncTask.getInstance().
                        executeOnMainThread(new RxSyncTask.Action02() {
                            @Override
                            public void onTask() {
                                background.setVisibility(View.INVISIBLE);
                                ivLoadVideoAnim.setVisibility(View.INVISIBLE);
                                glView.setVisibility(VISIBLE);
                                animationDrawable.stop();
                                if (firstFrameListener != null) {
                                    firstFrameListener.onFirstFrameComingListener();
                                }
                            }
                        });
            }
        });

        if (!StringUtils.isNotEmpty(deviceId)) {
            this.background.setVisibility(View.VISIBLE);
        }

        if (/*hasVideo()*/StringUtils.isNotEmpty(deviceId)) {
            this.background.setVisibility(View.INVISIBLE);
            if (isShowLoadAnim) {
                this.ivLoadVideoAnim.setVisibility(View.VISIBLE);
                animationDrawable.start();
            }
        } else {
            mRenderer.fillBlack();
            glView.setVisibility(INVISIBLE);
            this.background.setVisibility(View.VISIBLE);
            if (isShowLoadAnim) {
                this.ivLoadVideoAnim.setVisibility(View.INVISIBLE);
            }
        }
    }

    public void showVideoInfo(boolean isshowvideoinfo) {
        if (isshowvideoinfo) {
            tvVideoInfo.setVisibility(VISIBLE);
        } else {
            tvVideoInfo.setVisibility(GONE);
        }
    }

    public void updateVideoInfo(String info) {
        tvVideoInfo.setText(info);
    }

    // TODO: 2020/12/16 ===
//    public void setZOrderOnTop(boolean onTop) {
//        this.glView.setZOrderOnTop(onTop);
//        this.glView.setZOrderMediaOverlay(true);
//    }

    public boolean getZOrderOnTop() {
        return setZOrderOnTop;
    }

    public VideoRenderer getRender() {
        return this.mRenderer;
    }

    public void setShowVideoInfo(boolean showVideoInfo) {
        isShowVideoInfo = showVideoInfo;
    }

    public ImageView getBgImageView() {
        return background;
    }

    public MeetingManager.VideoParams getVideoParams() {
        return videoParams;
    }

    public void setVideoParams(MeetingManager.VideoParams videoParams) {
        Tlog.i(TAG, "setVideoParams: " + videoParams.toString());
        this.videoParams.setUserId(TextUtils.isEmpty(videoParams.getUserId()) ? "" : videoParams.getUserId());
        this.videoParams.setUserName(TextUtils.isEmpty(videoParams.getUserName()) ? "" : videoParams.getUserName());
        this.videoParams.setDeviceId(TextUtils.isEmpty(videoParams.getDeviceId()) ? "" : videoParams.getDeviceId());
        this.videoParams.setDeviceType(videoParams.getDeviceType());
    }

    public void recoverVideoParams() {
        stopLoadAnim();
        this.videoParams = new MeetingManager.VideoParams("", "");
    }

    public void setImageResource(int bgId) {
        backgroundResId = bgId;
        this.background.setImageResource(bgId);
    }

    public Bitmap getImageBitmap() {
        background.setDrawingCacheEnabled(true);
        return background.getDrawingCache();
    }

    public void setImageBitmap(Bitmap bitmap) {
        backgroundBitmap = bitmap;
        background.setImageBitmap(bitmap);
    }

    public void recoverBackground() {
        if (backgroundResId != 0) {
            background.setImageResource(backgroundResId);
        } else if (backgroundBitmap != null) {
            background.setImageBitmap(backgroundBitmap);
        } else {
            background.setImageResource(R.color.videoView_bg);
        }
    }

    /**
     * 设置渲染模式：铺满、原尺寸、均衡
     * @param scalingType
     */
    public void setScalingType(RendererCommon.ScalingType scalingType) {
        this.scalingType = scalingType;
        if (mRenderer != null) {
            mRenderer.setScalingType(scalingType);
        }
    }

    /**
     * 获取设置的渲染模式
     */
    public RendererCommon.ScalingType getScalingType() {
        return scalingType;
    }

    @Override
    public void setVisibility(int visibility) {
        super.setVisibility(visibility);
        this.glView.setVisibility(visibility);
    }

    /***************************平移部分*****************************/
    float scale = 1.0f;    // 缩放比例的偏移量
    float preSpan;          // 上一次两触点距离
    // 获取当前触摸的绝对坐标
    float rawX = 0;
    float rawY = 0;
    // 上一次离开时的坐标
    float lastX = 0;
    float lastY = 0;
    // 当次的偏移量
    float offsetX;
    float offsetY;
    float sumOffsetX;
    float sumOffsetY;

    //平移的最小偏移量
    private float touchSlop = ViewConfiguration.getTouchSlop();
    private int mode = 0;
    private static final int MODE_DRAG = 1;
    private static final int MODE_ZOOM = 2;
    ScaleGestureDetector scaleGestureDetector = null;

    @Override
    public String toString() {
        return "T3VideoView: "+ hashCode() + ", Render: " + getRender() + " {" +
                "videoParams: "+ videoParams.hashCode() + " = " + videoParams +
                '}';
    }

    @TargetApi(17)
    private DisplayMetrics getDisplayMetrics() {
        DisplayMetrics displayMetrics = new DisplayMetrics();
        WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        windowManager.getDefaultDisplay().getRealMetrics(displayMetrics);
        return displayMetrics;
    }

    public void setEnableMovement(boolean enableMovement) {
        this.enableMovement = enableMovement;
    }

    public void setEnableTouchEvent(boolean enableTouchEvent) {
        this.enableTouchEvent = enableTouchEvent;
    }

    @Override
    protected void onDetachedFromWindow() {
        dispose();
        super.onDetachedFromWindow();
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (!enableTouchEvent) {
            return false;
        }
        // 获取当前触摸的绝对坐标
        rawX = event.getRawX();
        rawY = event.getRawY();

        switch (event.getAction() & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_DOWN:
                mode = MODE_DRAG;
                break;
            case MotionEvent.ACTION_POINTER_DOWN:
                if (event.getPointerCount() >= 2) {
                    mode = MODE_ZOOM;
                }
                break;
            case MotionEvent.ACTION_MOVE:
                if (enableMovement && mode == MODE_DRAG && scale > 1.0f) {
                    // 当次的偏移量
                    offsetX = 0.00f;
                    offsetY = 0.00f;
                    // 两次的偏移量
                    if (Math.abs(rawX - lastX) >= touchSlop) {
                        offsetX = rawX - lastX;
                    }
                    if (Math.abs(rawY - lastY) >= touchSlop) {
                        offsetY = rawY - lastY;
                    }

                    int widthBound = (int) ((scale - 1) * screenWidth / 2);
                    int heightBound = (int) ((scale - 1) * screenHeight / 2);

                    if (Math.abs(sumOffsetX + offsetX) > widthBound) {
                        offsetX = sumOffsetX / Math.abs(sumOffsetX) * (widthBound - Math.abs(sumOffsetX));
                        sumOffsetX = sumOffsetX / Math.abs(sumOffsetX) * widthBound;
                    } else {
                        sumOffsetX += offsetX;
                    }
                    if (Math.abs(sumOffsetY + offsetY) > heightBound) {
                        offsetY = sumOffsetY / Math.abs(sumOffsetY) * (heightBound - Math.abs(sumOffsetY));
                        sumOffsetY = sumOffsetY / Math.abs(sumOffsetY) * heightBound;
                    } else {
                        sumOffsetY += offsetY;
                    }

                    if (offsetX != 0 || offsetY != 0) {
                        mRenderer.setOffset(offsetX, offsetY);
                    }
                }
                break;
            case MotionEvent.ACTION_UP:
                mode = 0;
                break;
            case MotionEvent.ACTION_POINTER_UP:
                mode = 0;
                break;
            default:
                break;
        }

        lastX = rawX;
        lastY = rawY;

        if (enableMovement) {
            return scaleGestureDetector.onTouchEvent(event);
        } else {
            return super.onTouchEvent(event);
        }
    }

    /***************************平移部分*****************************/

    private final static int SCALE_FACTOR = 600;
    private final static float SCALE_MAX = 3.0f;
    private final static float SCALE_MIN = 1.0f;
    /***************************缩放部分*****************************/
    private class simpleScaleGestureListener implements ScaleGestureDetector.OnScaleGestureListener {
        @Override
        public boolean onScale(ScaleGestureDetector detector) {
            if (mode == MODE_ZOOM && enableMovement) {
                float curSpan = detector.getCurrentSpan();  // 双指间距
                scale = scale + (curSpan - preSpan) / SCALE_FACTOR;

                if (scale < SCALE_MIN) {
                    scale = SCALE_MIN;
                } else if (scale > SCALE_MAX) {
                    scale = SCALE_MAX;
                }
                preSpan = curSpan;
//                mRenderer.setScale(scale);

                setPivotX(getWidth() >> 1);
                setPivotY(getHeight() >> 1);
                setScaleX(scale);
                setScaleY(scale);
            }
            return false;
        }

        @Override
        public boolean onScaleBegin(ScaleGestureDetector detector) {
            if (enableMovement) {
                mRenderer.setOffset(-sumOffsetX, -sumOffsetY);
                sumOffsetX = 0;
                sumOffsetY = 0;
                preSpan = detector.getPreviousSpan();
            }
            return false;
        }

        @Override
        public void onScaleEnd(ScaleGestureDetector detector) {

        }
    }

    /***************************缩放部分*****************************/

    public void addFirstFrameComingListener(FirstFrameComingListener listener) {
        firstFrameListener = listener;
    }

    public interface FirstFrameComingListener {
        void onFirstFrameComingListener();
    }
}
