/*
 * ********************************************************************************************************************
 *
 *               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.graphics.Canvas
import android.graphics.Matrix
import android.graphics.PointF
import android.graphics.RectF
import android.util.Log
import com.sybotan.android.graphy.enums.SGraphyItemFlag
import com.sybotan.android.graphy.listeners.SGraphyItemPosListener
import com.sybotan.android.graphy.utils.MatrixTools
import com.sybotan.base.extensions.toJson
import org.jetbrains.anko.doAsync
import java.util.*

/**
 * SGraphy图形引擎Item类
 *
 * @author  庞利祥(sybotan@126.com)
 */
open class SGraphyItem(parent: SGraphyItem? = null) {
    /**
     * 类对象
     */
    companion object {
        private val TAG = SGraphyItem::class.java.name
        /** 当前移动Item */
        private var currentMoveItem: SGraphyItem? = null
        /** 当前焦点Item */
        private var currentFocusItem: SGraphyItem? = null


        /**
         * MotionEvent转子对象MotionEvent
         *
         * @param   child   子item对象
         * @param   e       事件参数
         * @return  子对象MotionEvent
         */
        fun toChildMotionEvent(child: SGraphyItem, e: SMotionEvent): SMotionEvent {
            val ce = SMotionEvent(e)
            ce.matrix.postTranslate(child.pos.x, child.pos.y);
            ce.matrix.postScale(child.scale.x, child.scale.y);
            ce.matrix.postRotate(child.rotate,0f, 0f);
            // 不跟随缩放
            if (!child.isTransform) {
                ce.matrix.postScale(child._inverseScale, child._inverseScale);
            }
//            se.setLocation((e.x - child.pos.x) / child.scale.x, (e.y - child.pos.y) / child.scale.y)
            val matrixTransform = MatrixTools.matrixTransform(ce.matrix, e.x, e.y)
            ce.x = matrixTransform.x
            ce.y = matrixTransform.y
            return ce
        } // Function toSceneMotionEvent()
    } // companion object

    /** 父节点 */
    var parent:SGraphyItem? = parent
        set(value) {
            if (field == value) {   // 如果值没变化
                return
            }
            if (null != field) {    // 如果原parent不为空
                // 将节点从原parent节点中摘除
                field!!.children.remove(this)
            }
            field = value

            if (null != field) {    // 如果新parent不为空
                // 将节点加入到新parent节点中
                field!!.children.add(this)
                field!!.children.sortByDescending { it.zOrder }
            }
            invalidate()
        }

    /** 子节点列表 */
    val children =  ArrayList<SGraphyItem>()

    /** item所属场景 */
    var scene: SGraphyScene? = null
        get() {
            return if (null != parent) {
                parent!!.scene
            } else {
                field
            }
        }

    /** item位置 */
    var pos: PointF = PointF(0f, 0f)
        set(value) {
            if (field == value) {   // 如果值没变化
                return
            }
            field = value
            invalidate()
            posListener?.onMoveTo(field.x, field.y)
        }
    /** item位置变更监听器 */
    var posListener: SGraphyItemPosListener? = null

    /** z轴坐标，数值越大，越靠上 */
    var zOrder = 0f
        set(value) {
            if (field == value) {   // 如果值没变化
                return
            }
            field = value
            if (null != parent) {   // 如果你节点不空
                parent!!.children.sortByDescending { it.zOrder }
            }
            invalidate()
        }
    /** 缩放比例 */
    var scale = PointF(1f, 1f)
    /** 旋转 */
    var rotate = 0.0f
    /** 是否进行变形 */
   var isTransform = true;
    /** 放缩反比例 */
    var _inverseScale = 1f;
    /** item标志 */
    var flags: EnumSet<SGraphyItemFlag> = EnumSet.noneOf(SGraphyItemFlag::class.java)

    /** item是否可见 */
    var isVisible = true
        set(value) {
            if (field == value) {   // 如果值没变化
                return
            }
            field = value
            invalidate()
        }

    /** item是否获得焦点 */
    var focus = false
        set(value) {
            if (!flags.contains(SGraphyItemFlag.ItemIsFocusable)) {     // 如果item不能获得焦点
                return
            }
            if (field == value) {   // 如果值没变化
                return
            }
            field = value
            if (value) {            // 如果获得焦点
                currentFocusItem?.focus = false
                onGetFocus()
                currentFocusItem = this
            } else {
                onLostFocus()
                if (currentFocusItem === this) {        // 如果当前焦点对象失去焦点
                    currentFocusItem = null
                }
            }
        }

    /**
     * 当前获得焦点的item对象
     */
    val focusItem: SGraphyItem?
        get() = currentFocusItem

    /** item是否被选中 */
    var isSelected = false
        set(value) {
            if (field == value) {   // 如果值没变化
                return
            }
            field = value
            invalidate()
        }


    /**
     * Item对象边界区域
     *
     * @return  边界区域
     */
    open fun boundingRect(): RectF {
        return RectF()
    } // Function boundingRect()

    /**
     * 更新Item
     *
     * @param   rect        更新区域
     */
    fun invalidate(rect: RectF = RectF()) {
        scene?.update()
        return
    } // Function update()

    /**
     * 延迟刷新
     *
     * @param   delayed     延时的时间
     */
    fun postInvalidateDelayed(delayed: Long) {
        doAsync {
            Thread.sleep(delayed)
            invalidate()
        }

        return
    } // Function postInvalidateDelayed()

    /**
     * 隐藏item
     */
    fun hide() {
        isVisible = false
        return
    } // Function hide()

    /**
     * 显示item
     */
    fun show() {
        isVisible = true
        return
    } // Function show()

    /**
     * 移动item到指定位置
     *
     * @param   pos         新的位置
     */
    fun moveTo(pos: PointF)
    {
        this.pos = pos
        return
    } // Function move()

    /**
     * 移动item到指定位置
     *
     * @param   x           新位置的x坐标
     * @param   y           新位置的y坐标
     */
    fun moveTo(x: Float, y: Float)
    {
        pos = PointF(x, y)
        return
    } // Function move()

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 坐标换算相关操作
    /**
     * 将场景中的xy坐标转换成item坐标。（该节点被加载到场景中，如果未被加载到场景中，计算会出错）
     *
     * @param   x       场景中的横坐标
     * @param   y       场景中的纵坐标
     *
     * @return  在item中的坐标
     */
    fun mapFromScene(x: Float, y: Float): PointF {
        val m = this.scene2itemMattrix()
        val matrix = Matrix()
        m.invert(matrix)
       return MatrixTools.matrixTransform(matrix,x,y)
    } // Function mapFromScene()

    /**
     * 将场景中的xy坐标转换成item坐标。（该节点被加载到场景中，如果未被加载到场景中，计算会出错）
     *
     * @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 {
        if (null == parent) {
            return PointF(x, y)
        }
        val m = this.scene2itemMattrix()
        return return MatrixTools.matrixTransform(m,x,y)
//        return parent!!.mapToScene(x * scale.x + pos.x, y * scale.y + pos.y)
    } // Function mapToScene()

    /**
     * 将item中的xy坐标转换成场景坐标。（该节点被加载到场景中，如果未被加载到场景中，计算会出错）
     *
     * @param   point   item中的坐标
     *
     * @return  在场景中的坐标
     */
    fun mapToScene(point: PointF): PointF {
        return mapToScene(point.x, point.y)
    } // Function mapToScene()

    /**
     * 获得item的路径节点列表。（该节点被加载到场景中，如果未被加载到场景中，计算会出错）
     *
     * @return  从根节点到当前节点的路径节点列表。
     */
    fun itemPath(): MutableList<SGraphyItem> {
        if (null != parent) {
            val list = parent!!.itemPath()
            list.add(this)
            return list
        }

        return mutableListOf(this)
    } // Function itemPath()

    /**
     * 判断item是否包含点x,y
     *
     * @param   x       横坐标（当前item）
     * @param   y       纵坐标（当前item）
     *
     * @return  true包含；false不包含。
     */
    open fun contains(x: Float, y: Float): Boolean {
        return boundingRect().contains(x, y)
    } // Function ()

    /**
     * 判断item是否包含点x,y
     *
     * @param   point   点坐标（当前item）
     *
     * @return  true包含；false不包含。
     */
    fun contains(point: PointF): Boolean {
        return contains(point.x, point.y)
    } // Function ()

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 事件回调
    /**
     * Item绘制操作
     *
     * @param   canvas      画布对象
     * @param   rect        更新区域
     */
    open fun onPaint(canvas : Canvas, rect: RectF) {
        canvas.save()
        this.onDraw(canvas)
        canvas.restore()
        val src = kotlin.floatArrayOf(0f, 1f, 2f, 3f, 4f, 5f, 6f, 7f, 8f)
        for (i in 1 .. children.size) {        // 倒序依次取item列中的所有item。将所有item的边界做并交处理。
            val item = children[children.size - i]
            if (!item.isVisible) {      // 如果对象不可见
                continue
            }
            try {
                // 保存画布状态
                canvas.save()
                // item位移到指定位置绘制
                canvas.translate(item.pos.x, item.pos.y)
                canvas.scale(item.scale.x,item.scale.y)
                canvas.rotate(item.rotate)

                if (!item.isTransform) {
                    val matrix = canvas.matrix
                    matrix.getValues(src)
                    item._inverseScale = 1.0f/src[0]
                    canvas.scale(item._inverseScale,item._inverseScale)
                }
                // 设置绘制区域
                // canvas.clipRect(item.boundingRect())
                // 绘制item
                item.onPaint(canvas, rect)
                // 恢复画布状态
                canvas.restore()
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }
        return
    } // Function paint()

    /**
     * Item 绘制操作
     *
     * @param painter    绘制对象
     */
    open fun onDraw(canvas : Canvas) {

    }

    /**
     * 获得焦点变更回调
     */
    open fun onGetFocus() {
        // DO NOTHING
        return
    } // Function onGetFocus()

    /**
     * 失去焦点变更回调
     */
    open fun onLostFocus() {
        // DO NOTHING
        return
    } // Function onLostFocus()

    /**
     * 用户按下触发.
     *
     * @param   e       按下手势事件。
     * @return  如果事件被处理返回 true , 否则返回 false 。
     */
    open fun onDown(e: SMotionEvent): Boolean {
        for (item in children) {
            if (!item.isVisible) {      // 如果对象不可见
                continue
            }
            val ce = toChildMotionEvent(item, e)
            if (item.contains(ce.x, ce.y)                    // 如果点在子项目上
                    && item.onDown(ce)) {                    // 且子项目处理了事件
                return true
            }
        }

        if (flags.contains(SGraphyItemFlag.ItemIsFocusable)) {    // 如果是可获得焦点Item
            focus = true
            return true
        }

        return false
    } // Function onDown()

    /**
     * 用户释放触发.
     *
     * @param   e       释放手势事件。
     * @return  如果事件被处理返回 true , 否则返回 false 。
     */
    open fun onSingleTapUp(e: SMotionEvent): Boolean {
        Log.d(TAG, "onSingleTapUp： releaseItem")
        releaseItem()
        Log.e("手势scene",e.toJson())
        for (item in children) {
            if (!item.isVisible) {      // 如果对象不可见
                continue
            }
            val ce = toChildMotionEvent(item, e)
            if (item.contains(ce.x, ce.y)                                   // 如果点在子项目上
                    && item.onSingleTapUp(ce)) {                            // 且子项目处理了事件
                return true
            }
        }

        return false
    } // Function onSingleTapUp()

    /**
     * 用户点击触发
     *
     * @param   e       手势事件。
     * @return  如果事件被处理返回 true , 否则返回 false 。
     */
    open fun onShowPress(e: SMotionEvent): Boolean {
        Log.d(TAG, "onShowPress： releaseItem")
        for (item in children) {
            if (!item.isVisible) {      // 如果对象不可见
                continue
            }
            val ce = toChildMotionEvent(item, e)
            if (item.contains(ce.x, ce.y)                                   // 如果点在子项目上
                    && item.onShowPress(ce)) {                              // 且子项目处理了事件
                return true
            }
        }

        return false
    } // Function onShowPress()

    /**
     * 长按触摸屏超过一定时间。
     *
     * @param   e       长按手势事件。
     * @return  如果事件被处理返回 true , 否则返回 false 。
     */
    open fun onLongPress(e: SMotionEvent): Boolean {
        Log.d(TAG, "onLongPress： releaseItem")
        releaseItem()

        for (item in children) {
            if (!item.isVisible) {      // 如果对象不可见
                continue
            }
            val ce = toChildMotionEvent(item, e)
            if (item.contains(ce.x, ce.y)                                   // 如果点在子项目上
                    && item.onLongPress(ce)) {                              // 且子项目处理了事件
                return true
            }
        }

        return false
    } // Function onLoagPress()

    /**
     * 用户按下触摸屏，并拖动。
     *
     * @param   e1          第1个 ACTION_DOWN 手势事件
     * @param   e2          最后一个 ACTION_MOVE 手势事件
     * @param   distanceX   X轴上的移动距离，单位：像素
     * @param   distanceY   Y轴上的移动距离，单位：像素
     * @return  如果事件被处理返回 true , 否则返回 false 。
     */
    open fun onScroll(e1: SMotionEvent, e2: SMotionEvent, distanceX: Float, distanceY: Float): Boolean {
        if (flags.contains(SGraphyItemFlag.ItemIsMovable) && currentMoveItem === this) {
            moveTo(pos.x - distanceX * scale.x, pos.y - distanceY * scale.y)
            return true
        }

        for (item in children) {
            if (!item.isVisible) {      // 如果对象不可见
                continue
            }
            val ce1 = toChildMotionEvent(item, e1)
            val ce2 = toChildMotionEvent(item, e2)
            // 如果点在子项目上且子项目目处理了事件
            if (item.contains(ce2.x, ce2.y)              // 如果点在子项目上
                    && item.onScroll(ce1, ce2, distanceX / item.scale.x, distanceY / item.scale.y)) {   // 且子项目处理了事件
                return true
            }
        }

        if (flags.contains(SGraphyItemFlag.ItemIsMovable) && null == currentMoveItem) {    // 如果是可移动Item
            grabItem(this)
            currentMoveItem = this
            return true
        }

        return false
    } // Function onScroll()

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

        for (item in children) {
            if (!item.isVisible) {      // 如果对象不可见
                continue
            }
            val ce1 = toChildMotionEvent(item, e1)
            val ce2 = toChildMotionEvent(item, e2)
            // 如果点在子项目上且子项目目处理了事件
            if (item.contains(ce2.x, ce2.y)              // 如果点在子项目上
                    && item.onFling(ce1, ce2, velocityX / item.scale.x, velocityY / item.scale.y)) {   // 且子项目处理了事件
                return true
            }
        }

        return false
    } // Function onFling()

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 私有方法
    /**
     * 锁定item
     *
     * @param   item    被锁定的item
     */
    private fun grabItem(item: SGraphyItem) {
        Log.d(TAG, "grabItem")
        isSelected = true
        scene!!.grabItem = item
        return
    } // Function grabItem

    /**
     * 释放被锁定的item
     */
    private fun releaseItem() {
        isSelected = false
        scene!!.grabItem = null
        return
    } // Function grabItem

    /**
     * 场景对象到 item 对象的转换矩阵
     *
     * @return 转换矩阵
     */
    fun scene2itemMattrix() : Matrix{
        val m = Matrix()
        val list = this.itemPath()
        for ( item in list) {
            m.postTranslate(item.pos.x, item.pos.y);
            m.postScale(item.scale.x, item.scale.y);
            m.postRotate(item.rotate)
            // 如果不进行变形处理，则取消 painter 的变型操作
            if (!item.isTransform){
                m.postScale(item._inverseScale, item._inverseScale);
            }
        }
        return m
    }


} // Class SGraphyItem