/**
 * Created by huhongxun
 * @desc 定制缩放控制条(仿百度)
 */
import Control from 'ol/control/Control'
import { BASE_CLASS_NAME } from '../constants'
import { create, getElement, getStyle, on, stopPropagation, preventDefault } from '../utils'
import { linear, easeOut } from 'ol/easing'
import View from 'ol/View'
import { rotate } from 'ol/coordinate'
import '../assets/scss/zoomSlider.css'
class ZoomSlider extends Control {
    constructor(options = {}) {
        const className = (options.className !== undefined ? options.className : 'ole-zoom-slider')

        /**
         * @private
         * @type {Element}
         */
        const element = create('div', (className + ' ' + BASE_CLASS_NAME.CLASS_UNSELECTABLE))
        const translateContent = create('div', ('ole-zoom-slider-translate-content' + ' ' + BASE_CLASS_NAME.CLASS_SELECTABLE), element)
        const silderContent = create('div', ('ole-zoom-slider-content' + ' ' + BASE_CLASS_NAME.CLASS_SELECTABLE), element)
        const translateN = create('div', ('ole-zoom-slider-button ole-zoom-slider-translate-n' + ' ' + BASE_CLASS_NAME.CLASS_SELECTABLE), translateContent)
        translateN.setAttribute('title', '向上平移');
        const translateS = create('div', ('ole-zoom-slider-button ole-zoom-slider-translate-s' + ' ' + BASE_CLASS_NAME.CLASS_SELECTABLE), translateContent)
        translateS.setAttribute('title', '向下平移');
        const translateW = create('div', ('ole-zoom-slider-button ole-zoom-slider-translate-w' + ' ' + BASE_CLASS_NAME.CLASS_SELECTABLE), translateContent)
        translateW.setAttribute('title', '向左平移');
        const translateE = create('div', ('ole-zoom-slider-button ole-zoom-slider-translate-e' + ' ' + BASE_CLASS_NAME.CLASS_SELECTABLE), translateContent)
        translateE.setAttribute('title', '向右平移');
        const zoomIn = create('div', ('ole-zoom-slider-zoom-in' + ' ' + BASE_CLASS_NAME.CLASS_SELECTABLE), silderContent)
        zoomIn.setAttribute('title', '放大')
        const zoomOut = create('div', ('ole-zoom-slider-zoom-out' + ' ' + BASE_CLASS_NAME.CLASS_SELECTABLE), silderContent)
        zoomOut.setAttribute('title', '缩小')
        const slider = create('div', ('ole-zoom-slider-zoom-slider' + ' ' + BASE_CLASS_NAME.CLASS_SELECTABLE), silderContent)
        create('div', ('slider-background-top' + ' ' + BASE_CLASS_NAME.CLASS_SELECTABLE), slider)
        const sliderBackgroundBottom = create('div', ('slider-background-bottom' + ' ' + BASE_CLASS_NAME.CLASS_SELECTABLE), slider)
        const sliderBackgroundMask = create('div', ('slider-background-mask' + ' ' + BASE_CLASS_NAME.CLASS_SELECTABLE), slider)
        sliderBackgroundMask.setAttribute('title', '缩放到此级别')
        const sliderBar = create('div', ('slider-bar' + ' ' + BASE_CLASS_NAME.CLASS_SELECTABLE), slider, 'slider-bar')
        sliderBar.setAttribute('title', '滑动缩放地图')
        const render = options['render'] ? options['render'] : ZoomSlider.render
        super({
                element: element,
                render: render,
                target: options['target']
            })
            /**
             * 当前分辨率
             * @type {undefined}
             * @private
             */
        this.currentResolution_ = undefined

        /**
         * 滑块默认方向（默认竖向）
         * @type {number}
         * @private
         */
        this.direction_ = ZoomSlider.Direction_.VERTICAL

        /**
         * 是否正在拖拽
         * @type {boolean}
         * @private
         */
        this.dragging_ = false

        /**
         * 高度限制
         * @type {number}
         * @private
         */
        this.heightLimit_ = 0

        /**
         * 宽度限制
         * @type {number}
         * @private
         */
        this.widthLimit_ = 0

        /**
         * 原始X
         * @type {null}
         * @private
         */
        this.previousX_ = null

        /**
         * 原始Y
         * @type {null}
         * @private
         */
        this.previousY_ = null

        /**
         * 计算出的视图大小（边框加边距）
         * @type {null}
         * @private
         */
        this.thumbSize_ = null

        /**
         * 滑块是否被初始化
         * @type {boolean}
         * @private
         */
        this.sliderInitialized_ = false

        /**
         * 动画过渡时延
         * @type {number}
         * @private
         */
        this.duration_ = options['duration'] !== undefined ? options['duration'] : 200
            /**
             * 视图限制
             * @type {{ANIMATING: number, INTERACTING: number}}
             */
        this.viewHint = {
            ANIMATING: 0,
            INTERACTING: 1
        }

        /**
         * @private
         * @type {number}
         */
        this.pixelDelta_ = options['pixelDelta'] !== undefined ? options['pixelDelta'] : 128
            /**
             * 滑块容器
             * @type {Element}
             */
        this.silderContent = silderContent

        this.sliderBackgroundBottom = sliderBackgroundBottom

        on(translateN, 'click', this.handletranslateClick_.bind(this, 'translateN'), this)
        on(translateS, 'click', this.handletranslateClick_.bind(this, 'translateS'), this)
        on(translateW, 'click', this.handletranslateClick_.bind(this, 'translateW'), this)
        on(translateE, 'click', this.handletranslateClick_.bind(this, 'translateE'), this)
        on(zoomIn, 'click', this.handleZoomClick_.bind(this, 1), this)
        on(zoomOut, 'click', this.handleZoomClick_.bind(this, -1), this)
        on(silderContent, 'pointerdown', this.handleDraggerStart_, this)
        on(silderContent, 'pointermove', this.handleDraggerDrag_, this)
        on(silderContent, 'pointerup', this.handleDraggerEnd_, this)
        on(silderContent, 'click', this.handleContainerClick_, this)
        on(sliderBar, 'click', stopPropagation)
    }

    /**
     * 处理缩放点击
     * @param delta
     * @param event
     * @private
     */
    handleZoomClick_(delta, event) {
        preventDefault(event)
        this.zoomByDelta_(delta)
    }

    /**
     * 处理平移点击事件
     * @param type
     * @param event
     * @private
     */
    handletranslateClick_(type, event) {
        preventDefault(event)
        let view = this.getMap().getView()
        let mapUnitsDelta = view.getResolution() * this.pixelDelta_
        let [deltaX, deltaY] = [0, 0]
        switch (type) {
            case 'translateN':
                deltaY = mapUnitsDelta
                break
            case 'translateS':
                deltaY = -mapUnitsDelta
                break
            case 'translateW':
                deltaX = mapUnitsDelta
                break
            case 'translateE':
                deltaX = -mapUnitsDelta
                break
        }
        let delta = [deltaX, deltaY]
        rotate(delta, view.getRotation())
        this.pan(view, delta, this.duration_)
    }

    /**
     * 平移地图
     * @param view
     * @param delta
     * @param optDuration
     */
    pan(view, delta, optDuration) {
        let currentCenter = view.getCenter()
        if (currentCenter) {
            let center = view.constrainCenter(
                [currentCenter[0] + delta[0], currentCenter[1] + delta[1]])
            if (optDuration) {
                view.animate({
                    duration: optDuration,
                    easing: linear,
                    center: center
                })
            } else {
                view.setCenter(center)
            }
        }
    }

    /**
     * @param {number} delta Zoom delta.
     * @private
     */
    zoomByDelta_(delta) {
        let view = this.getMap().getView()
        if (view && view instanceof View) {
            let currentResolution = view.getResolution()
            if (currentResolution) {
                let newResolution = view.constrainResolution(currentResolution, delta)
                if (this.duration_ > 0) {
                    if (view.getAnimating()) {
                        view.cancelAnimations()
                    }
                    view.animate({
                        resolution: newResolution,
                        duration: this.duration_,
                        easing: easeOut
                    })
                } else {
                    view.setResolution(newResolution)
                }
            }
        }
    }

    /**
     * 设置地图
     * @param map
     */
    setMap(map) {
        super.setMap.call(this, map)
        if (map) {
            map.render()
        }
    }

    /**
     * @inheritDoc
     */
    disposeInternal() {
        on(this.silderContent, 'pointercancel', function(event) {}, this)
        super.disposeInternal.call(this)
    }

    /**
     * 初始化滑块元素
     * @private
     */
    initSlider_() {
        let container = this.silderContent
        let containerSize = {
            width: container.offsetWidth,
            height: container.offsetHeight
        }
        let thumb = getElement('slider-bar')
        let thumbWidth = thumb.offsetWidth +
            parseFloat(getStyle(thumb, 'marginRight')) +
            parseFloat(getStyle(thumb, 'marginLeft'))
        let thumbHeight = thumb.offsetHeight +
            parseFloat(getStyle(thumb, 'marginTop')) +
            parseFloat(getStyle(thumb, 'marginBottom'))
        this.thumbSize_ = [thumbWidth, thumbHeight]
        if (containerSize.width > containerSize.height) {
            this.direction_ = ZoomSlider.Direction_.HORIZONTAL
            this.widthLimit_ = containerSize.width - thumbWidth
        } else {
            this.direction_ = ZoomSlider.Direction_.VERTICAL
            this.heightLimit_ = containerSize.height - thumbHeight
        }
        this.sliderInitialized_ = true
    }

    /**
     * 容器点击事件处理
     * @param event
     * @private
     */
    handleContainerClick_(event) {
        let view = this.getMap().getView()
        let relativePosition = this.getRelativePosition_(event.offsetX - this.thumbSize_[0] / 2, event.offsetY - this.thumbSize_[1] / 2)
        let resolution = this.getResolutionForPosition_(relativePosition)
        view.animate({
            resolution: view.constrainResolution(resolution),
            duration: this.duration_,
            easing: easeOut
        })
    }

    /**
     * 处理拖拽
     * @param event
     * @private
     */
    handleDraggerStart_(event) {
        if (!this.dragging_ && event.target === getElement('slider-bar')) {
            this.previousX_ = event.clientX
            this.previousY_ = event.clientY
            this.dragging_ = true
        }
    }

    /**
     * 处理拖动事件
     * @param event
     * @private
     */
    handleDraggerDrag_(event) {
        if (this.dragging_) {
            let element = getElement('slider-bar')
            let deltaX = event.clientX - this.previousX_ + parseInt(element.style.left, 10)
            let deltaY = event.clientY - this.previousY_ + parseInt(element.style.top, 10)
            let relativePosition = this.getRelativePosition_(deltaX, deltaY)
            this.currentResolution_ = this.getResolutionForPosition_(relativePosition)
            this.getMap().getView().setResolution(this.currentResolution_)
            this.setThumbPosition_(this.currentResolution_)
            this.previousX_ = event.clientX
            this.previousY_ = event.clientY
        }
    }

    /**
     * 处理拖拽结束事件
     * @param event
     * @private
     */
    handleDraggerEnd_(event) {
        if (this.dragging_) {
            let view = this.getMap().getView()
            view.animate({
                resolution: view.constrainResolution(this.currentResolution_),
                duration: this.duration_,
                easing: easeOut
            })
            this.dragging_ = false
            this.previousX_ = undefined
            this.previousY_ = undefined
        }
    }

    /**
     * 计算指针位置（相对于父容器）
     * @param res
     * @private
     */
    setThumbPosition_(res) {
        let position = this.getPositionForResolution_(res)
        let thumb = getElement('slider-bar')
        if (this.direction_ === ZoomSlider.Direction_.HORIZONTAL) {
            thumb.style.left = this.widthLimit_ * position + 'px'
            this.sliderBackgroundBottom.style.width = this.widthLimit_ - (this.widthLimit_ * position - 5) + 'px'
        } else {
            thumb.style.top = this.heightLimit_ * position + 'px'
            this.sliderBackgroundBottom.style.height = this.heightLimit_ - (this.heightLimit_ * position - 5) + 'px'
        }
    }

    /**
     * 给出x和y偏移量的指针的相对位置
     * @param x
     * @param y
     * @returns {number}
     * @private
     */
    getRelativePosition_(x, y) {
        let amount;
        if (this.direction_ === ZoomSlider.Direction_.HORIZONTAL) {
            amount = x / this.widthLimit_
        } else {
            amount = y / this.heightLimit_
        }
        return Math.min(Math.max(amount, 0), 1)
    }

    /**
     * 计算相关分辨率
     * @param position
     * @returns {number}
     * @private
     */
    getResolutionForPosition_(position) {
        let view = this.getMap().getView()
        if (view && view instanceof View) {
            return this.getResolutionForValueFunction(1 - position)
        }
    }

    /**
     * 获取值
     * @param resolution
     * @param optPower
     * @returns {number}
     */
    getValueForResolutionFunction(resolution, optPower) {
        let power = optPower || 2;
        let view = this.getMap().getView()
        let maxResolution = view.getMaxResolution()
        let minResolution = view.getMinResolution()
        let max = Math.log(maxResolution / minResolution) / Math.log(power)
        return ((Math.log(maxResolution / resolution) / Math.log(power)) / max)
    }

    /**
     * 获取分辨率
     * @param value
     * @param optPower
     * @returns {number}
     */
    getResolutionForValueFunction(value, optPower) {
        let power = optPower || 2
        let view = this.getMap().getView()
        let maxResolution = view.getMaxResolution()
        let minResolution = view.getMinResolution()
        let max = Math.log(maxResolution / minResolution) / Math.log(power)
        return (maxResolution / Math.pow(power, value * max))
    }

    /**
     * 计算相关位置
     * @param res
     * @returns {number}
     * @private
     */
    getPositionForResolution_(res) {
        let view = this.getMap().getView()
        if (view && view instanceof View) {
            return (1 - this.getValueForResolutionFunction(res))
        }
    }
}
ZoomSlider.render = function(mapEvent) {
    if (!mapEvent.frameState) {
        return
    }
    if (!this.sliderInitialized_) {
        this.initSlider_()
    }
    let res = mapEvent.frameState.viewState.resolution
    if (res !== this.currentResolution_) {
        this.currentResolution_ = res
        this.setThumbPosition_(res)
    }
}
ZoomSlider.Direction_ = {
    VERTICAL: 0,
    HORIZONTAL: 1
}
export default ZoomSlider