'use strict';

Object.defineProperty(exports, '__esModule', { value: true });

var three = require('three');
var elementResizeDetectorMaker = require('element-resize-detector');
var lodashEs = require('lodash-es');
var CameraControls = require('camera-controls');
var Stats = require('stats.js');
var mitt = require('mitt');
var shared = require('@ktmap/shared');

function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }

var elementResizeDetectorMaker__default = /*#__PURE__*/_interopDefaultLegacy(elementResizeDetectorMaker);
var CameraControls__default = /*#__PURE__*/_interopDefaultLegacy(CameraControls);
var Stats__default = /*#__PURE__*/_interopDefaultLegacy(Stats);
var mitt__default = /*#__PURE__*/_interopDefaultLegacy(mitt);

const _v3A = new three.Vector3();
class MyCameraControls extends CameraControls__default["default"] {
    constructor() {
        super(...arguments);
        Object.defineProperty(this, "truckYDisabled", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: false
        });
        // @ts-ignore 重写 CameraControls 方法
        Object.defineProperty(this, "_truckInternal", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: (deltaX, deltaY, dragToOffset) => {
                const _this = this;
                const offset = _v3A.copy(_this._camera.position).sub(_this._target);
                // @ts-ignore
                const fov = _this._camera.getEffectiveFOV() * three.MathUtils.DEG2RAD;
                const targetDistance = offset.length() * Math.tan(fov * 0.5);
                const truckX = (_this.truckSpeed * deltaX * targetDistance) / _this._elementRect.width;
                const pedestalY = (_this.truckSpeed * deltaY * targetDistance) / _this._elementRect.width;
                if (_this.verticalDragToForward) {
                    dragToOffset
                        ? _this.setFocalOffset(_this._focalOffsetEnd.x + truckX, _this._focalOffsetEnd.y, _this._focalOffsetEnd.z, true)
                        : _this.truck(truckX, 0, true);
                    _this.forward(-pedestalY, true);
                }
                else {
                    dragToOffset
                        ? _this.setFocalOffset(_this._focalOffsetEnd.x + truckX, _this._focalOffsetEnd.y + pedestalY, _this._focalOffsetEnd.z, true)
                        : _this.truck(this.truckYDisabled ? 0 : truckX, pedestalY, true);
                }
                // const offset = _v3A.copy(this._camera.position).sub(this._target);
                // // half of the fov is center to top of screen
                // // @ts-ignore
                // const fov = this._camera.getEffectiveFOV() * MathUtils.DEG2RAD;
                // const targetDistance = offset.length() * Math.tan(fov * 0.5);
                // const truckX =
                //   (this.truckSpeed * deltaX * targetDistance) / this._elementRect.w;
                // const truckY =
                //   (this.truckSpeed * deltaY * targetDistance) / this._elementRect.w;
                // this.truck(this.truckYDisabled ? 0 : truckX, truckY, true);
            }
        });
    }
}

// import CameraControls from 'camera-controls';
// import { IObjectBaseConfig, IType } from './parkingLayer/types';
// let timer = 0
// export const reDraw = (time = 3000) => {
//   // @ts-ignore
//   CameraControls.viewChanged = true
//   clearTimeout(timer)
//   timer = setTimeout(() => {
//     // @ts-ignore
//     CameraControls.viewChanged = false
//   }, time)
// }
/**
 * @desc 射线拾取
 */
function getIntersects({ event, $map, scene, camera, objects }) {
    const { top, left } = $map.getBoundingClientRect();
    // 声明raycaster和mouse变量
    const raycaster = new three.Raycaster();
    const mouse = new three.Vector2();
    // 通过鼠标点击的位置计算出raycaster所需要的点的位置，以屏幕中心为原点，值的范围为-1到1.
    mouse.x = ((event.clientX - left) / $map.clientWidth) * 2 - 1;
    mouse.y = -((event.clientY - top) / $map.clientHeight) * 2 + 1;
    // 通过鼠标点的位置和当前相机的矩阵计算出raycaster
    raycaster.setFromCamera(mouse, camera);
    // 获取raycaster直线和所有模型相交的数组集合，注意：true表示返回的值会递归，找到更深层次的模型
    const intersects = raycaster.intersectObjects(objects || scene.children, true);
    // 2d 场景下会存在bug，就是选择点的时候，跑到中心点去了，数组第一个是对象 AxesHelper 导致的，这里进行过滤
    // intersects = intersects.filter(item => !(item.object instanceof AxesHelper))
    // if (filter) {
    //   intersects = filter(intersects)
    // }
    return intersects;
}
/**
 * @desc 射线拾取对象，获取世界坐标
 * objects 传入需要被射线拾取的物体。精准判断情况，不传入 objects， 获取被射线拾取的所有对象，然后再判断第一个对象是否被命中，即可判断当前是否点击了想要的对象。
 * @return 返回第一个点和所有被射线拾取的对象
 */
function getWorldPosition({ event, $map, scene, camera, objects }) {
    const intersects = getIntersects({ event, $map, scene, camera, objects });
    const points = intersects.map(item => item.point);
    const worldPosition = new three.Vector3();
    if (points.length > 0) {
        worldPosition.set(points[0].x, points[0].y, points[0].z);
        return { worldPosition, intersects };
    }
    return {};
}
/**
 * @desc 根据 name 获取对应的对象集合
 */
function getObjectsByName(name, parent) {
    // scene.getObjectByName 只会返回第一个
    // return scene.getObjectByName(name);
    // 不能深层遍历
    // return scene.children.filter(item => item.name === name);
    // 较优算法
    const arr = [];
    parent.traverse(item => {
        if (item.name === name)
            arr.push(item);
    });
    return arr;
}

function createStats(container) {
    var _a, _b;
    const stats = new Stats__default["default"]();
    stats.showPanel(0); // 0: fps, 1: ms, 2: mb, 3+: custom
    (_a = document.querySelector(`.${container}__stats-wrapper`)) === null || _a === void 0 ? void 0 : _a.remove();
    (_b = document.getElementById(container)) === null || _b === void 0 ? void 0 : _b.appendChild(stats.dom);
    stats.dom.className = `.${container}__stats-wrapper`;
    stats.dom.style.cssText =
        'position: absolute; bottom: 0px; right: 0px; cursor: pointer; opacity: 0.9; z-index: 10000;';
    return stats;
}

const subsetOfTHREE = {
    MOUSE: three.MOUSE,
    Vector2: three.Vector2,
    Vector3: three.Vector3,
    Vector4: three.Vector4,
    Quaternion: three.Quaternion,
    Matrix4: three.Matrix4,
    Spherical: three.Spherical,
    Box3: three.Box3,
    Sphere: three.Sphere,
    Raycaster: three.Raycaster,
    MathUtils: {
        DEG2RAD: three.MathUtils.DEG2RAD,
        clamp: three.MathUtils.clamp
    }
};
class _Map {
    constructor(container, { style = 'normal', showStats = false, viewMode = '3D' } = {}) {
        Object.defineProperty(this, "container", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: container
        });
        Object.defineProperty(this, "options", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
        Object.defineProperty(this, "stats", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
        Object.defineProperty(this, "scene", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
        Object.defineProperty(this, "camera", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
        Object.defineProperty(this, "$map", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
        Object.defineProperty(this, "renderer", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
        Object.defineProperty(this, "domElement", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
        Object.defineProperty(this, "updateSizeMap", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: new Map()
        });
        Object.defineProperty(this, "tweenGroupMap", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: new Map()
        });
        Object.defineProperty(this, "emitter", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: mitt__default["default"]()
        });
        Object.defineProperty(this, "timer", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: 0
        });
        Object.defineProperty(this, "viewChanged", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: false
        });
        Object.defineProperty(this, "cameraControls", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
        /**
         * 存储 map 的一些相关信息
         */
        Object.defineProperty(this, "info", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: {
                fitDistance: 0,
                boundingClientRect: {
                    x: 0,
                    y: 0,
                    left: 0,
                    top: 0,
                    right: 0,
                    bottom: 0,
                    width: 0,
                    height: 0
                }
            }
        });
        /**
         * @desc 重渲染，为了提高性能，往场景添加元素，需要调用 redraw
         * 箭头函数，保证 this 指向正确
         */
        Object.defineProperty(this, "reDraw", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: (time = 3000) => {
                this.viewChanged = true;
                clearTimeout(this.timer);
                this.timer = setTimeout(() => {
                    this.viewChanged = false;
                }, time);
            }
        });
        this.options = { style, showStats, viewMode };
        const { scene, camera, cameraControls, $map, renderer } = this.init();
        this.scene = scene;
        this.camera = camera;
        this.cameraControls = cameraControls;
        this.$map = $map;
        this.renderer = renderer;
        // 画布元素
        this.domElement = renderer.domElement;
        // 配置相机相关的参数
        this.reset();
        this.setShowStats(showStats);
        // #region 开始事件注册相关
        let isMousedonw = false;
        let time = 0;
        const onMouseDown = (ev) => {
            if (ev.button === 2)
                return;
            isMousedonw = true;
            time = +new Date();
        };
        const onMouseUp = (ev) => {
            if (!isMousedonw)
                return;
            const newTime = +new Date();
            if (newTime - time > 300) ;
            else {
                const { scene, camera, $map } = this;
                // 世界坐标相关
                const obj = getWorldPosition({
                    event: ev,
                    scene,
                    camera,
                    $map
                });
                // 如果有获取到屏幕坐标的话，默认会携带当前地图屏幕坐标，用于其他操作
                let screenPosition;
                if (obj.worldPosition) {
                    // TODO:这里没有必要使用 getScreenPostion ，直接使用鼠标的 clientX，clientY + $map 的 left top
                    screenPosition = shared.getScreenPostion({
                        worldPosition: obj.worldPosition,
                        $map,
                        scene,
                        camera
                    });
                }
                // @ts-ignore
                this.emitter.emit('click', Object.assign(Object.assign(Object.assign({}, ev), obj), { screenPosition }));
            }
            isMousedonw = false;
        };
        const onMouseMove = (ev) => {
            const { scene, camera, $map } = this;
            // 世界坐标相关
            const obj = getWorldPosition({
                event: ev,
                scene,
                camera,
                $map
            });
            // 如果有获取到屏幕坐标的话，默认会携带当前地图屏幕坐标，用于其他操作
            let screenPosition;
            if (obj.worldPosition) {
                screenPosition = shared.getScreenPostion({
                    worldPosition: obj.worldPosition,
                    $map,
                    scene,
                    camera
                });
            }
            // @ts-ignore
            this.emitter.emit('mousemove', Object.assign(Object.assign(Object.assign({}, ev), obj), { screenPosition }));
        };
        // 这里注册到 canvas 上面，防止弹窗点击事件冒泡，需要再进行一些额外的冒泡控制
        this.domElement.removeEventListener('mousedown', onMouseDown);
        this.domElement.removeEventListener('mouseup', onMouseUp);
        this.domElement.ownerDocument.removeEventListener('mousemove', onMouseMove);
        this.domElement.addEventListener('mousedown', onMouseDown);
        this.domElement.addEventListener('mouseup', onMouseUp);
        this.domElement.ownerDocument.addEventListener('mousemove', onMouseMove);
        // this.$map.removeEventListener('mousedown', onMouseDown);
        // this.$map.removeEventListener('mouseup', onMouseUp);
        // this.$map.ownerDocument.removeEventListener('mousemove', onMouseMove);
        // this.$map.addEventListener('mousedown', onMouseDown);
        // this.$map.addEventListener('mouseup', onMouseUp);
        // this.$map.ownerDocument.addEventListener('mousemove', onMouseMove);
        // #endregion
        console.log('map init success');
    }
    /**
     * 生成图片
     */
    generateImage(type = 'image/png', quality = 1) {
        // 获取canvas对象
        const canvas = this.renderer.domElement;
        const img = canvas.toDataURL(type, quality);
        return img;
    }
    getScene() {
        return this.scene;
    }
    /**
     * @desc 二封 scene.add
     * to do：
     * - 需要判断新增的 layer 是否重名，判断 name 属性
     */
    add(...objects) {
        for (const item of objects) {
            // 表示自定义对象
            if (!(item instanceof three.Object3D)) {
                // 📝 ElementType 是 @ktmap/plugins 扩展的，只要是继承了 Element 类的都有 __inject 和 __init 两个方法
                // @ts-ignore
                item.__inject(this);
                // @ts-ignore
                item.__init();
            }
            else {
                this.scene.add(item);
            }
        }
        // https://blog.csdn.net/sujun10/article/details/64924125
        // switch (obj?.constructor?.name) {
        // @ts-ignore
        // switch (obj?.NAME) {
        //   // case 'PoisLayer':
        //   //   obj = obj as PoisLayer;
        //   //   // 判断如果当前如果添加的是一个 marker 就需要添加到一个数组，后续会根据这个数组循环 updateSize
        //   //   // 注入需要的参数
        //   //   obj._inject(this);
        //   //   obj._init();
        //   //   this.updateSizeMap.set(obj, obj._updateSize.bind(obj));
        //   //   console.log('add-this.updateSizeMap :>> ', this.updateSizeMap);
        //   //   break;
        //   case 'ParkingLayer':
        //     obj = obj as ParkingLayer;
        //     // 判断如果当前如果添加的是一个 marker 就需要添加到一个数组，后续会根据这个数组循环 updateSize
        //     obj._inject(this);
        //     obj._init();
        //     this.updateSizeMap.set(obj, obj._updateSize.bind(obj));
        //     console.log('add-this.updateSizeMap :>> ', this.updateSizeMap);
        //     break;
        //   default:
        //     // @ts-ignore
        //     this.scene.add(...object);
        //     break;
        // }
        this.reDraw();
        // console.log('add-current-scene :>> ', this.scene);
        return this;
    }
    remove(...object) {
        // let obj = object[0];
        // // switch (obj?.constructor?.name) {
        // // @ts-ignore
        // switch (obj?.NAME) {
        //   case 'PoisLayer':
        //     obj = obj as PoisLayer;
        //     // 判断如果当前如果添加的是一个 marker 就需要添加到一个数组，后续会根据这个数组循环 updateSize
        //     this.updateSizeMap.delete(obj);
        //     console.log('delete-this.updateSizeMap :>> ', this.updateSizeMap);
        //     this.scene.remove(obj._group);
        //     break;
        //   default:
        //     // @ts-ignore
        //     this.scene.remove(...object);
        //     break;
        // }
        // this.reDraw();
        // console.log('add-current-scene :>> ', this.scene);
        // return this;
    }
    // private eventExtMap
    on(type, handler
    // ext?: Record<string, any>
    ) {
        // mitt 监听
        this.emitter.on(type, handler);
        // switch (type) {
        //   case 'click': {
        //     // 直接设置 click 和 cameraControls 的左键拖拽混乱，使用时间间隔判断，当前是否在拖拽，把 click 拆分
        //     this.domElement.addEventListener('mousedown', () => {});
        //     // this.domElement.addEventListener(
        //     //   'mouseup',
        //     //   this.onMouseUp.bind(this, ext)
        //     // );
        //     break;
        //   }
        //   case 'mousemove': {
        //     // this.domElement.addEventListener('mousemove', ev => {
        //     //   const { scene, camera, $map } = this;
        //     //   const obj = getWorldPosition({
        //     //     event: ev,
        //     //     scene,
        //     //     camera,
        //     //     $map,
        //     //     objects: ext?.objects?.()
        //     //   });
        //     //   // 如果有获取到屏幕坐标的话，默认会携带当前地图屏幕坐标，用于其他操作
        //     //   let screenPosition;
        //     //   if (obj.worldPosition) {
        //     //     screenPosition = getScreenPostion({
        //     //       worldPosition: obj.worldPosition,
        //     //       $map,
        //     //       scene,
        //     //       camera
        //     //     });
        //     //   }
        //     //   this.emitter.emit('mousemove', {
        //     //     ...ev,
        //     //     ...obj,
        //     //     screenPosition
        //     //   });
        //     // });
        //     break;
        //   }
        //   default:
        //     break;
        // }
        return this;
    }
    off(type, handler) {
        // switch (type) {
        //   case 'click':
        //     this.domElement.removeEventListener('mousedown', this.onMouseDown);
        //     // this.domElement.removeEventListener('mouseup', this.onMouseUp);
        //     break;
        //   default:
        //     break;
        // }
        this.emitter.off(type, handler);
        return this;
    }
    /**
     * @desc 初始化地图：场景，相机，灯光，渲染器，rendererResize，轨道控制器
     */
    init() {
        const $map = document.getElementById(this.container);
        // #region 场景
        const scene = new three.Scene();
        // #endregion
        // #region 相机
        const width = $map.clientWidth;
        const height = $map.clientHeight;
        const k = width / height;
        /**
         * 后面两个参数的概念，就像人的眼睛
         * 第三个参数想象成距离人的眼睛，多近才会看不到，比如 0.1，表示小于0.1，人才会看不到
         * 第四个参数想象成距离人的眼睛，多远才会看不到，比如 10000，表示大于10000，人才会看不到
         */
        // const camera = new THREE.PerspectiveCamera(45, k, 0.01, 83150);
        // 最后一个参数是要进行判断，还是说直接设置一个很大的值？？
        // const camera = new THREE.PerspectiveCamera(45, k, 0.01, 1000000);
        // const camera = new PerspectiveCamera(45, k, 0.01, Math.pow(10, 100));
        const camera = new three.PerspectiveCamera(45, k, 0.01, Math.pow(10, 100));
        // const camera = new PerspectiveCamera(45, k, 0.01, Math.pow(10, 100));
        // const camera = new PerspectiveCamera(45, k, Math.pow(0.01, 100), 100);
        // const camera = new THREE.PerspectiveCamera(45, k, 0.01, 2000);
        // 把z轴向上
        camera.up.x = 0;
        camera.up.y = 0;
        camera.up.z = 1;
        // 设置相机方向(指向的场景对象)
        // camera.position.set(0, -736903.9005224737, 425451.6653335355)
        camera.lookAt(scene.position);
        // #endregion
        // #region 灯光
        const light = new three.HemisphereLight('#ffffff', '#e7e7e7', 1.0);
        light.name = 'light';
        light.position.set(-100, -100, 0);
        scene.add(light);
        // #endregion
        // #region 渲染器
        const renderer = new three.WebGLRenderer({
            // 如果想保存three.js canvas画布上的信息，注意设置preserveDrawingBuffer
            preserveDrawingBuffer: true,
            // 抗锯齿，开启来会比较卡，按情况开启
            antialias: true,
            alpha: true,
            logarithmicDepthBuffer: true
        });
        renderer.setSize($map.clientWidth, $map.clientHeight);
        // 设置背景颜色
        renderer.setClearColor('#e7f2fe', 1);
        // renderer.setClearColor(0xd3df56, 1) // 设置背景颜色
        // 暗黑模式
        // renderer.setClearColor(THEME.dark.renderer.color, 1); //设置背景颜色
        renderer.setPixelRatio(window.devicePixelRatio);
        $map.appendChild(renderer.domElement);
        renderer.render(scene, camera);
        // #endregion
        // #region rendererResize
        const onWindowResize = lodashEs.debounce(() => {
            const boundingClientRect = $map.getBoundingClientRect();
            // ✨ 将 boundingClientRect 赋值到 map info 避免其他地方获取 $map 宽高导致重排，性能下降
            this.info.boundingClientRect = JSON.parse(JSON.stringify(boundingClientRect));
            const { width, height } = this.info.boundingClientRect;
            camera.aspect = width / height;
            camera.updateProjectionMatrix();
            renderer.setSize(width, height);
            this.reDraw();
        }, 100);
        const observer = elementResizeDetectorMaker__default["default"]();
        observer.listenTo($map, onWindowResize);
        // #endregion
        // #region 相机控制器
        const cameraControls = this.createCameraControls($map, scene, camera, renderer, () => {
            for (const [, value] of this.updateSizeMap) {
                value === null || value === void 0 ? void 0 : value();
            }
        }, this.tweenGroupMap);
        // #endregion
        // 地图渲染完毕
        // emitter.emit('complete')
        return { scene, camera, cameraControls, $map, renderer };
    }
    /**
     * @desc 相机控制器
     */
    createCameraControls($map, scene, camera, renderer, updateFn, tweenGroupMap) {
        MyCameraControls.install({ THREE: subsetOfTHREE });
        const clock = new three.Clock();
        // ❌
        // const controls = new MyCameraControls(camera, renderer.domElement);
        // ✨ 事件要注册到外层 map 上，否则会导致窗体不能进行 wheel 事件
        const controls = new MyCameraControls(camera, $map);
        const _this = this;
        (function anim() {
            var _a, _b;
            (_a = _this.stats) === null || _a === void 0 ? void 0 : _a.begin();
            // snip
            const delta = clock.getDelta();
            const hasControlsUpdated = controls.update(delta);
            requestAnimationFrame(anim);
            // you can skip this condition to render though
            // 不再把 viewChanged 放置 CameraControls 作为静态属性，这样子不能保证多例，因为静态属性会共享一个副本
            // if (hasControlsUpdated || CameraControls.viewChanged) {
            // //  只有相机被改变才触发
            // if (hasControlsUpdated) {
            //   updateFn();
            // }
            if (hasControlsUpdated || _this.viewChanged) {
                // 一些会受到相机改变影响的方法
                updateFn();
                renderer.render(scene, camera);
                // 一些会受到相机改变影响的方法，移动至 render 之前
                // updateFn();
            }
            // http://tweenjs.github.io/tween.js/docs/user_guide.html#controlling-groups-of-tweens
            // 补间动画，必须要加入 group 作用域，否则会影响到其他动画
            // TWEEN.update();
            for (const [k] of tweenGroupMap)
                k === null || k === void 0 ? void 0 : k.update();
            (_b = _this.stats) === null || _b === void 0 ? void 0 : _b.end();
            // requestAnimationFrame(anim);
        })();
        // renderer.render(scene, camera);
        return controls;
    }
    /**
     * @desc 恢复到相机的初始状态
     */
    reset() {
        const { cameraControls, camera } = this;
        // 休息阈值，默认 0.0025，调整会影响到 await 的过渡时长
        // controls.restThreshold = 0.0025;
        // controls.restThreshold = 0.01;
        // controls.restThreshold = 10;
        // 阻尼惯性，默认 0.05，调整会影响到 await 的过渡时长
        // cameraControls.dampingFactor = 0.05 * 2;
        cameraControls.dampingFactor = 1;
        // 拖动时的阻尼惯性，默认 0.25
        cameraControls.draggingDampingFactor = 0.25 * 2;
        // controls.azimuthRotateSpeed = 1 * 2;
        // controls.polarRotateSpeed = 1 * 2;
        // controls.dollySpeed = 1 * 1.5;
        cameraControls.dollySpeed = 1 * 1.5;
        // 为了2d精灵图世界转屏幕性能，这里提高地图的放大缩放阈值
        // cameraControls.dollySpeed = 1 * 3;
        // 如果为true，表示前后（远近）
        cameraControls.verticalDragToForward = true;
        // 上下
        cameraControls.minPolarAngle = 0;
        cameraControls.maxPolarAngle = Math.PI;
        // dolly
        cameraControls.minDistance = 0;
        cameraControls.maxDistance = Infinity;
        // 左右
        cameraControls.minAzimuthAngle = -Infinity;
        cameraControls.maxAzimuthAngle = Infinity;
        // 改变相机 fov
        camera.fov = 45;
        camera.updateProjectionMatrix();
        return this;
    }
    destroy() { }
    // /**
    //  * @desc 添加地图上的控件，如二三维切换器，缩放器等
    //  */
    // addControl(control: IControl) {
    //   control
    //     ._inject(this)
    //     // 初始化挂载
    //     ._init();
    //   return this;
    // }
    /**
     * @desc 添加地图上的辅助对象，如灯光线条或者
     */
    addHelper() {
        return this;
    }
    // #region 一些控制地图的方法
    // 皮肤变化
    setStyle(style) {
        // this.options.style = style
        return this;
    }
    setShowStats(showStats) {
        if (showStats) {
            this.stats = createStats(this.container);
            this.reDraw();
        }
        return this;
    }
    // 维度变化
    setViewMode(viewMode) {
        this.options.viewMode = viewMode;
        viewMode === '3D' ? this.fitToBox3d() : this.fitToBox2d();
        return this;
    }
    // mesh 获取当前楼层为基准
    fitToBox2d() {
        const _getTarget = () => {
            // const allLevelObjs = getObjectsByName(OBJECT_NAME.level, this.scene);
            const allLevelObjs = getObjectsByName('level', this.scene);
            // 最高楼层
            return allLevelObjs[allLevelObjs.length - 1];
        };
        // const levelObj = getCurrentLevelObj(unref(currentLotInfo).zIndex)
        const target = _getTarget();
        const { fitDistance, dollys } = shared.getDollys(target, this.cameraControls);
        const enableTransition = true;
        // 控制水平垂直旋转
        this.cameraControls.minPolarAngle =
            this.cameraControls.maxPolarAngle =
                this.cameraControls.minAzimuthAngle =
                    this.cameraControls.maxAzimuthAngle =
                        0;
        // @ts-ignore
        this.cameraControls.dollyTo(fitDistance, enableTransition);
        this.cameraControls.rotateTo(0, 0, enableTransition);
        // moveTo 到合适的位置
        // this.cameraControls.moveTo(0, 0, levelObj?.position.z!, true)
        this.cameraControls.moveTo(0, 0, 0, true);
    }
    fitToBox3d() {
        // 开启只能上下移动的模式
        // this.cameraControls.truckYDisabled = true
        // this.cameraControls.verticalDragToForward = false
        const _getTarget = () => {
            // const allLevelObjs = getObjectsByName(OBJECT_NAME.level, this.scene);
            const allLevelObjs = getObjectsByName('level', this.scene);
            // 最高楼层
            return allLevelObjs[allLevelObjs.length - 1];
        };
        const enableTransition = true;
        // [多层] to do... 判断以面积最小的楼层来设置 dollys
        // if (stateLevelSwitcher.mode === 'multi') {
        //   // 上下旋转范围
        //   global.controls.minPolarAngle = Math.PI * (60 / 180)
        //   global.controls.maxPolarAngle = Math.PI * (60 / 180)
        // } else {
        //   // 上下旋转范围
        //   global.controls.minPolarAngle = 0
        //   global.controls.maxPolarAngle = Math.PI * (60 / 180)
        // }
        // 控制上下旋转
        // this.cameraControls.maxPolarAngle = Math.PI * (60 / 180); // radians
        // 左右
        this.cameraControls.minAzimuthAngle = -Infinity;
        this.cameraControls.maxAzimuthAngle = Infinity;
        const target = _getTarget();
        const { fitDistance, dollys } = shared.getDollys(target, this.cameraControls);
        if (fitDistance) {
            this.cameraControls.dollyTo(fitDistance, enableTransition);
            Object.assign(this.info, {
                fitDistance
            });
        }
        this.cameraControls.rotateTo(0, Math.PI * (60 / 180), enableTransition);
        this.cameraControls.moveTo(0, 0, 0, true);
    }
}

/**
 * mitt 事件总程
 */
class MyMitt {
    constructor() {
        Object.defineProperty(this, "emitter", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: mitt__default["default"]()
        });
    }
    on(type, handler) {
        this.emitter.on(type, handler);
        return this;
    }
    off(type, handler) {
        this.emitter.off(type, handler);
        return this;
    }
}

/**
 * 基类，element
 *
 * 被添加到地图上的元素
 *
 * 若当前扩展类不需要 mitt，在继承时，泛型填写 any，如：
 *
 * ```ts
 * class Foo extends Element<any>{
 *
 * }
 * ```
 */
class Element extends MyMitt {
    constructor() {
        super();
        /**
         * NAME
         * 一般为子类的类名（防止工具压缩把类名压缩了，导致一些拿类名判断的逻辑失效，所以手动加上类名）
         */
        // abstract NAME: string;
        Object.defineProperty(this, "map", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
    }
    /**
     * __inject
     * 当元素被 map.add 之后，会注入整个 map 对象到子类，提供子类使用 map
     */
    __inject(map) {
        this.map = map;
        return this;
    }
}

/**
 * TODO:Control 基类，待实现
 */
class Control extends MyMitt {
    constructor() {
        super();
        // abstract NAME: string;
        Object.defineProperty(this, "map", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
    }
    __inject(map) {
        this.map = map;
        return this;
    }
}

// 📝 https://www.zhihu.com/question/276314390
/**
 * Service 服务类
 */
class Service extends MyMitt {
    constructor() {
        super();
    }
}

/**
 * 信息窗体
 */
class InfoWindow extends MyMitt {
    constructor() {
        super();
    }
}

// 📝 https://www.zhihu.com/question/276314390
// 目前不好约束子类 static 成员，只能在运行时检测
/**
 * 右键菜单
 */
class ContextMenu {
    constructor() { }
}

// @ts-ignore
const KTMap = {
    Map: _Map,
    use(plugins) {
        plugins = Array.isArray(plugins) ? plugins : [plugins];
        for (const item of plugins) {
            // @ts-ignore
            this[item.NAME] = item;
        }
        return this;
    }
};
// // eslint-disable-next-line @typescript-eslint/no-namespace
// declare namespace KTMap {
//   type foo = string;
// }

exports.ContextMenu = ContextMenu;
exports.Control = Control;
exports.Element = Element;
exports.InfoWindow = InfoWindow;
exports.MyMitt = MyMitt;
exports.Service = Service;
exports["default"] = KTMap;
