/*
 * ********************************************************************************************************************
 *
 *               iFHS7.
 *              ;BBMBMBMc                  rZMBMBR              BMB
 *              MBEr:;PBM,               7MBMMEOBB:             BBB                       RBW
 *     XK:      BO     SB.     :SZ       MBM.       c;;     ir  BBM :FFr       :SSF:    ;xBMB:r   iuGXv.    i:. iF2;
 *     DBBM0r.  :D     S7   ;XMBMB       GMBMu.     MBM:   BMB  MBMBBBMBMS   WMBMBMBBK  MBMBMBM  BMBRBMBW  .MBMBMBMBB
 *      :JMRMMD  ..    ,  1MMRM1;         ;MBMBBR:   MBM  ;MB:  BMB:   MBM. RMBr   sBMH   BM0         UMB,  BMB.  KMBv
 *     ;.   XOW  B1; :uM: 1RE,   i           .2BMBs  rMB. MBO   MBO    JMB; MBB     MBM   BBS    7MBMBOBM:  MBW   :BMc
 *     OBRJ.SEE  MRDOWOR, 3DE:7OBM       .     ;BMB   RMR7BM    BMB    MBB. BMB    ,BMR  .BBZ   MMB   rMB,  BMM   rMB7
 *     :FBRO0D0  RKXSXPR. JOKOOMPi       BMBSSWBMB;    BMBB:    MBMB0ZMBMS  .BMBOXRBMB    MBMDE RBM2;SMBM;  MBB   xBM2
 *         iZGE  O0SHSPO. uGZ7.          sBMBMBDL      :BMO     OZu:BMBK,     rRBMB0;     ,EBMB  xBMBr:ER.  RDU   :OO;
 *     ,BZ, 1D0  RPSFHXR. xWZ .SMr                  . .BBB
 *      :0BMRDG  RESSSKR. 2WOMBW;                   BMBMR
 *         i0BM: SWKHKGO  MBDv
 *           .UB  OOGDM. MK,                                          Copyright (c) 2015-2019.  斯伯坦机器人
 *              ,  XMW  ..
 *                  r                                                                     All rights reserved.
 *
 * ********************************************************************************************************************
 */

package com.sybotan.android.graphy

import android.content.Context
import android.graphics.*
import android.util.AttributeSet
import android.util.Log
import android.view.*
import android.view.MotionEvent
import com.sybotan.android.graphy.enums.SGraphyViewTouchState
import com.sybotan.android.graphy.events.SGraphyViewMoveEvent
import com.sybotan.android.graphy.events.SGraphyViewZoomEvent
import com.sybotan.android.graphy.utils.MatrixTools
import com.sybotan.base.extensions.toJson
import org.greenrobot.eventbus.EventBus
import kotlin.math.max
import kotlin.math.min

/**
 * SGraphy图形引擎视图类
 *
 * @author  庞利祥(sybotan@126.com)
 */
open class SGraphyView(context: Context, attrs: AttributeSet? = null)
    : SurfaceView(context, attrs), SurfaceHolder.Callback, View.OnTouchListener, GestureDetector.OnGestureListener, Runnable {

    /**
     * 类对象
     */
    companion object {
        private val TAG = SGraphyView::class.java.name
        private var fitrate =0.8f
    } // companion object

    /** 指向加载到视图中的场景 */
    var scene: SGraphyScene? = null
        set(value) {
            if (field == value) {       // 如果场景没有变化
                return
            }
            field?.view = null
            value?.view = this
            // 保存场景
            field = value
        }
    /** 旋转 */
    var rotate = 0.0f
    /** 是否支持二指缩放操作 */
    var canZoom = true
    /** 缩放比例 */
    var scale = 1f
        get() = max(min(field, maxScale), minScale)
        set(value) {
            field = value
            moveRange()
        }
    /** 默认缩放比例，用于指定最小缩放比例*/
    var defaultScale = 0.0000001f
    /**点的基数缩放比 */
    var defaultPointScale = 0.01785718f
    /** 最小缩放比例 */
    var minScale = 0.0000001f
        set(value) {
            field = min(value, maxScale)
        }
    /** 最大缩放比例 */
    var maxScale = 1000000f
        set(value) {
            field = max(value, minScale)
        }

    /** 是否支持移动场景操作 */
    var canMove = true
    /** 场景原点位置在视图中的位置 */
    var pos = PointF(0f, 0f)
        get() {
            field.x = min(max(field.x, minPos.x), maxPos.x)
            field.y = min(max(field.y, minPos.y), maxPos.y)
            return field
        }
    /** 视图原点最小值 */
    var minPos = PointF(Float.MIN_VALUE, Float.MIN_VALUE)
    /** 视图原点最大值 */
    var maxPos = PointF(Float.MAX_VALUE, Float.MAX_VALUE)


    /** 手执状态 */
    private var touchState = SGraphyViewTouchState.NoneState

    /** 是否在绘制状态 */
    private var isDrawing = false
    /** 手势解析器 */
    private val gesture by lazy {
        GestureDetector(this)
    }

    /** 未缩放时二指间距离 */
    private var beforeLength = 0.0f
    /** 缩放后二指间距离 */
    private var afterLength = 0.0f
    /** 后台绘制线程 */
    private var paintThread: Thread? = null

    /** 默认构造函数 */
    init {
        Log.d(TAG, "init")
        initView()
    } // init

    /**
     * 适配视图到视图
     */
    fun fitSceneToView() {
        // 未设置场景
        if (null == scene) {
            return
        }

        val w = width.toFloat()
        val h = height.toFloat()
        val rect = scene!!.worldRect()
        setCenterPoint( PointF(rect.centerX(), rect.centerY()),
                min(w / rect.width(), h / rect.height()) * fitrate)
    } // Function FitView()

    /**
     * 设置中心点
     *
     * @param scale 场景的缩放比例
     */
    fun setCenterPoint(pos : PointF, scale : Float? = null) {
        if (scale != null){
            this.scale = scale
        }
        this.pos = PointF(-pos.x  * this.scale + this.width.toFloat() / 2f,
                -pos.y * this.scale + this.height.toFloat() / 2f)
    } // Function setCenterPoint()

    /**
     * 限定移动范围
     */
    fun moveRange(){
        if (scene == null) {
            return
        }
        val w= this.width.toFloat()
        val h = this.height.toFloat()
        val rect = scene!!.worldRect()
        this.minPos = PointF(-rect.centerX() * this.scale + w / 2f - rect.width() / 2f * this.scale,
                -rect.centerY() * this.scale + h / 2f - rect.height() / 2f * this.scale)
        /**
         *   -(rect.left + rect.right) / 2f * graphyView.scale  + width / 2 图形的中心点，
         *   rect.width() / 2 * graphyView.scale / 0.8f 图形宽度的一半乘以缩放比例除以适配百分比
         *
         */
        this.maxPos = PointF(-rect.centerX() * this.scale  + w / 2f + rect.width() / 2f * this.scale,
                -rect.centerY() * this.scale +  h / 2f + rect.height() / 2f * this.scale)
    } // Function moveRange()

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // Surface相关操作
    /**
     * Surface创建成功时回调函数。
     *
     * @param   holder      surface创建成功时的保持器。
     */
    override fun surfaceCreated(holder: SurfaceHolder?) {
        Log.d(TAG, "surfaceCreated")

        isDrawing = true
        // 启动后台绘制线程
        paintThread = Thread(this)
        paintThread!!.start()
        return
    } // Function surfaceCreated()

    /**
     * Surface变现时回调函数。
     *
     * @param   holder      Surface保持器
     * @param   format      新的surface格式
     * @param   width       宽度
     * @param   height      高度
     */
    override fun surfaceChanged(holder: SurfaceHolder?, format: Int, width: Int, height: Int) {
        Log.d(TAG, "surfaceChanged：width=$width, height=$height")

        return
    } // Function surfaceChanged()

    /**
     * Surface被销毁时回调函数。
     *
     * @param   holder      surface销毁时的保持器。
     */
    override fun surfaceDestroyed(holder: SurfaceHolder?) {
        Log.d(TAG, "surfaceDestroyed")

        paintThread!!.interrupt()
        paintThread = null
        return
    } // Function surfaceDestroyed()

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 坐标换算相关操作
    /**
     * 将场景中的xy坐标转换成视图坐标。
     *
     * @param   x       场景中的横坐标
     * @param   y       场景中的纵坐标
     *
     * @return  在item中的坐标
     */
    fun mapFromScene(x: Float, y: Float): PointF {
        return PointF(x * scale + pos.x, y * scale + pos.y)
    } // Function mapFromScene()

    /**
     * 将场景中的xy坐标转换成视图坐标。
     *
     * @param   point   场景中的坐标
     * @return  在item中的坐标
     */
    fun mapFromScene(point: PointF): PointF {
        return mapFromScene(point.x, point.y)
    } // Function mapFromScene()

    /**
     * 将item中的xy坐标转换成场景坐标。
     *
     * @param   x       item中的横坐标
     * @param   y       item中的纵坐标
     * @return  在场景中的坐标
     */
    fun mapToScene(x: Float, y: Float): PointF {
        return PointF((x - pos.x) / scale, (y - pos.y) / scale)
    } // Function mapToScene()

    /**
     * 将item中的xy坐标转换成场景坐标。
     *
     * @param   point   item中的坐标
     * @return  在场景中的坐标
     */
    fun mapToScene(point: PointF): PointF {
        return mapToScene(point.x, point.y)
    } // Function mapToScene()

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 手势相关操作
    /**
     * 用户对视图进行触摸操作时触发
     *
     * @param   v       触摸的视图对象
     * @param   event   保存触摸事件参数
     * @return  如果处理了该事件返回true, 否则返回false
     */
    override fun onTouch(v: View, event: MotionEvent): Boolean {
        try {
            return when (event.action and MotionEvent.ACTION_MASK) {
                MotionEvent.ACTION_POINTER_DOWN -> {        // 多点触控
                    if (canZoom) {      // 如果可以缩放
                        touchState = SGraphyViewTouchState.ZoomState
                        beforeLength = getDistance(event)       // 获取两点的距离
                    }
                    true
                }
                MotionEvent.ACTION_POINTER_UP -> {          // 多点释放
                    touchState = SGraphyViewTouchState.NoneState
                    true
                }
                MotionEvent.ACTION_MOVE -> {
                    if (SGraphyViewTouchState.ZoomState == touchState && canZoom) { // 两点绽放操作
                        viewZoom(event)
                        true
                    } else {
                        gesture.onTouchEvent(event)
                    }
                }
                else -> {
                    gesture.onTouchEvent(event)
                }
            }
        } catch (e: Exception) {
            return true
        }
    } // Function onTouch()

    /**
     * 用户点击屏幕时触发。
     *
     * @param   e   触摸事件
     */
    override fun onShowPress(e: MotionEvent) {
        scene?.onShowPress(toSceneMotionEvent(e))
        return
    } // Function onShowPress()

    /**
     * 用户按下屏幕就会触发.
     *
     * @param   e       按下手势事件。
     * @return  如果事件被处理返回 true , 否则返回 false 。
     */
    override fun onDown(e: MotionEvent): Boolean {
        scene?.onDown(toSceneMotionEvent(e))
        return true
    } // Function onDown()

    /**
     * 用户释放屏幕触发.
     *
     * @param   e       释放手势事件。
     * @return  如果事件被处理返回 true , 否则返回 false 。
     */
    override fun onSingleTapUp(e: MotionEvent): Boolean {
        Log.e("手势view",e.toString())
        scene?.onSingleTapUp(toSceneMotionEvent(e))
        return true
    } // Function onSingleTapUp()

    /**
     * 长按触摸屏超过一定时间。
     *
     * @param   e       长按手势事件。
     */
    override fun onLongPress(e: MotionEvent) {
        scene?.onLongPress(toSceneMotionEvent(e))
        return
    } // Function onLoagPress()

    /**
     * 用户按下触摸屏，并拖动。
     *
     * @param   e1          第1个 ACTION_DOWN 手势事件
     * @param   e2          最后一个 ACTION_MOVE 手势事件
     * @param   distanceX   X轴上的移动距离，单位：像素
     * @param   distanceY   Y轴上的移动距离，单位：像素
     * @return  如果事件被处理返回 true , 否则返回 false 。
     */
    override fun onScroll(e1: MotionEvent, e2: MotionEvent, distanceX: Float, distanceY: Float): Boolean {
        if (scene == null) {
            return true
        }

        // 如果场景处理了该事件，则返回
        if (scene!!.onScroll(toSceneMotionEvent(e1), toSceneMotionEvent(e2), distanceX / scale, distanceY / scale)) {
            return true
        }

        if (canMove) {      // 如果可以移动场景
            // 移动场景
            pos.x = pos.x - distanceX
            pos.y = pos.y - distanceY
            Log.e("x",pos.x.toString())
            Log.e("y",pos.y.toString())
            EventBus.getDefault().post(SGraphyViewMoveEvent(this, pos.x, pos.y))
        }

        return true
    } // Function onScroll()

    /**
     * 用户按下触摸屏、快速移动后松开。即滑动操作。
     *
     * @param   e1          第1个 ACTION_DOWN 手势事件
     * @param   e2          最后一个 ACTION_MOVE 手势事件
     * @param   velocityX   X轴上的移动速度，像素/秒
     * @param   velocityY   Y轴上的移动速度，像素/秒
     * @return  如果事件被处理返回 true , 否则返回 false 。
     */
    override fun onFling(e1: MotionEvent, e2: MotionEvent, velocityX: Float, velocityY: Float): Boolean {
        scene?.onFling(toSceneMotionEvent(e1), toSceneMotionEvent(e2), velocityX / scale, velocityY / scale)
        return true
    } // Function onFling()

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 绘制相关操作
    /**
     * 重载线程run函数。
     */
    override fun run() {
        while(null != paintThread && !paintThread!!.isInterrupted) {      // 循环控制绘制
            var canvas: Canvas? = null
            try {
                Thread.sleep(100)
                canvas = holder.lockCanvas()
                if (null == canvas) {   // 如果canvas为空
                    continue
                }
                synchronized(holder) {
                    drawScene(canvas)
                }
            } catch (e: Exception) {
                e.printStackTrace()

            } finally {
                if (null != canvas) {
                    holder.unlockCanvasAndPost(canvas)
                }
            }
        }

        return
    } // Function run()

    /**
     * 后台场景绘制线程
     *
     * @param   canvas      画布
     */
    open fun drawScene(canvas: Canvas) {
        // 绘制背景
        canvas.save()
        scene?.drawBackground(canvas, RectF())
        canvas.restore()

        // 绘制场景
        canvas.save()
        canvas.translate(pos.x, pos.y)
        canvas.scale(scale, scale)
        scene?.drawScene(canvas, RectF())
        canvas.restore()

        // 绘制前景
        canvas.save()
        scene?.drawForeground(canvas, RectF())
        canvas.restore()
        return
    } // Function drawScene()

    /**
     * 缩放视图时计算视图的位置与缩放比例
     *
     * @param   zoom        缩放比例
     * @param   midPoint    缩放计算的中心点
     */
    fun scaleByPoint(zoom: Float, midPoint: PointF) {
        var z = zoom
        /**
         * 缩放比例在最小比例和最大比例范围内
         */
        when {
            scale * zoom >= maxScale -> {
                z = maxScale / scale
                scale = maxScale
            }
            scale * zoom <= minScale -> {
                z = minScale / scale
                scale = minScale
            }
            else -> {
                scale *= zoom
            }
        }

        pos.x = midPoint.x - (midPoint.x - pos.x) * z
        pos.y = midPoint.y - (midPoint.y - pos.y) * z

        EventBus.getDefault().post(SGraphyViewZoomEvent(this, scale))
        EventBus.getDefault().post(SGraphyViewMoveEvent(this, pos.x, pos.y))
        return
    } // Function scaleByPoint()

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 初始化相关操作
    /**
     * 初始化视图控件
     */
    private fun initView() {
        holder.addCallback(this)
        // 设置支持透明色
        holder.setFormat(PixelFormat.TRANSPARENT)
        isFocusable = true
        isFocusableInTouchMode = true

        setOnTouchListener(this)

        return
    } // Function initView()

    /**
     * 缩放视图
     *
     * @param   event       触摸事件
     */
    private fun viewZoom(event: MotionEvent): Boolean {
        if (touchState == SGraphyViewTouchState.ZoomState) {
            afterLength = getDistance(event)// 获取两点的距离
            val gapLenght = afterLength - beforeLength// 变化的长度
            if (Math.abs(gapLenght) > 5f && beforeLength != 0.0f) {
                val tempScale = afterLength / beforeLength// 求的缩放的比例
                val p = getMiddlePoint(event)
                scaleByPoint(tempScale, p)    //重设置
                beforeLength = afterLength
            }
        }
        return true
    } // Function onTouchMove()

    /**
     * 获取两手指之间的距离
     *
     * @param   event       触摸事件
     * @return 两手指之间的距离
     */
    private fun getDistance(event: MotionEvent): Float {
        val x = (event.getX(0) - event.getX(1)).toDouble()
        val y = (event.getY(0) - event.getY(1)).toDouble()
        return Math.sqrt(x * x + y * y).toFloat()
    } // Function getDistance()

    /**
     * 获得两个手指触摸点的中点坐标
     *
     * @param   event
     * @return  得到视图的x坐标中点
     */
    private fun getMiddlePoint(event: MotionEvent): PointF {
        return PointF((event.getX(1) + event.getX(0)) / 2f,
                (event.getY(1) + event.getY(0)) / 2f)
    } // Function getMiddlePoint()

    /**
     * MotionEvent转场景对象MotionEvent
     *
     * @param   e       MotionEvent
     * @return  子对象MotionEvent
     */
    private fun toSceneMotionEvent(e: MotionEvent): SMotionEvent {
        val src1 = kotlin.floatArrayOf(0f, 1f, 2f, 3f, 4f, 5f, 6f, 7f, 8f)
        val src2 = kotlin.floatArrayOf(0f, 1f, 2f, 3f, 4f, 5f, 6f, 7f, 8f)
        val se = SMotionEvent(e)
        se.matrix.postTranslate(pos.x,pos.y)
        se.matrix.postScale(this.scale, this.scale);
        se.matrix.postRotate(this.rotate)
        se.matrix.getValues(src1)
        Log.e("src1= ",src1.toJson())

        val matrixMat = Matrix()
        se.matrix.invert(matrixMat)
        matrixMat.getValues(src2)
        Log.e("src2= ",src2.toJson())
        val matrixTransform = MatrixTools.matrixTransform(matrixMat, e.x, e.y)
        se.x = matrixTransform.x
        se.y = matrixTransform.y
//        se.setLocation((e.x - pos.x) / scale, (e.y - pos.y) / scale)
        return se
    } // Function toSceneMotionEvent()



} // Class SGraphyView