/*
 * ********************************************************************************************************************
 *
 *               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-2018.  斯伯坦机器人世界
 *              ,  XMW  ..
 *                  r                                                                     All rights reserved.
 *
 * ********************************************************************************************************************
 */
import './SCanvas'
import SPoint from './types/SPoint'
import SMouseEvent from './SMouseEvent'

const bindEvent = Symbol('bindEvent')
const toSceneMotionEvent = Symbol('toSceneMotionEvent')
    /**
     * Graphy图形引擎视图类
     *
     * @author  Andy
     */
export default class SGraphyView {
    /**
     * 构造函数
     *
     * @param   id    Canvas对象ID
     */
    constructor(id, scene) {
            this.canvasView = document.getElementById(id)
            this.canvas = this.canvasView.getContext('2d')
            this[bindEvent](this.canvasView)
            this.scene = scene
            this.pos = new SPoint(10, 0)
            this.scale = 1
            this.minScale = 0.004
            this.maxScale = 0.5
            this._midKeyX = 0
            this._midKeyY = 0
            this.wheelZoom = 1.05

            window.requestAnimationFrame(this.onDraw.bind(this))
        } // Function constructor()

    /**
     * 绑定canvas事件
     *
     * @param   canvas      canvas对象
     * @private
     */
    [bindEvent](canvasView) {
        canvasView.onclick = this.onClick.bind(this)
        canvasView.ondblclick = this.onDbClick.bind(this)
        canvasView.onmousedown = this.onMouseDown.bind(this)
        canvasView.onmousemove = this.onMouseMove.bind(this)
        canvasView.onmouseup = this.onMouseUp.bind(this)
        canvasView.onmousewheel = this.onMouseWheel.bind(this)
        canvasView.onresize = this.onResize.bind(this)
    } // Function [bindEvent]()

    /**
     * 获取canvas的宽度  
     */

    get width() {
        return this.canvasView.width
    }

    get height() {
        return this.canvasView.height
    }

    /**
     * 将场景中的xy坐标转换成视图坐标。
     *
     * @param   x       场景中的横坐标
     * @param   y       场景中的纵坐标
     *
     * @return  SPoint
     */
    mapFromScene(x, y = null) {
            if (typeof(x) === 'object') { // 如果传入的是SPoint对象
                return new SPoint(x.x * this.scale + this.pos.x, x.y * this.scale + this.pos.y)
            }

            return new SPoint(x * this.scale + this.pos.x, y * this.scale + this.pos.y)
        } // Function mapFromScene()

    /**
     * 将item中的xy坐标转换成场景坐标。
     *
     * @param   x       item中的横坐标
     * @param   y       item中的纵坐标
     * @return  SPoint
     */
    mapToScene(x, y = null) {
            if (typeof(x) === 'object') { // 如果传入的是SPoint对象
                return new SPoint((x.x - this.pos.x) / this.scale, (x.y - this.pos.y) / this.scale)
            }

            return new SPoint((x - this.pos.x) / this.scale, (y - this.pos.y) / this.scale)
        } // Function mapToScene()

    /**
     * 缩放视图时计算视图的位置与缩放比例
     *
     * @param   zoom        缩放比例
     * @param   x0          缩放计算的中心点X坐标
     * @param   y0          缩放计算的中心点Y坐标
     */
    scaleByPoint(zoom, x0, y0) {
            let z = zoom
                /**
                 * 缩放比例在最小比例和最大比例范围内
                 */
            if (this.scale * zoom >= this.maxScale) { // 大于最大缩放比例
                z = this.maxScale / this.scale
                this.scale = this.maxScale
            } else if (this.scale * zoom <= this.minScale) { // 小于最小绽放比例
                z = this.minScale / this.scale
                this.scale = this.minScale
            } else {
                this.scale *= zoom
            }

            this.pos.x = x0 - (x0 - this.pos.x) * z
            this.pos.y = y0 - (y0 - this.pos.y) * z

            // EventBus.getDefault().post(SGraphyViewZoomEvent(this, scale))
            // EventBus.getDefault().post(SGraphyViewMoveEvent(this, pos.x, pos.y))
            // return
        } // Function scaleByPoint()
        // ===================================================================================================================
        // 事件
    onDraw() {
            this.canvas.save()
            this.canvas.clearRect(0, 0, this.canvasView.width, this.canvasView.height)
            this.canvas.restore()

            if (this.scene != null) {
                // 绘制背景
                this.canvas.save()
                this.scene.drawBackground(this.canvas)
                this.canvas.restore()

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

                // 绘制前景
                this.canvas.save()
                this.scene.drawForeground(this.canvas)
                this.canvas.restore()
            }

            window.requestAnimationFrame(this.onDraw.bind(this))
        } // Function onDraw()

    /**
     * 鼠标单击事件
     *
     * @param   e   保存事件参数
     */
    onClick(e) {
            if (this.scene != null) {
                let se = this[toSceneMotionEvent](e)
                this.scene.onClick(se)
            }
        } // Function onClick()

    /**
     * 鼠标双击事件
     *
     * @param   e   保存事件参数
     */
    onDbClick(e) {
            if (this.scene != null) {
                let ce = this[toSceneMotionEvent](e)
                this.scene.onDbClick(ce)
            }
        } // Function onDbClick()

    /**
     * 鼠标按下事件
     *
     * @param   e   保存事件参数
     */
    onMouseDown(e) {
            let se = new SMouseEvent(e)
            if (se.buttons & SMouseEvent.MIDDLE_BUTTON) { // 如果按下中键
                this._midKeyX = e.x
                this._midKeyY = e.y
            }

            if (this.scene != null) {
                let ce = this[toSceneMotionEvent](e)
                this.scene.onMouseDown(ce)
            }
        } // Function onMouseDown()

    /**
     * 鼠标移动事件
     *
     * @param   e   保存事件参数
     */
    onMouseMove(e) {
            let se = new SMouseEvent(e)
            if (se.buttons & SMouseEvent.MIDDLE_BUTTON) { // 如果按下中键，则移动视图
                this.pos.x += e.x - this._midKeyX
                this.pos.y += e.y - this._midKeyY
                this._midKeyX = e.x
                this._midKeyY = e.y
                return
            }
            if (this.scene != null) {
                let ce = this[toSceneMotionEvent](e)
                this.scene.onMouseMove(ce)
            }
        } // Function onMouseMove()

    /**
     * 释放鼠标事件
     *
     * @param   e   保存事件参数
     */
    onMouseUp(e) {
            if (this.scene != null) {
                let ce = this[toSceneMotionEvent](e)
                this.scene.onMouseUp(ce)
            }
        } // Function onMouseUp()

    /**
     * 鼠标滚轮事件
     *
     * @param   e   保存事件参数
     */
    onMouseWheel(e) {
            let se = new SMouseEvent(e)
            if (e.wheelDelta < 0) {
                this.scaleByPoint(1 / this.wheelZoom, se.x, se.y)
            } else {
                this.scaleByPoint(this.wheelZoom, se.x, se.y)
            }
        } // Function onMouseWheel()

    /**
     * View大小发生变化事件
     *
     * @param   e   保存事件参数
     */
    onResize(e) {} // Function onResize()

    /**
     * MotionEvent转场景对象MotionEvent
     *
     * @param   e       MotionEvent
     * @return  子对象MotionEvent
     */
    [toSceneMotionEvent](e) {
        let se = new SMouseEvent(e)
        se.x = (se.x - this.pos.x) / this.scale
        se.y = (se.y - this.pos.y) / this.scale
        return se
    } // Function toSceneMotionEvent()
} // Class SGraphyView