/**
 * Created by huhongxun
 * @desc 自定义鹰眼控件
 */
import '../assets/scss/overviewMap.css'
import Control from 'ol/control/Control'
import { BASE_CLASS_NAME, OVERVIEWMAP } from '../constants'
import { on, off, setStyle, create } from '../utils'
import Overlay from 'ol/Overlay'
import Collection from 'ol/Collection'
import Map from 'ol/Map'
import { rotate, add } from 'ol/coordinate'
import { getTopLeft, getBottomRight, containsExtent, getBottomLeft, getTopRight } from 'ol/extent'
class OverviewMap extends Control {
    constructor(options = {}) {
        const className = options.className !== undefined ? options.className : 'ole-overview-map';

        const element = create('div', className + ' ' + BASE_CLASS_NAME.CLASS_UNSELECTABLE);

        const render = options.render ? options.render : OverviewMap.render
        super({
            element: element,
            render: render,
            target: options.target
        });

        /**
         * @type {boolean}
         * @private
         */
        this.collapsed_ = options.collapsed !== undefined ? options.collapsed : true;

        /**
         * @private
         * @type {boolean}
         */
        this.collapsible_ = options.collapsible !== undefined ? options.collapsible : true;

        if (!this.collapsible_) {
            this.collapsed_ = false
        }

        /**
         * @type {Element}
         * @private
         */
        this.ovmapDiv_ = create('div', 'ole-overview-map-target', element)

        /**
         * 收起按钮
         * @type {Element}
         * @private
         */
        if (this.collapsible_) {
            this.collapsElement_ = create('div', 'ole-overview-map-button', element)
            on(this.collapsElement_, 'click', this.handleClick_, this)
        }

        /**
         * @type {ol.Map}
         * @private
         */
        this.ovmap_ = new Map({
            controls: new Collection(),
            interactions: new Collection(),
            view: options.view
        });

        this.addBoxControl_()
    }

    addOptionLayers_(options) {
        let map = this.ovmap_;
        if (options.layers) {
            options.layers.forEach(function(layer) {
                map.addLayer(layer);
            }, this)
        }
    }

    /**
     * handle move event
     * @param event
     * @private
     */
    move_(event) {
        const overlayBox = this.boxOverlay_.getElement();
        const coordinates = this.ovmap_.getEventCoordinate(OverviewMap.computeDesiredMousePosition(event, overlayBox));
        this.boxOverlay_.setPosition(coordinates);
    }

    /**
     * handle move end events
     * @param event
     * @private
     */
    endMoving_(event) {
        const coordinates = this.ovmap_.getEventCoordinate(event);
        this.getMap().getView().setCenter(coordinates);
        off(window, 'mousemove', this.move_, this);
        off(window, 'mouseup', this.endMoving_, this);
    }

    /**
     * add event handle
     * @private
     */
    addEvent_() {
        on(window, 'mousemove', this.move_, this);
        on(window, 'mouseup', this.endMoving_, this);
    }

    /**
     * add box handle
     * @private
     */
    addBoxControl_() {
        const box = create('div', 'ole-overview-map-box');
        on(box, 'mousedown', this.addEvent_, this);
        this.boxOverlay_ = new Overlay({
            position: [0, 0],
            positioning: 'bottom-left',
            element: box
        });
        this.ovmap_.addOverlay(this.boxOverlay_);
    }

    /**
     * set map
     * @param map
     */
    setMap(map) {
        let oldMap = this.getMap();
        if (map === oldMap) {
            return
        }
        if (oldMap) {
            let oldView = oldMap.getView();
            if (oldView) {
                this.unbindView_(oldView);
            }
            this.ovmap_.setTarget(null);
        }
        super.setMap.call(this, map);
        if (map) {
            this.ovmap_.setTarget(this.ovmapDiv_);
            map.on('propertychange', this.handleMapPropertyChange_, this);
            if (this.ovmap_.getLayers().getLength() === 0) {
                this.ovmap_.setLayerGroup(map.getLayerGroup());
            }
            let view = map.getView();
            if (view) {
                this.bindView_(view);
                if (this.isDef(view)) {
                    this.ovmap_.updateSize();
                    this.resetExtent_();
                }
            }
        }
    }

    /**
     * check view is defined
     * @param view
     * @returns {boolean}
     */
    isDef(view) {
        return !!view.getCenter() && view.getResolution() !== undefined
    }

    /**
     * handle map change
     * @param event
     * @private
     */
    handleMapPropertyChange_(event) {
        if (event.key === 'view') {
            let oldView = (event.oldValue);
            if (oldView) {
                this.unbindView_(oldView);
            }
            let newView = this.getMap().getView();
            this.bindView_(newView);
        }
    }

    /**
     * bind view event
     * @param view
     * @private
     */
    bindView_(view) {
        view.on('change:rotation', this.handleRotationChanged_, this)
    }

    /**
     * unbind view events
     * @param view
     * @private
     */
    unbindView_(view) {
        view.un('change:rotation', this.handleRotationChanged_, this);
    }

    /**
     * handle view rotation change
     * @private
     */
    handleRotationChanged_() {
        this.ovmap_.getView().setRotation(this.getMap().getView().getRotation())
    }

    /**
     * 重新调整范围，避免过大或者过小
     * @private
     */
    validateExtent_() {
        let map = this.getMap()
        let ovmap = this.ovmap_
        let mapSize = /** @type {ol.Size} */ (map.getSize())
        let view = map.getView()
        let extent = view.calculateExtent(mapSize)
        let ovmapSize = /** @type {ol.Size} */ (ovmap.getSize())
        let ovview = ovmap.getView()
        let ovextent = ovview.calculateExtent(ovmapSize)
        let topLeftPixel =
            ovmap.getPixelFromCoordinate(getTopLeft(extent))
        let bottomRightPixel =
            ovmap.getPixelFromCoordinate(getBottomRight(extent))
        let boxWidth = Math.abs(topLeftPixel[0] - bottomRightPixel[0])
        let boxHeight = Math.abs(topLeftPixel[1] - bottomRightPixel[1])
        let ovmapWidth = ovmapSize[0]
        let ovmapHeight = ovmapSize[1]
        if (boxWidth < ovmapWidth * OVERVIEWMAP.MIN_RATIO ||
            boxHeight < ovmapHeight * OVERVIEWMAP.MIN_RATIO ||
            boxWidth > ovmapWidth * OVERVIEWMAP.MAX_RATIO ||
            boxHeight > ovmapHeight * OVERVIEWMAP.MAX_RATIO) {
            this.resetExtent_()
        } else if (!containsExtent(ovextent, extent)) {
            this.recenter_()
        }
    }

    /**
     * reset extent
     * @private
     */
    resetExtent_() {
        if (OVERVIEWMAP.MAX_RATIO === 0 || OVERVIEWMAP.MIN_RATIO === 0) {
            return
        }
        let map = this.getMap()
        let ovmap = this.ovmap_
        let mapSize = /** @type {ol.Size} */ (map.getSize())
        let view = map.getView()
        let extent = view.calculateExtent(mapSize)
        let ovview = ovmap.getView()
        let steps = Math.log(OVERVIEWMAP.MAX_RATIO / OVERVIEWMAP.MIN_RATIO) / Math.LN2
        let ratio = 1 / (Math.pow(2, steps / 2) * OVERVIEWMAP.MIN_RATIO)
        this.scaleFromCenter(extent, ratio)
        ovview.fit(extent)
    }

    /**
     * 计算中心点的缩放
     * @param extent
     * @param value
     */
    scaleFromCenter(extent, value) {
        let deltaX = ((extent[2] - extent[0]) / 2) * (value - 1)
        let deltaY = ((extent[3] - extent[1]) / 2) * (value - 1)
        extent[0] -= deltaX
        extent[2] += deltaX
        extent[1] -= deltaY
        extent[3] += deltaY
    }

    /**
     * reset center
     * @private
     */
    recenter_() {
        let map = this.getMap()
        let ovmap = this.ovmap_
        let view = map.getView()
        let ovview = ovmap.getView()
        ovview.setCenter(view.getCenter())
    }

    /**
     * Update the box using the main map extent
     * @private
     */
    updateBox_() {
        let map = this.getMap();
        let ovmap = this.ovmap_;
        let mapSize = /** @type {ol.Size} */ (map.getSize());
        let view = map.getView();
        let ovview = ovmap.getView();
        let rotation = view.getRotation();
        let overlay = this.boxOverlay_;
        let box = this.boxOverlay_.getElement();
        let extent = view.calculateExtent(mapSize);
        let ovresolution = ovview.getResolution();
        let bottomLeft = getBottomLeft(extent);
        let topRight = getTopRight(extent);
        let rotateBottomLeft = this.calculateCoordinateRotate_(rotation, bottomLeft);
        overlay.setPosition(rotateBottomLeft);
        if (box) {
            setStyle(box, {
                width: Math.abs((bottomLeft[0] - topRight[0]) / ovresolution) + 'px',
                height: Math.abs((topRight[1] - bottomLeft[1]) / ovresolution) + 'px'
            });
        }
    }

    /**
     * 计算坐标角度
     * @param rotation
     * @param coordinate
     * @returns {*}
     * @private
     */
    calculateCoordinateRotate_(rotation, coordinate) {
        let coordinateRotate;
        const map = this.getMap();
        const view = map.getView();
        let currentCenter = view.getCenter();
        if (currentCenter) {
            coordinateRotate = [
                coordinate[0] - currentCenter[0],
                coordinate[1] - currentCenter[1]
            ];
            rotate(coordinateRotate, rotation);
            add(coordinateRotate, currentCenter);
        }
        return coordinateRotate;
    }

    /**
     * 处理点击事件
     * @param event
     * @private
     */
    handleClick_(event) {
        event.preventDefault();
        this.handleToggle_(event);
    }

    /**
     * @private
     */
    handleToggle_(event) {
        if (this.collapsed_) {
            this.collapsed_ = false;
            event.target.style.backgroundPosition = '-40px -405px';
            this.element.style.width = '17px';
            this.element.style.height = '17px';
        } else {
            this.collapsed_ = true;
            event.target.style.backgroundPosition = '-40px -386px';
            this.element.style.width = '120px';
            this.element.style.height = '120px';
        }
        let ovmap = this.ovmap_;
        if (!this.collapsed_ && !ovmap) {
            ovmap.updateSize();
            this.resetExtent_();
            ovmap.once('postrender', this.updateBox_, this)
        }
    }

    /**
     * 返回鹰眼是否可折叠
     * @returns {*|boolean}
     */
    getCollapsible() {
        return this.collapsible_;
    }

    /**
     * 设置鹰眼是否可折叠
     * @param collapsible
     */
    setCollapsible(collapsible) {
        if (this.collapsible_ === collapsible) {
            return
        }
        this.collapsible_ = collapsible
        if (!collapsible && this.collapsed_) {
            this.handleToggle_();
        }
    }

    /**
     * 设置鹰眼收起状态
     * @param collapsed
     */
    setCollapsed(collapsed) {
        if (!this.collapsible_ || this.collapsed_ === collapsed) {
            return
        }
        this.handleToggle_()
    }

    /**
     * 判断鹰眼是否收起
     * @returns {boolean|*}
     */
    getCollapsed() {
        return this.collapsed_
    }

    /**
     * 返回当前鹰眼地图
     * @returns {ol.Map}
     */
    getOverviewMap() {
        return this.ovmap_
    }
}
OverviewMap.render = function(mapEvent) {
        this.validateExtent_();
        this.updateBox_();
    }
    /**
     * 计算鼠标位置
     * @param mousePosition
     * @param overlayBox
     * @returns {{clientX: number, clientY: *}}
     */
OverviewMap.computeDesiredMousePosition = function(mousePosition, overlayBox) {
    return {
        clientX: mousePosition.clientX - (overlayBox.offsetWidth / 2),
        clientY: mousePosition.clientY + (overlayBox.offsetHeight / 2)
    }
}
export default OverviewMap