package cn.tee3.avd;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Handler;
import android.os.Looper;
import android.util.AttributeSet;
import android.util.SparseArray;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;

import com.google.gson.Gson;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;

import cn.tee3.R;

public class WhiteboardDrawView extends View {
    private static final String TAG = "WhiteboardDrawView";
    private String userId = "";
    private String boardId = "";
    private Context context;
    private Paint mPaint;
    private Bitmap mBufferBitmap;
    private Canvas mBufferCanvas;

    private IUploadAnnotationInternalListener upLoadAnnotationCallback;

    SparseArray<ToolType> annotation2ToolType = new SparseArray<>();

    private LinkedHashMap<String, DrawViewBean> drawViewBeanMap = new LinkedHashMap<>();
    private List<String> localDrawViewBeanList = new ArrayList<>();

    public boolean isDrawToolViewVisible = true;

    private MWhiteboard.Whiteboard whiteboard;
    private WhiteboardView whiteboardView;

    private int arrowSize = 30;
    private int calWidth;
    private int calHeight;
    private int curWidth;
    private int curHeight;

    private int orientation = -1;
    private boolean isOrientationChange = false;
    private boolean reachBorder;
    private String laserPenId;
    private int bgColor;
    private int boardBackgroundColor = Color.TRANSPARENT;
    private boolean isLocalWhiteboard = true;
    private LinkedList<String> annotationList = new LinkedList<>();
    private WhiteboardView.DrawType drawType = WhiteboardView.DrawType.FOLLOW_SETTING_SIZE;

    /*************************************************************************************************************************************************/

    public enum ToolType {
        MOUSE(1),
        LINE(1 << 1),
        RECTANGLE(1 << 2),
        ELLIPSE(1 << 3),            /*椭圆*/
        POLY_LINE(1 << 4),
        POLYGON(1 << 5),            /*多边形*/
        LASER_PEN(1 << 6),           /*激光笔*/
        TEXT(1 << 7),
        RUBBER_LINE(1 << 8),
        RUBBER_RECTANGLE(1 << 9),
        RHOMB(1 << 10),             /*菱形*/
        ARROW(1 << 11),
        SUCCESS(1 << 12),
        FAILURE(1 << 13),
        FLUORESCENT_PEN(1 << 4),    /*荧光笔*/
        LOCAL_ARROW(1 << 1);        /*本地画的单箭头，类型也是使用直线的类型*/

        int code;

        ToolType(int code) {
            this.code = code;
        }

        public int getCode() {
            return code;
        }
    }

    private ToolType toolType;
    private int lineColor;
    private int lineWidth;

    private GestureDetector gestureDetector;
    private MyGestureDetectorListener myGestureDetectorListener;

    int x;
    int y;
    DrawViewBean drawViewBean;

    private IWhiteboardTouchEventNotify touchEventNotify;

    public class MyGestureDetectorListener extends GestureDetector.SimpleOnGestureListener {
        public MyGestureDetectorListener() {
            super();
        }

        @Override
        /** 单击确认，这里才是真正单击触发的地方 */
        public boolean onSingleTapConfirmed(MotionEvent e) {
            if (touchEventNotify != null) {
                touchEventNotify.onSingleClick();
            }
            return false;
        }
    }

    /*************************************************************************************************************************************************/
    public WhiteboardDrawView(Context context) {
        super(context);
        this.context = context;
        init();
    }

    public WhiteboardDrawView(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.context = context;
        init();
    }

    public WhiteboardDrawView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    public WhiteboardDrawView(Context context, WhiteboardView whiteboardView, MWhiteboard.Whiteboard whiteboard, LinkedList<String> annotationList, boolean isLocalWhiteboard) {
        super(context);
        this.context = context;
        init();
        if (!boardId.equals(whiteboard._boardId)) {
            this.whiteboardView = whiteboardView;
            this.whiteboard = whiteboard;
            boardId = whiteboard._boardId;
            this.isLocalWhiteboard = isLocalWhiteboard;
            drawViewBeanMap.clear();
            localDrawViewBeanList.clear();
            this.annotationList = annotationList;
//            requestLayout();
        }
        refreshView();
    }

    public void setDrawToolViewVisible(boolean isVisible) {
        isDrawToolViewVisible = isVisible;
        if (touchEventNotify != null) {
            if (isDrawToolViewVisible) {
                touchEventNotify.onStartZoomOperation();
            } else {
                if (scale == 1.0f) {
                    touchEventNotify.onStopZoomOperation();
                } else {
                    touchEventNotify.onStartZoomOperation();
                }
            }
        }
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }

    private void init() {
        setDrawingCacheEnabled(true);
//        setBackgroundColor(boardBackgroundColor);
        lineColor = getResources().getColor(R.color.cn_tee3_red);
        mPaint = getPaint(MWhiteboard.DisplayDimension.sp2px(context, 3), lineColor);

        toolType = ToolType.POLY_LINE;  // 默认设为曲线
        lineWidth = 3;

        initAnnotation2ToolType();
        myGestureDetectorListener = new MyGestureDetectorListener();
        gestureDetector = new GestureDetector(context, myGestureDetectorListener);
    }

    private MAnnotationInternal.AnnotationStr.MPoint[] pointsRemote2Local(MAnnotationInternal.AnnotationStr.MPoint[] points) {
        MAnnotationInternal.AnnotationStr.MPoint[] mPoints = new MAnnotationInternal.AnnotationStr.MPoint[points.length];
        for (int i = 0; i < points.length; i++) {
            mPoints[i] = new MAnnotationInternal.AnnotationStr.MPoint();
            mPoints[i].setX((int) (points[i].getX() / factorZoomX));
            mPoints[i].setY((int) (points[i].getY() / factorZoomY));
//            Log.d("qqq", "x1= " + points[i].getX() + ", y= " + points[i].getY() + "; x2= " + mPoints[i].getX() + ", y2= " + mPoints[i].getY());
        }
        return mPoints;
    }

    private MAnnotationInternal.AnnotationStr.MPoint[] pointsLocal2Remote(MAnnotationInternal.AnnotationStr.MPoint[] points) {
        MAnnotationInternal.AnnotationStr.MPoint[] mPoints = new MAnnotationInternal.AnnotationStr.MPoint[points.length];
        for (int i = 0; i < points.length; i++) {
            mPoints[i] = new MAnnotationInternal.AnnotationStr.MPoint();
            mPoints[i].setX((int) (points[i].getX() * factorZoomX));
            mPoints[i].setY((int) (points[i].getY() * factorZoomY));
        }
        return mPoints;
    }

    public void setParams(WhiteboardView whiteboardView, MWhiteboard.Whiteboard whiteboard, LinkedList<String> annotationList, boolean isLocalWhiteboard) {
        if (!boardId.equals(whiteboard._boardId)) {
            this.whiteboardView = whiteboardView;
            this.whiteboard = whiteboard;
            boardId = whiteboard._boardId;
            this.isLocalWhiteboard = isLocalWhiteboard;
            drawViewBeanMap.clear();
            localDrawViewBeanList.clear();
            this.annotationList = annotationList;
            requestLayout();
        }
        refreshView();
    }

    protected void setDrawType(WhiteboardView.DrawType type) {
        drawType = type;
    }

    public void updateAnnotation(LinkedList<String> annotationList) {
        drawViewBeanMap.clear();
        localDrawViewBeanList.clear();
        for (String annotationStr : annotationList) {
            DrawViewBean bean = new DrawViewBean(annotationStr);
            if (bean.annotation.points == null || bean.annotation.points.length == 0) {
                continue;
            }
            MAnnotationInternal.AnnotationStr.MPoint[] points = pointsRemote2Local(bean.annotation.points);
            bean.annotation.setPoints(points);
            drawViewBeanMap.put(bean.getId(), bean);
            if (userId.equals(bean.getAnnotation().getAnnotationbase().user_id_)) {
                localDrawViewBeanList.add(bean.getId());
            }
        }
        this.bgColor = Color.argb(whiteboard._a, whiteboard._r, whiteboard._g, whiteboard._b); //白板背景颜色
        refreshView();
    }

    public void reConnect() {
        if (upLoadAnnotationCallback != null) {
            for (String id : localDrawViewBeanList) {
                upLoadAnnotationCallback.onUploadAddAnnotation(boardId, id);
            }
        }
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        if (mBufferBitmap != null && !mBufferBitmap.isRecycled()) {
            mBufferBitmap.recycle();
            mBufferBitmap = null;
            Tlog.i(TAG, "drawView -> onRecycle() -> mBufferBitmap.recycle()");
        }
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
    }

    private float factor;
    private float factorZoomX = 1.0f;
    private float factorZoomY = 1.0f;

    private void initBuffer(int width, int height) {
        Tlog.i(TAG, "initBuffer, width=" + width + ", height=" + height);
        if (mBufferBitmap != null && !mBufferBitmap.isRecycled()) {
            mBufferBitmap.recycle();
            mBufferBitmap = null;
        }
        mBufferBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        mBufferCanvas = new Canvas(mBufferBitmap);
        new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
            @Override
            public void run() {
                if (mBufferBitmap != null) {
                    requestLayout();
                }
            }
        }, 100);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        if (whiteboardView == null) {
            setMeasuredDimension(0, 0);
            return;
        }
        int w2 = whiteboardView.getWidth();
        int h2 = whiteboardView.getHeight();

        float factorX = 1.0f;
        float factorY = 1.0f;

        if (whiteboard == null) {
            factorX = 1.0f;
            factorY = 1.0f;
            calWidth = w2;
            calHeight = h2;
        } else {
            int w1 = whiteboard._width;
            int h1 = whiteboard._height;
            switch (drawType) {
                case FOLLOW_SETTING_SIZE:
                    calWidth = w2;
                    calHeight = h2;
                    factorX = (float) w1 / (float) w2;
                    factorY = (float) h1 / (float) h2;
                    break;
                case KEEP_WIDTH_HEIGHT_RATIO:
                    factor = (float) w1 / (float) h1;

                    if (w1 * h2 > h1 * w2) {
                        calWidth = w2;
                        calHeight = (int) (w2 / factor);
                    } else if (w1 * h2 < h1 * w2) {
                        calHeight = h2;
                        calWidth = (int) (h2 * factor);
                    } else if (w1 * h2 == h1 * w2) {
                        calWidth = w2;
                        calHeight = h2;
                    }
                    factorX = (float) w1 / (float) calWidth;
                    factorY = (float) h1 / (float) calHeight;
                    break;
            }
//            Log.d("qqq", "\n \n w2 = " + w2 + ", h2 = " + h2 + "; w1 = " + w1 + ", h1 = " + h1 + "; zoomX = " + factorZoomX + ", zoomY = " + factorZoomY + ", factor = " + factor + "    ================================================");
        }

        if (calWidth != curWidth || calHeight != curHeight || mBufferBitmap == null) {
            curWidth = calWidth;
            curHeight = calHeight;
            setMeasuredDimension(calWidth, calHeight);

            isOrientationChange = !(orientation == context.getResources().getConfiguration().orientation);
            if (isOrientationChange) {
                orientation = context.getResources().getConfiguration().orientation;
                scale = 1.0f;
                sumX = 0;
                sumY = 0;
            }

            if (calWidth != 0 && calHeight != 0) {
                initBuffer(calWidth, calHeight);
            }
        }

        if (annotationList.size() > 0) {
            factorZoomX = factorX;
            factorZoomY = factorY;
            for (String annotationStr : annotationList) {
                DrawViewBean bean = new DrawViewBean(annotationStr);
                if (bean.annotation.points == null || bean.annotation.points.length == 0) {
                    continue;
                }
                MAnnotationInternal.AnnotationStr.MPoint[] points = pointsRemote2Local(bean.annotation.points);
                bean.annotation.setPoints(points);
                drawViewBeanMap.put(bean.getId(), bean);

                if (userId.equals(bean.getAnnotation().getAnnotationbase().user_id_)) {
                    localDrawViewBeanList.add(bean.getId());
                }
            }
            annotationList.clear();
            this.bgColor = Color.argb(whiteboard._a, whiteboard._r, whiteboard._g, whiteboard._b); //白板背景颜色
            refreshView();
        } else if ((factorZoomX != factorX || factorZoomY != factorY) && whiteboardView != null) {
            LinkedList<String> lines = whiteboardView.getLines();
            factorZoomX = factorX;
            factorZoomY = factorY;
            drawViewBeanMap.clear();
            localDrawViewBeanList.clear();
            for (String line : lines) {
                DrawViewBean bean = new DrawViewBean(line);
                if (bean.annotation.points == null || bean.annotation.points.length == 0) {
                    continue;
                }
                MAnnotationInternal.AnnotationStr.MPoint[] points = pointsRemote2Local(bean.annotation.points);
                bean.annotation.setPoints(points);
                drawViewBeanMap.put(bean.getId(), bean);
                if (userId.equals(bean.getAnnotation().getAnnotationbase().user_id_)) {
                    localDrawViewBeanList.add(bean.getId());
                }
            }
            refreshView();
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (mBufferBitmap != null && !mBufferBitmap.isRecycled()) {
            if (p1 != null && p2 != null) {
                scalePoint.setX((p1.getX() + p2.getX()) / 2);
                scalePoint.setY((p2.getY() + p2.getY()) / 2);
                ViewGroup.LayoutParams layoutParams = getLayoutParams();
                layoutParams.width = (int) (calWidth * scale);
                layoutParams.height = (int) (calHeight * scale);
                setLayoutParams(layoutParams);
                canvas.scale(scale, scale, scalePoint.getX(), scalePoint.getY());
            }

            if (sumX != 0 && sumY != 0) {
                canvas.translate(sumX, sumY);
            }
            if (drawViewBean != null && !drawViewBeanMap.containsKey(drawViewBean.getId())) {
                canvas.drawPath(drawViewBean.getmPath(), drawViewBean.getmPaint());
            }
            canvas.drawBitmap(mBufferBitmap, 0, 0, null);
        } else {
            canvas.drawColor(boardBackgroundColor);
        }
    }

    public int getOrientation() {
        return orientation;
    }

    public void setOrientation(int orientation) {
        this.orientation = orientation;
    }

    public void setTouchEventNotify(IWhiteboardTouchEventNotify notify) {
        touchEventNotify = notify;
    }

    public interface IWhiteboardTouchEventNotify {
        void onStartZoomOperation();

        void onStopZoomOperation();

        void onSingleClick();
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int action = event.getActionMasked();
        if (isDrawToolViewVisible) {
            x = (int) ((event.getX() + scalePoint.getX() * (scale - 1)) / scale - sumX);
            y = (int) ((event.getY() + scalePoint.getY() * (scale - 1)) / scale - sumY);
            switch (toolType) {
                case RUBBER_LINE:
                    onTouchEventRubber(action);
                    break;
                case LASER_PEN:
                    onTouchEventLaserPen(action);
                    break;
                default:
                    onTouchEventLine(action);
                    break;
            }
            invalidate();
        } else {
            onTouchScaleAndTranslate(event);
            gestureDetector.onTouchEvent(event);
        }
//        Log.i(TAG, "onTouch -> drawViewBeanMap.size=" + drawViewBeanMap.size() + ", localDrawViewBeanList.size=" + localDrawViewBeanList.size());
        return true;
    }

    private int getPointsDistance(MAnnotationInternal.AnnotationStr.MPoint p1, MAnnotationInternal.AnnotationStr.MPoint p2) {
        int dx = Math.abs(p1.getX() - p2.getX());
        int dy = Math.abs(p1.getY() - p2.getY());
        return (int) Math.sqrt((double) dx * (double) dx + (double) dy * (double) dy);
    }

    private int pointsNum;
    private int distanceOrg = -1, curDistance;
    private boolean isScaleAndTranslate;

    private int points = 10000;

    private MAnnotationInternal.AnnotationStr.MPoint p1 = new MAnnotationInternal.AnnotationStr.MPoint();
    private MAnnotationInternal.AnnotationStr.MPoint p2 = new MAnnotationInternal.AnnotationStr.MPoint();

    private static float scale = 1.0f;
    private int cx, cy, lastX, lastY, sumX, sumY;

    private MAnnotationInternal.AnnotationStr.MPoint scalePoint = new MAnnotationInternal.AnnotationStr.MPoint();

    private enum ScaleOrTranslate {
        IDLE,
        TRANSLATE,
        SCALE,
    }

    private ScaleOrTranslate scaleOrTranslate = ScaleOrTranslate.IDLE;

    public static float getScale() {
        return scale;
    }

    private boolean hasSendStartZoomNotify, isScaleOperation;

    private void onTouchScaleAndTranslate(MotionEvent event) {
        int index = event.getActionIndex();
        cx = (int) event.getX();
        cy = (int) event.getY();

        switch (event.getActionMasked()) {
            case MotionEvent.ACTION_DOWN:
                points = 1;
                p1 = new MAnnotationInternal.AnnotationStr.MPoint((int) event.getX(), (int) event.getY());
                scaleOrTranslate = ScaleOrTranslate.TRANSLATE;
                lastX = cx;
                lastY = cy;
                break;
            case MotionEvent.ACTION_MOVE:
                switch (event.getPointerCount()) {
                    case 1:
                        if (scale != 1.0f && !isScaleOperation) {
                            sumX += (cx - lastX) * 0.6f;
                            sumY += (cy - lastY) * 0.6f;
                            lastX = cx;
                            lastY = cy;
                            invalidate();
                        }
                        break;
                    case 2:
                        if (event.getPointerCount() == 2) {
                            isScaleOperation = true;
                            isScaleAndTranslate = true;
                            int indexP1 = event.findPointerIndex(0);
                            int indexP2 = event.findPointerIndex(1);

                            MAnnotationInternal.AnnotationStr.MPoint p_1 = new MAnnotationInternal.AnnotationStr.MPoint((int) event.getX(indexP1), (int) event.getY(indexP1));
                            MAnnotationInternal.AnnotationStr.MPoint p_2 = new MAnnotationInternal.AnnotationStr.MPoint((int) event.getX(indexP2), (int) event.getY(indexP2));

                            curDistance = getPointsDistance(p_1, p_2);
                            if (distanceOrg == -1) {
                                distanceOrg = curDistance;
                            }
                            float lastScale = scale;
                            scale = scale + ((float) curDistance - (float) distanceOrg) / 600;
                            distanceOrg = curDistance;
                            if (scale <= 1) {
                                sumX = 0;
                                sumY = 0;
                                scale = 1;
                            } else if (scale > 3) {
                                scale = 3;
                            }
                            if (lastScale != scale) {
                                invalidate();
                            }

                            if (touchEventNotify != null) {
                                if (scale == 1) {
                                    hasSendStartZoomNotify = false;
                                    touchEventNotify.onStopZoomOperation();
                                } else if (!hasSendStartZoomNotify) {
                                    hasSendStartZoomNotify = true;
                                    touchEventNotify.onStartZoomOperation();
                                }
                            }
                        }
                        break;
                }
                break;
            case MotionEvent.ACTION_POINTER_DOWN:
                int pointerId = event.getPointerId(index);
                switch (pointerId) {
                    case 1:
                        p2 = new MAnnotationInternal.AnnotationStr.MPoint((int) event.getX(), (int) event.getY());
                        points = 2;
                        distanceOrg = -1;
                        scaleOrTranslate = ScaleOrTranslate.SCALE;
                        break;
                    default:
                        points = 10000;
                        scaleOrTranslate = ScaleOrTranslate.IDLE;
                        break;
                }
                break;
            case MotionEvent.ACTION_UP:
                if (isScaleOperation) {
                    isScaleOperation = false;
                }
                isScaleAndTranslate = false;
                scaleOrTranslate = ScaleOrTranslate.IDLE;
                points = 10000;
                break;
        }
    }

    private boolean rubberIsBeasy = false;

    public void onTouchEventRubber(int action) {
        if (rubberIsBeasy) {
            return;
        }
        rubberIsBeasy = true;
        boolean checkOver = false;
        if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_MOVE || action == MotionEvent.ACTION_UP) {
            for (String key : localDrawViewBeanList) {
                DrawViewBean bean = drawViewBeanMap.get(key);
                if (bean != null) {
                    MAnnotationInternal.AnnotationStr.MPoint[] points = bean.getAnnotation().getPoints();
                    if (points == null) {
                        drawViewBeanMap.remove(bean.id);
                        localDrawViewBeanList.remove(bean.id);
                        if (upLoadAnnotationCallback != null) {
                            upLoadAnnotationCallback.onUploadRemoveAnnotation(boardId, bean.getId());
                        }
                        return;
                    }
                    switch (getToolTypeByCode(bean.getAnnotation().getAnnotationbase().getTool_type_())) {
                        case MOUSE:
                            break;
                        case LINE:  /** 小于1400就可以说明在线附近了 */
                            float distance = distancePoint2Line(new MAnnotationInternal.AnnotationStr.MPoint(x, y), points[0], points[1]);
                            if (distance < 1400) {
                                checkOver = true;
                            }
                            break;
                        case POLY_LINE:
                        case FLUORESCENT_PEN:
                            for (MAnnotationInternal.AnnotationStr.MPoint point : points) {
                                double sqrt = Math.sqrt(Math.pow(Math.abs(point.getX() - x), 2) + Math.pow(Math.abs(point.getY() - y), 2));
                                if (sqrt < 60) {
                                    checkOver = true;
                                    break;
                                }
                            }
                            break;
                    }
                } else {
                    checkOver = true;
                }

                if (checkOver) {
                    drawViewBeanMap.remove(key);
                    localDrawViewBeanList.remove(key);
                    refreshView();
                    if (upLoadAnnotationCallback != null) {
                        upLoadAnnotationCallback.onUploadRemoveAnnotation(boardId, key);
                    }
                    break;
                }
            }
        }
        rubberIsBeasy = false;
    }

    private DrawViewBean laserPen;

    public void onTouchEventLaserPen(int action) {
        if (action == MotionEvent.ACTION_MOVE) {
            if (laserPen == null) {
                laserPen = new DrawViewBean(mPaint, createAnnotation());
            } else {
                laserPen.getPointList().clear();
                laserPen.getAnnotation().setPoints(null);
            }
            laserPen.addPoint(x, y);
            laserPen.addPoint(x + 200, y + 200);
            uploadAnnotation(laserPen);
            drawViewBeanMap.remove(laserPenId);
            refreshView();
            drawLaserPen(laserPen);
        }
    }

    /**
     * 画 曲线、荧光笔线、箭头
     */
    public void onTouchEventLine(int action) {
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                reachBorder = false;
                drawViewBean = null;
                drawViewBean = new DrawViewBean(mPaint, createAnnotation());
                drawViewBean.pathMoveTo(x, y);
                break;
            case MotionEvent.ACTION_MOVE:
                if (reachBorder) {
                    break;
                }
                if (x < 0 || y < 0) {
                    reachBorder = true;
                }
                x = Math.max(x, 0);
                y = Math.max(y, 0);
                localDrawView(drawViewBean);
                break;
            case MotionEvent.ACTION_UP:
                reachBorder = false;
                drawViewBeanMap.put(drawViewBean.getId(), drawViewBean);
                localDrawViewBeanList.add(drawViewBean.getId());
                if (drawViewBean != null) {
                    mBufferCanvas.drawPath(drawViewBean.getmPath(), drawViewBean.getmPaint());
                }
                uploadAnnotation(drawViewBean);
                drawViewBean = null;
                break;
        }
    }

    private MAnnotationInternal.AnnotationStr createAnnotation() {
        if (userId == null || userId.equals("")) {
            return null;
        }
        String annotation_id_ = UUID.randomUUID().toString();
        String user_id_ = userId;
        int line_width_ = lineWidth;

        int line_arrow_ = 1;    // 1->代表曲线和直线，4->单箭头，6->双箭头
        switch (toolType) {
            case FLUORESCENT_PEN:
            case POLY_LINE:
                line_arrow_ = 1;
                break;
            case LOCAL_ARROW:   /** 本地画箭头 */
                line_arrow_ = 4;
                break;
        }

        String object_id_ = "";
        int tool_type_ = getCodeByToolType(toolType);     //

        MAnnotationInternal.AnnotationStr.Annotationbase.Rect rect_1 = new MAnnotationInternal.AnnotationStr.Annotationbase.Rect(0, 0, 0, 0);
        MAnnotationInternal.AnnotationStr.Annotationbase.Rect rect_2 = new MAnnotationInternal.AnnotationStr.Annotationbase.Rect(0, 0, 0, 0);
        MAnnotationInternal.AnnotationStr.Annotationbase.Rect rect_3 = new MAnnotationInternal.AnnotationStr.Annotationbase.Rect(0, 0, 0, 0);
        MAnnotationInternal.AnnotationStr.Annotationbase.MColor fill_color_ = new MAnnotationInternal.AnnotationStr.Annotationbase.MColor(0, 255, 255, 255); // 圆和矩形的填充颜色

        MAnnotationInternal.AnnotationStr.Annotationbase.MColor line_color_ =
                new MAnnotationInternal.AnnotationStr.Annotationbase.MColor(Color.alpha(lineColor), Color.red(lineColor), Color.green(lineColor), Color.blue(lineColor));

        MAnnotationInternal.AnnotationStr.Annotationbase annotationbase = new MAnnotationInternal.AnnotationStr.
                Annotationbase(annotation_id_, user_id_, line_width_, line_arrow_, object_id_, tool_type_, rect_1, rect_2, rect_3, fill_color_, line_color_);

        MAnnotationInternal.AnnotationStr.MPoint[] points = null;
        return new MAnnotationInternal.AnnotationStr(annotationbase, points);
    }

    /**
     * 上传画线的动作 “描述”
     *
     * @param drawViewBean
     */
    private void uploadAnnotation(DrawViewBean drawViewBean) {
        if (upLoadAnnotationCallback != null) {
            boolean isValid = drawViewBean.preparePoints();
            if (isValid) {
                drawViewBean.annotation.points = pointsLocal2Remote(drawViewBean.annotation.points);
                upLoadAnnotationCallback.onUploadAddAnnotation(boardId, new Gson().toJson(drawViewBean.annotation));   /** 上传画好的线条注释 pppppppppppppppp*/
                drawViewBean.annotation.points = pointsRemote2Local(drawViewBean.annotation.points);
            } else {
                drawViewBeanMap.remove(drawViewBean.id);
                localDrawViewBeanList.remove(drawViewBean.id);
//                localBeanMap.remove(drawViewBean.id);
            }
//            Log.w(TAG, new Gson().toJson(drawViewBean.annotation));
        }
    }

    private void localDrawView(DrawViewBean drawViewBean) {
        switch (toolType) {
            case POLY_LINE:
            case FLUORESCENT_PEN:
                drawViewBean.pathQuadTo(x, y);
                break;
            case LOCAL_ARROW:
                drawViewBean.mPath = getPath4SingleArrow(drawViewBean.mPath, drawViewBean.pointList.get(0).getX(), drawViewBean.pointList.get(0).getY(), x, y);
                drawViewBean.addPoint(x, y);
                break;
        }
    }

    public void setToolType(WhiteboardToolView.DrawToolbarObj bean) {
        if (toolType == ToolType.LASER_PEN && bean.getToolType() != ToolType.LASER_PEN && upLoadAnnotationCallback != null && laserPen != null) {
            refreshView();
            upLoadAnnotationCallback.onUploadRemoveAnnotation(boardId, laserPen.getId());
        }
        toolType = bean.getToolType();
        lineWidth = bean.getLineWidth();
        lineColor = bean.getLineColor();
        mPaint.setStrokeWidth(MWhiteboard.DisplayDimension.sp2px(context, lineWidth));
        mPaint.setColor(lineColor);
    }

    public ToolType getToolType() {
        return toolType;
    }

    private Paint getPaint(int strokeWidth, int lineColor, int fillColor) {
        Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);    // Paint.ANTI_ALIAS_FLAG 是使位图抗锯齿的标志; Paint.DITHER_FLAG 是使位图进行有利的抖动的位掩码标志 ; Paint.FILTER_BITMAP_FLAG 是使位图过滤的位掩码标志,对位图进行滤波处理
        if (Color.alpha(fillColor) != 0) {
            mPaint.setStyle(Paint.Style.FILL_AND_STROKE);   // 描边和填充
            mPaint.setColor(fillColor);
        } else {
            mPaint.setStyle(Paint.Style.STROKE);    // 描边
            mPaint.setColor(lineColor);
        }
//        mPaint.setFilterBitmap(true);   // 1)mPaint.setAntiAlias(); 2)mPaint.setBitmapFilter(true)。第一个函数是用来防止边缘的锯齿，第二个函数是用来对位图进行滤波处理。
//        mPaint.setAntiAlias(true);
        mPaint.setStrokeJoin(Paint.Join.ROUND); // 设置线段连接处样式，取值有：Join.MITER（结合处为锐角）、Join.Round(结合处为圆弧)、Join.BEVEL(结合处为直线)
        mPaint.setStrokeCap(Paint.Cap.ROUND);   // 设置线冒样式，取值有Cap.ROUND(圆形线冒)、Cap.SQUARE(方形线冒)、Paint.Cap.BUTT(无线冒)
//        mXferModeDraw = new PorterDuffXfermode(PorterDuff.Mode.SRC);    //todo 在目标图像上层绘制源图像, 注：会屏蔽掉半透明的颜色
//        mPaint.setXfermode(mXferModeDraw);
        mPaint.setStrokeWidth(strokeWidth);
        return mPaint;
    }

    private Paint getPaint(int strokeWidth, int lineColor) {
        Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);    // Paint.ANTI_ALIAS_FLAG 是使位图抗锯齿的标志; Paint.DITHER_FLAG 是使位图进行有利的抖动的位掩码标志 ; Paint.FILTER_BITMAP_FLAG 是使位图过滤的位掩码标志,对位图进行滤波处理
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setColor(lineColor);
        mPaint.setStrokeJoin(Paint.Join.ROUND); // 设置线段连接处样式，取值有：Join.MITER（结合处为锐角）、Join.Round(结合处为圆弧)、Join.BEVEL(结合处为直线)
        mPaint.setStrokeCap(Paint.Cap.ROUND);   // 设置线冒样式，取值有Cap.ROUND(圆形线冒)、Cap.SQUARE(方形线冒)、Paint.Cap.BUTT(无线冒)
        mPaint.setStrokeWidth(strokeWidth);
        return mPaint;
    }
    public class DrawViewBean {
        private String id;
        private Path mPath;
        private Paint mPaint;
        float mLastX;
        float mLastY;
        private MAnnotationInternal.AnnotationStr annotation;
        public List<MAnnotationInternal.AnnotationStr.MPoint> pointList = new ArrayList<>();

        public DrawViewBean(Paint mPaint, MAnnotationInternal.AnnotationStr annotation) {     /** 本地画图时使用的构造方法 */
            this.mPath = new Path();
            this.mPaint = new Paint(mPaint);
            this.annotation = annotation;
            id = annotation.getAnnotationbase().getAnnotation_id_();
        }

        public DrawViewBean(String annotationStr) {    /** 监听里接收到的画图操作，使用该构造方法 */
            this.annotation = parseJsonWithGson(annotationStr, MAnnotationInternal.AnnotationStr.class);
            id = this.annotation.getAnnotationbase().getAnnotation_id_();
            MAnnotationInternal.AnnotationStr.Annotationbase.MColor lineColorObj = annotation.getAnnotationbase().getLine_color_();
            int lineColor = Color.argb(lineColorObj.getA(), lineColorObj.getR(), lineColorObj.getG(), lineColorObj.getB());

            MAnnotationInternal.AnnotationStr.Annotationbase.MColor fillColorObj = annotation.getAnnotationbase().getFill_color_();
            int fillColor;
            if (annotation.getAnnotationbase().getTool_type_() == 2) {
                fillColor = Color.argb(0, fillColorObj.getR(), fillColorObj.getG(), fillColorObj.getB());
            } else {
                fillColor = Color.argb(fillColorObj.getA(), fillColorObj.getR(), fillColorObj.getG(), fillColorObj.getB());
            }

            int lineWidth = MWhiteboard.DisplayDimension.sp2px(context, annotation.getAnnotationbase().getLine_width_());

            this.mPaint = getPaint(lineWidth, lineColor, fillColor);
        }

        public void pathMoveTo(int x, int y) {
            if (mPath != null) {
                mPath.moveTo(x, y);
                pointList.add(new MAnnotationInternal.AnnotationStr.MPoint(x, y));
                mLastX = x;
                mLastY = y;
            }
        }

        public void pathLineTo(int x, int y) {
            if (mPath != null) {
                mPath.lineTo(x, y);
                pointList.add(new MAnnotationInternal.AnnotationStr.MPoint(x, y));
            }
        }

        public void pathQuadTo(int x, int y) {
            if (mPath != null && pointList.size() != 0) {
                pointList.add(new MAnnotationInternal.AnnotationStr.MPoint(x, y));
                mPath.quadTo(mLastX, mLastY, (x + mLastX) / 2, (y + mLastY) / 2);
                mLastX = x;
                mLastY = y;
            }
        }

        public void addPoint(int x, int y) {
            pointList.add(new MAnnotationInternal.AnnotationStr.MPoint(x, y));
        }

        public boolean preparePoints() {
            if (annotation.getPoints() == null) {
                if (annotation.getAnnotationbase().getTool_type_() == ToolType.LOCAL_ARROW.code && annotation.getAnnotationbase().getLine_arrow_() == 4) {
                    MAnnotationInternal.AnnotationStr.MPoint mPoints[] = new MAnnotationInternal.AnnotationStr.MPoint[2];
                    mPoints[0] = new MAnnotationInternal.AnnotationStr.MPoint(pointList.get(0).getX(), pointList.get(0).getY());
                    mPoints[1] = new MAnnotationInternal.AnnotationStr.MPoint(pointList.get(pointList.size() - 1).getX(), pointList.get(pointList.size() - 1).getY());
                    annotation.setPoints(mPoints);
                } else {
                    annotation.setPoints(pointList.toArray(new MAnnotationInternal.AnnotationStr.MPoint[0]));
                }

                if (annotation.getPoints() == null) {
                    return false;
                } else {
                    return true;
                }
            } else {
                return true;
            }
        }

        public String getId() {
            return id;
        }

        public void setId(String id) {
            this.id = id;
        }

        public Path getmPath() {
            return mPath;
        }

        public void setmPath(Path mPath) {
            this.mPath = mPath;
        }

        public Paint getmPaint() {
            return mPaint;
        }

        public void setmPaint(Paint mPaint) {
            this.mPaint = mPaint;
        }

        public float getmLastX() {
            return mLastX;
        }

        public void setmLastX(float mLastX) {
            this.mLastX = mLastX;
        }

        public float getmLastY() {
            return mLastY;
        }

        public void setmLastY(float mLastY) {
            this.mLastY = mLastY;
        }

        public MAnnotationInternal.AnnotationStr getAnnotation() {
            return annotation;
        }

        public List<MAnnotationInternal.AnnotationStr.MPoint> getPointList() {
            return pointList;
        }

        public void setPointList(List<MAnnotationInternal.AnnotationStr.MPoint> pointList) {
            this.pointList = pointList;
        }
    }

    private void drawText(String str, int x1, int y1, int x2, int y2, DrawViewBean bean) {
        int xMin = x1 > x2 ? x2 : x1;
        int xMax = x1 > x2 ? x1 : x2;
        int yMin = y1 > y2 ? y2 : y1;
        int yMax = y1 > y2 ? y1 : y2;   // 以左下角为参考点设置文字位置
        bean.getmPaint().setTextSize(60);
        bean.getmPaint().setStrokeWidth(5);
        mBufferCanvas.drawText(str, Math.min(x1, x2), yMin + (yMax - yMin) / 2, bean.getmPaint());
        bean.getmPaint().setStrokeWidth(lineWidth);
        invalidate();
    }

    private void drawPolyLine(DrawViewBean bean) {
        if (bean.getmPath() == null || isOrientationChange) {
            bean.mPath = new Path();
            MAnnotationInternal.AnnotationStr.MPoint[] points = bean.getAnnotation().getPoints();
            for (int i = 0; i < points.length; i++) {
                int x = points[i].getX();
                int y = points[i].getY();
                if (i == 0) {
                    bean.getmPath().moveTo(x, y);
                    continue;
                }
                int lastX = points[i - 1].getX();
                int lastY = points[i - 1].getY();
                bean.getmPath().quadTo(lastX, lastY, (x + lastX) / 2, (y + lastY) / 2);
            }
        }
        mBufferCanvas.drawPath(bean.getmPath(), bean.getmPaint());
    }

    private void drawRhomb(DrawViewBean bean) {
        if (bean.getmPath() == null || isOrientationChange) {
            bean.mPath = new Path();
            MAnnotationInternal.AnnotationStr.MPoint[] points = bean.getAnnotation().getPoints();
            if (points.length != 2) {
                return;
            }
            int xMin = points[0].getX() >= points[1].getX() ? points[1].getX() : points[0].getX();
            int xMax = points[0].getX() >= points[1].getX() ? points[0].getX() : points[1].getX();
            int yMin = points[0].getY() >= points[1].getY() ? points[1].getY() : points[0].getY();
            int yMax = points[0].getY() >= points[1].getY() ? points[0].getY() : points[1].getY();
            int dx = xMax - xMin;
            int dy = yMax - yMin;
            MAnnotationInternal.AnnotationStr.MPoint a = new MAnnotationInternal.AnnotationStr.MPoint(xMin, yMin + dy / 2);
            MAnnotationInternal.AnnotationStr.MPoint b = new MAnnotationInternal.AnnotationStr.MPoint(xMin + dx / 2, yMin);
            MAnnotationInternal.AnnotationStr.MPoint c = new MAnnotationInternal.AnnotationStr.MPoint(xMax, yMin + dy / 2);
            MAnnotationInternal.AnnotationStr.MPoint d = new MAnnotationInternal.AnnotationStr.MPoint(xMin + dx / 2, yMax);

            bean.pathMoveTo(a.getX(), a.getY());
            bean.pathLineTo(b.getX(), b.getY());
            bean.pathLineTo(c.getX(), c.getY());
            bean.pathLineTo(d.getX(), d.getY());
            bean.pathLineTo(a.getX(), a.getY());
            bean.getmPaint().setStyle(Paint.Style.STROKE);
        }
        mBufferCanvas.drawPath(bean.getmPath(), bean.getmPaint());
    }

    private void drawLaserPen(DrawViewBean bean) {
        MAnnotationInternal.AnnotationStr.MPoint[] points = bean.getAnnotation().getPoints();
        Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.cn_tee3_img_laser_pen);
        int width = bitmap.getWidth();
        int height = bitmap.getHeight();
        int left = points[0].getX();
        int top = points[0].getY();
        Rect src = new Rect(left, top, width + left, height + top);   /** 图片裁剪区域，null则不裁剪全显示 */
        RectF dst = new RectF(left - (float)width / 2, top - (float)height / 2, left + (float)width / 2, top + (float)height / 2);   /** 图片显示位置 */
        mBufferCanvas.drawBitmap(bitmap, null, dst, bean.getmPaint());
    }

    private void drawEllipse(DrawViewBean bean) {
        MAnnotationInternal.AnnotationStr.MPoint[] points = bean.getAnnotation().getPoints();
        if (points.length != 2) {
            return;
        }
        int xMin = points[0].getX() >= points[1].getX() ? points[1].getX() : points[0].getX();
        int xMax = points[0].getX() >= points[1].getX() ? points[0].getX() : points[1].getX();
        int yMin = points[0].getY() >= points[1].getY() ? points[1].getY() : points[0].getY();
        int yMax = points[0].getY() >= points[1].getY() ? points[0].getY() : points[1].getY();

        RectF rectF = new RectF(xMin, yMin, xMax, yMax);     // 指定椭圆外轮廓矩形区域
        mBufferCanvas.drawOval(rectF, bean.getmPaint());
    }

    private void drawRect(DrawViewBean bean) {
        MAnnotationInternal.AnnotationStr.MPoint[] points = bean.getAnnotation().getPoints();
        if (points.length != 2) {
            return;
        }
        int xMin = points[0].getX() >= points[1].getX() ? points[1].getX() : points[0].getX();
        int xMax = points[0].getX() >= points[1].getX() ? points[0].getX() : points[1].getX();
        int yMin = points[0].getY() >= points[1].getY() ? points[1].getY() : points[0].getY();
        int yMax = points[0].getY() >= points[1].getY() ? points[0].getY() : points[1].getY();

        mBufferCanvas.drawRect(xMin, yMin, xMax, yMax, bean.getmPaint());
    }

    private void drawArrow(DrawViewBean bean) {
        if (bean.getmPath() == null || isOrientationChange) {
            bean.mPath = new Path();
            MAnnotationInternal.AnnotationStr.MPoint[] points = bean.getAnnotation().getPoints();
            if (points.length != 2) {
                return;
            }
            int xMin = points[0].getX() >= points[1].getX() ? points[1].getX() : points[0].getX();
            int xMax = points[0].getX() >= points[1].getX() ? points[0].getX() : points[1].getX();
            int yMin = points[0].getY() >= points[1].getY() ? points[1].getY() : points[0].getY();
            int yMax = points[0].getY() >= points[1].getY() ? points[0].getY() : points[1].getY();
            int dx = xMax - xMin;
            int dy = yMax - yMin;

            MAnnotationInternal.AnnotationStr.MPoint a = new MAnnotationInternal.AnnotationStr.MPoint(xMin, yMin + 2 * dy / 3);
            MAnnotationInternal.AnnotationStr.MPoint b = new MAnnotationInternal.AnnotationStr.MPoint(xMin, yMin + dy / 3);
            MAnnotationInternal.AnnotationStr.MPoint c = new MAnnotationInternal.AnnotationStr.MPoint(xMin + 3 * dx / 5, b.getY());
            MAnnotationInternal.AnnotationStr.MPoint d = new MAnnotationInternal.AnnotationStr.MPoint(c.getX(), yMin);
            MAnnotationInternal.AnnotationStr.MPoint e = new MAnnotationInternal.AnnotationStr.MPoint(xMax, yMin + dy / 2);
            MAnnotationInternal.AnnotationStr.MPoint f = new MAnnotationInternal.AnnotationStr.MPoint(c.getX(), yMax);
            MAnnotationInternal.AnnotationStr.MPoint g = new MAnnotationInternal.AnnotationStr.MPoint(c.getX(), a.getY());
            bean.pathMoveTo(a.getX(), a.getY());
            bean.pathLineTo(b.getX(), b.getY());
            bean.pathLineTo(c.getX(), c.getY());
            bean.pathLineTo(d.getX(), d.getY());
            bean.pathLineTo(e.getX(), e.getY());
            bean.pathLineTo(f.getX(), f.getY());
            bean.pathLineTo(g.getX(), g.getY());
            bean.pathLineTo(a.getX(), a.getY());
            bean.getmPath().close();    // 使这些点构成封闭的多边形
        }
        mBufferCanvas.drawPath(bean.getmPath(), bean.getmPaint());
    }

    private void drawTextInArrow(DrawViewBean bean) {
        bean.mPath = new Path();
        MAnnotationInternal.AnnotationStr.MPoint[] points = bean.getAnnotation().getPoints();
        if (points.length != 2) {
            return;
        }

        int xMin = Math.min(points[1].getX(), points[0].getX());
        int xMax = Math.max(points[1].getX(), points[0].getX());
        int yMin = Math.min(points[1].getY(), points[0].getY());
        int yMax = Math.max(points[1].getY(), points[0].getY());

        String ownName = whiteboard.get_ownName() + "";
        if (ownName.length() > 6) {
            ownName = ownName.substring(0, 6);
            ownName += "...";
        }
        Rect rect = new Rect();
//        bean.getmPaint().getTextBounds(title, 0, title.length(), rect);
        bean.getmPaint().setTextSize(25);
        bean.getmPaint().getTextBounds(ownName, 0, ownName.length(), rect);
        int width = rect.width() + 20;
        int height = rect.height() + 10;

        int x = xMin + Math.abs(xMax - xMin) / 2;
        int y = yMin + Math.abs(yMax - yMin) / 2;

        xMin = x - width / 2;
        xMax = x + width / 2 + 35;
        yMin = y - height / 2 - 20;
        yMax = y + height / 2 + 20;

        MAnnotationInternal.AnnotationStr.MPoint a = new MAnnotationInternal.AnnotationStr.MPoint(xMin, y + height / 2);
        MAnnotationInternal.AnnotationStr.MPoint b = new MAnnotationInternal.AnnotationStr.MPoint(xMin, y - height / 2);
        MAnnotationInternal.AnnotationStr.MPoint c = new MAnnotationInternal.AnnotationStr.MPoint(xMin + width, b.getY());
        MAnnotationInternal.AnnotationStr.MPoint d = new MAnnotationInternal.AnnotationStr.MPoint(c.getX(), yMin);
        MAnnotationInternal.AnnotationStr.MPoint e = new MAnnotationInternal.AnnotationStr.MPoint(xMax, y);
        MAnnotationInternal.AnnotationStr.MPoint f = new MAnnotationInternal.AnnotationStr.MPoint(c.getX(), yMax);
        MAnnotationInternal.AnnotationStr.MPoint g = new MAnnotationInternal.AnnotationStr.MPoint(c.getX(), a.getY());
        bean.pathMoveTo(a.getX(), a.getY());
        bean.pathLineTo(b.getX(), b.getY());
        bean.pathLineTo(c.getX(), c.getY());
        bean.pathLineTo(d.getX(), d.getY());
        bean.pathLineTo(e.getX(), e.getY());
        bean.pathLineTo(f.getX(), f.getY());
        bean.pathLineTo(g.getX(), g.getY());
        bean.pathLineTo(a.getX(), a.getY());
        bean.getmPath().close();    // 使这些点构成封闭的多边形

        bean.getmPaint().setStrokeWidth(3);
        bean.getmPaint().setColor(Color.RED);
        bean.getmPaint().setStyle(Paint.Style.FILL);
        mBufferCanvas.drawPath(bean.getmPath(), bean.getmPaint());

        bean.getmPaint().setStrokeWidth(1);
        bean.getmPaint().setColor(Color.DKGRAY);
        mBufferCanvas.drawText(ownName, xMin + 10, yMax - 28, bean.getmPaint());
        bean.getmPaint().setColor(lineColor);
        bean.getmPaint().setStrokeWidth(lineWidth);
    }

    private void drawStraightLine(DrawViewBean bean) {
        if (bean.getmPath() == null || isOrientationChange) {
            bean.mPath = new Path();
            MAnnotationInternal.AnnotationStr.MPoint[] points = bean.getAnnotation().getPoints();
            int x1 = points[0].getX();
            int y1 = points[0].getY();
            int x2 = points[1].getX();
            int y2 = points[1].getY();
            switch (bean.getAnnotation().getAnnotationbase().getLine_arrow_()) {
                case 1:     /** 直线 */
                    bean.pathMoveTo(x1, y1);
                    bean.pathLineTo(x2, y2);
                    break;
                case 4:     /** 单箭头 */
                    bean.setmPath(getPath4SingleArrow(bean.getmPath(), x1, y1, x2, y2));
                    break;
                case 6:     /** 双箭头 */
                    bean.setmPath(getPath4DoubleArrow(bean.getmPath(), x1, y1, x2, y2));
                    break;
            }
        }
        mBufferCanvas.drawPath(bean.getmPath(), bean.getmPaint());
    }

    private void delAnnotationAndRefresh(String annotionId) {
        List<String> keyList = getKeyList(drawViewBeanMap);
        for (String key : keyList) {
            DrawViewBean bean = drawViewBeanMap.get(key);
            if (bean.getId().equals(annotionId)) {
                drawViewBeanMap.remove(key);
                break;
            }
        }
        refreshView();
    }

    public void clear() {
        if (mBufferBitmap != null) {
            if (upLoadAnnotationCallback != null) {
                upLoadAnnotationCallback.onClearAnnotations(boardId);
            }

            for (String key : localDrawViewBeanList) {
                drawViewBeanMap.remove(key);
            }

            localDrawViewBeanList.clear();
            refreshView();
            invalidate();
        }
    }

    public void undo() {
        if (mBufferBitmap != null) {
            if (upLoadAnnotationCallback != null) {
                upLoadAnnotationCallback.onUndo(boardId);
            }

            drawViewBeanMap.remove(localDrawViewBeanList.get(localDrawViewBeanList.size() - 1));
            localDrawViewBeanList.remove(localDrawViewBeanList.size() - 1);
            refreshView();
            invalidate();
        }
    }

    /**
     * 清除绘制内容
     */
    public void clearPaint() {
        if (mBufferBitmap == null || mBufferCanvas == null) {
            return;
        }
        mBufferBitmap.eraseColor(boardBackgroundColor);
        mBufferCanvas.drawColor(boardBackgroundColor, PorterDuff.Mode.CLEAR);
        mBufferCanvas.save();
        invalidate();
    }

    public void release() {
        whiteboard = null;
        whiteboardView = null;
        drawViewBeanMap.clear();
        localDrawViewBeanList.clear();
        annotationList.clear();
        if (mBufferBitmap != null && !mBufferBitmap.isRecycled()) {
            mBufferBitmap.recycle();
        }
    }

    public void refreshView() {
        if (mBufferBitmap == null) {
            return;
        }
        clearPaint();   // 清除所有画线
        if (drawViewBeanMap != null && drawViewBeanMap.size() > 0) {
            List<String> keyList = getKeyList(drawViewBeanMap);
            for (String key : keyList) {
                DrawViewBean bean = drawViewBeanMap.get(key);
                MAnnotationInternal.AnnotationStr annotation = bean.getAnnotation();
                ToolType toolType = getToolTypeByCode(annotation.getAnnotationbase().getTool_type_());
                switch (toolType) {
                    case MOUSE:
                        break;
                    case LINE:
                        drawStraightLine(bean);     /** 画 “直线”、”单箭头“，“双箭头”；其中 “直线” -> line_arrow = 1, "单箭头” -> line_arrow = 4， “双箭头” -> line_arrow = 6 */
                        break;
                    case FLUORESCENT_PEN:
                    case POLY_LINE:                 /** 曲线 */
                        drawPolyLine(bean);
                        break;
                    case ARROW:
                        drawArrow(bean);
                        break;
                    case RECTANGLE:
                        drawRect(bean);
                        break;
                    case ELLIPSE:   // 椭圆
                        drawEllipse(bean);
                        break;
                    case POLYGON:   // 多边形
                        break;
                    case TEXT:
                        drawTextInArrow(bean);  // 绘制箭头，箭头里包含白板名称
                        break;
                    case LASER_PEN:
                        laserPenId = bean.getId();
                        drawLaserPen(bean);
                        break;
                    case RUBBER_LINE:
                        break;
                    case RUBBER_RECTANGLE:
                        break;
                    case RHOMB:     // 菱形
                        drawRhomb(bean);
                        break;
                    case SUCCESS:
                        MAnnotationInternal.AnnotationStr.MPoint[] points = annotation.getPoints();
                        if (points.length == 2) {
                            drawText("√", points[0].getX(), points[0].getY(), points[1].getX(), points[1].getY(), bean);
                        }
                        break;
                    case FAILURE:
                        MAnnotationInternal.AnnotationStr.MPoint[] p = annotation.getPoints();
                        if (p.length == 2) {
                            drawText("×", p[0].getX(), p[0].getY(), p[1].getX(), p[1].getY(), bean);
                        }
                        break;
                }
            }
            isOrientationChange = false;
            invalidate();
        }
    }

    public void setWhiteboardColor(int color) {
        boardBackgroundColor = color;
        setBackgroundColor(boardBackgroundColor);
    }

    private float distancePoint2Line(MAnnotationInternal.AnnotationStr.MPoint pt, MAnnotationInternal.AnnotationStr.MPoint p, MAnnotationInternal.AnnotationStr.MPoint q) {
        float pqx = q.getX() - p.getX();
        float pqy = q.getY() - p.getY();
//        float pqz = q[2] - p[2];
        float dx = pt.getX() - p.getX();
        float dy = pt.getY() - p.getY();
//        float dz = pt[2] - p[2];
        float d = pqx * pqx + pqy * pqy;      // qp线段长度的平方
        float t = pqx * dx + pqy * dy;         // p pt向量 点积 pq 向量（p相当于A点，q相当于B点，pt相当于P点）
        if (d > 0)         // 除数不能为0; 如果为零 t应该也为零。下面计算结果仍然成立。
            t /= d;    // 此时t 相当于 上述推导中的 r。
        if (t < 0)
            t = 0;     // 当t（r）< 0时，最短距离即为 pt点 和 p点（A点和P点）之间的距离。
        else if (t > 1)
            t = 1;     // 当t（r）> 1时，最短距离即为 pt点 和 q点（B点和P点）之间的距离。

        // t = 0，计算 pt点 和 p点的距离; t = 1, 计算 pt点 和 q点 的距离; 否则计算 pt点 和 投影点 的距离。
        dx = p.getX() + t * pqx - pt.getX();
        dy = p.getY() + t * pqy - pt.getY();
//        dz = p[2] + t*pqz - pt[2];
        return dx * dx + dy * dy;
    }

    public <k, v> List<k> getKeyList(HashMap<k, v> map) {
        List<k> keys = new ArrayList<>();
        keys.addAll(map.keySet());
        return keys;
    }

    //将Json数据解析成相应的映射对象
    public <T> T parseJsonWithGson(String jsonData, Class<T> type) {
        Gson gson = new Gson();
        T result = gson.fromJson(jsonData, type);
        return result;
    }

    /******************************************************************     ToolType 枚举相关的初始化 *********************************************************************************************************/
    private void initAnnotation2ToolType() {
        ToolType[] values = ToolType.values();
        for (int i = 0; i < 14; i++) {  // 不添加 FLUORESCENT_PEN 和 LOCAL_ARROW（单箭头）
            annotation2ToolType.put(values[i].code, values[i]);  // values第一个是荧光笔不放入里面
        }
    }

    public int getCodeByToolType(ToolType tool) {
        return tool.getCode();
    }

    private ToolType getToolTypeByCode(int code) {
        return annotation2ToolType.get(code);
    }

    /*******************************************************************************    annotation  【添加、删除、更新】   ********************************************************************************************************/
    public void annotationDel(String j_boardId, String annotation_id) {
        delAnnotationAndRefresh(annotation_id);
    }

    public void annotationAdd(String j_boardId, String j_annotationstr) {
        DrawViewBean bean = new DrawViewBean(j_annotationstr);
        if (bean.annotation.points == null || bean.annotation.points.length == 0) {
            return;
        }
        String userId = bean.getAnnotation().getAnnotationbase().getUser_id_();
        int toolType = bean.getAnnotation().getAnnotationbase().getTool_type_();
        bean.annotation.points = pointsRemote2Local(bean.annotation.points);

        if (toolType == ToolType.LASER_PEN.code) {
            Set<Map.Entry<String, DrawViewBean>> entries = drawViewBeanMap.entrySet();
            Iterator<Map.Entry<String, DrawViewBean>> iterator = entries.iterator();

            while (iterator.hasNext()) {
                Map.Entry<String, DrawViewBean> next = iterator.next();
                DrawViewBean value = next.getValue();
                MAnnotationInternal.AnnotationStr.Annotationbase laserPen = value.getAnnotation().getAnnotationbase();
                if (laserPen.getUser_id_().equals(userId) && laserPen.getTool_type_() == ToolType.LASER_PEN.code && !laserPen.getAnnotation_id_().equals(bean.getId())) {
                    iterator.remove();
                    break;
                }
            }
        }
        drawViewBeanMap.put(bean.getId(), bean);
        refreshView();
    }

    public void annotationUpdate(String j_boardId, String j_annotationstr) {
//        Log.i(TAG, "onAnnotationUpdateNotifyStr() -> " + j_annotationstr);
    }

    /*******************************************************************************    画完线通知其他人    ********************************************************************************************************/
    public void setUpLoadAnnotationCallback(IUploadAnnotationInternalListener upLoadAnnotationCallback) {
        this.upLoadAnnotationCallback = upLoadAnnotationCallback;
    }

    public interface IUploadAnnotationInternalListener {
        void onUploadAddAnnotation(String boardId, String annotationStr);

        void onUploadRemoveAnnotation(String boardId, String annotationId);

        void onClearAnnotations(String boardId);

        void onUndo(String boardId);

        void onRedo(String boardId);
    }

    /********************************************************************************************************************************/

    Path getPath4SingleArrow(Path mPath, float x1, float y1, float x2, float y2) {
        MAnnotationInternal.AnnotationStr.MPoint[] points = getArrowPointsByTwoPoints(x1, y1, x2, y2);
        if (points == null) {
            return mPath;
        }
        mPath.reset();
        mPath.moveTo(x1, y1);
        mPath.lineTo(x2, y2);
        mPath.lineTo(points[0].getX(), points[0].getY());
        mPath.lineTo(x2, y2);
        mPath.lineTo(points[1].getX(), points[1].getY());
        return mPath;
    }

    Path getPath4DoubleArrow(Path mPath, float x1, float y1, float x2, float y2) {
        MAnnotationInternal.AnnotationStr.MPoint[] points_01 = getArrowPointsByTwoPoints(x1, y1, x2, y2);
        MAnnotationInternal.AnnotationStr.MPoint[] points_02 = getArrowPointsByTwoPoints(x2, y2, x1, y1);
        if (points_01 == null || points_02 == null) {
            return mPath;
        }
        mPath.reset();
        mPath.moveTo(points_02[0].getX(), points_02[0].getY());
        mPath.lineTo(x1, y1);
        mPath.lineTo(points_02[1].getX(), points_02[1].getY());
        mPath.lineTo(x1, y1);
        mPath.lineTo(x2, y2);
        mPath.lineTo(points_01[0].getX(), points_01[0].getY());
        mPath.lineTo(x2, y2);
        mPath.lineTo(points_01[1].getX(), points_01[1].getY());
        return mPath;
    }

    double awrad = Math.atan(3.5 / 8);

    private MAnnotationInternal.AnnotationStr.MPoint[] getArrowPointsByTwoPoints(float x1, float y1, float x2, float y2) {
        if (Math.abs(x1 - x2) < 1 || Math.abs(y1 - y2) < 1) {
            return null;
        }

        int x3 = 0;
        int y3 = 0;
        // 箭头中的第二条线的起点
        int x4 = 0;
        int y4 = 0;

        double[] arrXY_1 = rotateVec(x2 - x1, y2 - y1, awrad, arrowSize);
        double[] arrXY_2 = rotateVec(x2 - x1, y2 - y1, -awrad, arrowSize);

        // 第一端点
        Double X3 = Double.valueOf(x2 - arrXY_1[0]);
        x3 = X3.intValue();
        Double Y3 = Double.valueOf(y2 - arrXY_1[1]);
        y3 = Y3.intValue();

        // 第二端点
        Double X4 = Double.valueOf(x2 - arrXY_2[0]);
        x4 = X4.intValue();
        Double Y4 = Double.valueOf(y2 - arrXY_2[1]);
        y4 = Y4.intValue();

        MAnnotationInternal.AnnotationStr.MPoint[] points = new MAnnotationInternal.AnnotationStr.MPoint[2];
        points[0] = new MAnnotationInternal.AnnotationStr.MPoint(x3, y3);
        points[1] = new MAnnotationInternal.AnnotationStr.MPoint(x4, y4);
        return points;
    }

    private double[] rotateVec(float px, float py, double ang, double arrowSize) {
        double mathstr[] = new double[2];
        double vx = px * Math.cos(ang) - py * Math.sin(ang);
        double vy = px * Math.sin(ang) + py * Math.cos(ang);
        double d = Math.sqrt(vx * vx + vy * vy);
        vx = vx / d * arrowSize;
        vy = vy / d * arrowSize;
        mathstr[0] = vx;
        mathstr[1] = vy;
        return mathstr;
    }
}
