'use strict';

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

var map = require('@ktmap/map');
var shared = require('@ktmap/shared');
var three = require('three');
var pluginParkingLayer = require('@ktmap/plugin-parking-layer');
var pluginLabelsLayer = require('@ktmap/plugin-labels-layer');
var TWEEN = require('@tweenjs/tween.js');
var pluginInfoWindow = require('@ktmap/plugin-info-window');
var OBB_js = require('three/examples/jsm/math/OBB.js');

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

var TWEEN__default = /*#__PURE__*/_interopDefaultLegacy(TWEEN);

// import { PluginOptions } from '../types';
/**
 * demo 行为
 */
exports.DemoAction = void 0;
(function (DemoAction) {
    // 模拟车子进场
    DemoAction[DemoAction["In"] = 0] = "In";
    // 模拟车子出厂
    DemoAction[DemoAction["Out"] = 1] = "Out";
    // 模拟车辆拥堵
    DemoAction[DemoAction["Jam"] = 2] = "Jam";
})(exports.DemoAction || (exports.DemoAction = {}));

/******************************************************************************
Copyright (c) Microsoft Corporation.

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */

function __awaiter(thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
}

exports.CarAction = void 0;
(function (CarAction) {
    CarAction[CarAction["Empty"] = 0] = "Empty";
    // 进场中
    CarAction[CarAction["EnterActive"] = 1] = "EnterActive";
    // 停泊
    CarAction[CarAction["Enter"] = 2] = "Enter";
    // 离场中
    CarAction[CarAction["LeaveActive"] = 3] = "LeaveActive";
    // 离场
    CarAction[CarAction["Leave"] = 4] = "Leave";
    // 堵车中
    CarAction[CarAction["Jam"] = 5] = "Jam";
    // 违停
    CarAction[CarAction["Violation"] = 6] = "Violation";
})(exports.CarAction || (exports.CarAction = {}));
/**
 * 车子类
 */
class Car {
    constructor(map, options) {
        var _a;
        Object.defineProperty(this, "map", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: map
        });
        Object.defineProperty(this, "options", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: options
        });
        Object.defineProperty(this, "stopID", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
        Object.defineProperty(this, "tweenGroupCar", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
        Object.defineProperty(this, "carAction", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: exports.CarAction.Empty
        });
        Object.defineProperty(this, "car", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
        /*
          假设人的步行速度在 1.5m/s
          目前地图上 1000mm（=1米）就为 1500ms，所以 TWEEN 的 duration 直接用 distance/1.5 就可以算出
          假设汽车在车库的行驶速度为 20~30 km/h，三个时间换算为 m/s（20km/h->5.6m/s，25km/h->7m/s，30km/h->8.3m/s）
        */
        // speed = 5.6;
        // speed = 7;
        // speed = 8.3;
        options.speed = (_a = options.speed) !== null && _a !== void 0 ? _a : 5.6;
        // 暴露模型
        this.car = options.car;
        this.init();
    }
    /**
     * 获取车模型
     */
    static loadCarModal(BASE_URL) {
        return __awaiter(this, void 0, void 0, function* () {
            const car = yield shared.GLTFLoaderPromise(`${BASE_URL}sandTable/assets/car/scene.gltf`);
            // TODO:统一提取规范name
            car.name = 'car';
            car.traverse((child) => {
                // name 都加上 car_前缀，用来射线拾取的时候方便判断
                // child.name && (child.name = `car_${child.name}`);
                if (child.isMesh) {
                    child.material.emissive = child.material.color;
                    child.material.emissiveMap = child.material.map;
                }
            });
            // 先隐藏
            car.visible = false;
            // car.scale.addScalar(240);
            car.scale.multiplyScalar(240);
            car.position.copy(new three.Vector3(0, 0, 0));
            car.rotateX(Math.PI * (90 / 180));
            // 📝 OBB 计算需要
            car.matrixAutoUpdate = false;
            return car;
        });
    }
    init() {
        return __awaiter(this, void 0, void 0, function* () {
            const { map, options: { car } } = this;
            // 初始时把车子加入场景
            map.scene.add(car);
            map.reDraw();
        });
    }
    /**
     * 进场
     */
    in({ begin, end, parkObject, violation }) {
        return this.runPrepare({
            begin,
            end,
            action: exports.DemoAction.In,
            parkObject,
            violation
        });
    }
    /**
     * 离场
     * 如果当前车子还没有停好，不能离场行为，给出提示
     */
    out({ begin, end, jam }) {
        return this.runPrepare({ begin, end, action: exports.DemoAction.Out, jam });
    }
    /**
     * 准备，开始规划一条路线
     */
    runPrepare({ begin, end, action, parkObject, jam, violation }) {
        return __awaiter(this, void 0, void 0, function* () {
            // console.log('begin :>> ', begin);
            // console.log('end :>> ', end);
            // console.log('parkObject :>> ', parkObject);
            const { map, options: { aStar } } = this;
            begin = shared.truncateVector3(begin);
            end = shared.truncateVector3(end);
            if (begin && end) {
                // 模拟测试
                // 得到绘制轨迹的点集合
                const trackPoints = aStar.calcShortestPath([begin.x, begin.y], [end.x, end.y]);
                if (trackPoints.length >= 2) {
                    console.log('trackPoints :>> ', trackPoints);
                    const points = [];
                    // 绘制简单的线段
                    for (const item of trackPoints) {
                        // @ts-ignore
                        points.push(new three.Vector3(item.x, item.y, 0));
                    }
                    // 移动车到指定的开始位置，进行动画模拟行驶泊车
                    yield this.run({
                        trackPoints,
                        parkPosition: end,
                        action,
                        parkObject,
                        jam,
                        violation
                    });
                }
                else {
                    console.warn('线段长度小于2，无法进行绘制');
                }
                map.reDraw();
            }
        });
    }
    /**
     * 模拟车子行驶到到指定终点车位泊车
     */
    run({ trackPoints, parkPosition, action, parkObject, jam, violation }) {
        var _a;
        return __awaiter(this, void 0, void 0, function* () {
            const { map, options: { car, speed } } = this;
            // 先展示车子
            car && (car.visible = true);
            // 滚动轮子
            // TODO:模拟真实，公式后续细算
            const r = 0.01 * speed;
            const loopWheel = (wheels) => {
                map.reDraw();
                for (const item of wheels)
                    item.rotateX(-r * Math.PI);
                this.stopID = requestAnimationFrame(() => loopWheel(wheels));
            };
            // clear：清除动画相关，删除对应的 map 键
            (_a = this.tweenGroupCar) === null || _a === void 0 ? void 0 : _a.removeAll();
            map.tweenGroupMap.delete(this.tweenGroupCar);
            // 销毁raf
            cancelAnimationFrame(this.stopID);
            if (car) {
                // 前轮左
                const frontLeftWheel = shared.getObjectByName('F_L_Wheel_2', car);
                // 前轮右边
                const frontRightWheel = shared.getObjectByName('F_R_Wheel_3', car);
                // 后轮，目前模型两个后轮是一个元素
                const rearWheel = shared.getObjectByName('Rear_Wheels_1', car);
                loopWheel([frontLeftWheel, frontRightWheel, rearWheel]);
            }
            // add：重新开始一个新的补间组
            this.tweenGroupCar = new TWEEN__default["default"].Group();
            // 把补间组加入 map 的 anim 方法
            map.tweenGroupMap.set(this.tweenGroupCar, null);
            const start = (trackPoints) => {
                this.carAction =
                    action === exports.DemoAction.In
                        ? exports.CarAction.EnterActive
                        : exports.CarAction.LeaveActive;
                Object.assign(this.options.car.userData, {
                    carAction: this.carAction
                });
                let resolve;
                const p = new Promise(_ => (resolve = _));
                const tweens = [];
                for (let i = 1, len = trackPoints.length; i < len; i++) {
                    tweens.push(createTween(trackPoints[i - 1], trackPoints[i], this.tweenGroupCar, 
                    // tweens 的最后一个才会进行 promise 的 resolve 回调
                    i === trackPoints.length - 1 ? resolve : () => { }));
                }
                // 方式1
                for (let i = 1; i < tweens.length; i++) {
                    tweens[i - 1].chain(tweens[i]);
                }
                // // 方式2
                // const first = tweens[0];
                // tweens.unshift();
                // first.chain(...tweens);
                // 动画开始
                tweens[0].start();
                return p;
            };
            /**
             * ✨ 只在进场需要
             * 停车，这里先用比较糙的手法，让车子直接移动到车位
             * 需要和车位平行（需要进行计算），暂时不管车头的朝向
             */
            const parking = (parkPosition, parkObject) => {
                // let resolve!: () => void;
                // const p = new Promise<void>(_ => (resolve = _));
                var _a;
                const { map } = this;
                /**
                 * 获取车位的角度，用来调整车子的角度
                 */
                const getParkRadian = (data) => {
                    let len = 0, tempLen = 0, p1, p2;
                    for (let i = 1, l = data.length; i < l; i++) {
                        tempLen = shared.getDistance(data[i - 1], data[i]);
                        if (tempLen > len) {
                            p1 = data[i - 1];
                            p2 = data[i];
                            len = tempLen;
                        }
                    }
                    // TODO:有些车场需要 p1，p2 换位才可以尽量模拟车头朝外部，后续可以深入研究，如果让车头智能朝向外部
                    // const { radian } = getAngle(p2, p1);
                    const { radian } = shared.getAngle(p1, p2);
                    return radian;
                };
                // 计算 parkObject 最长边的旋转角度
                const { data } = parkObject.userData;
                const radian = getParkRadian(data);
                car === null || car === void 0 ? void 0 : car.position.copy(parkPosition);
                car && (car.rotation.y = radian - Math.PI / 2);
                // 📝 OBB 计算需要
                car.updateMatrix();
                car.updateMatrixWorld();
                car.userData.obb.copy(car.userData.obbSetup);
                car.userData.obb.applyMatrix4(car.matrixWorld);
                // clear：清除动画相关，删除对应的 map 键
                (_a = this.tweenGroupCar) === null || _a === void 0 ? void 0 : _a.removeAll();
                map.tweenGroupMap.delete(this.tweenGroupCar);
                // 停止轮子滚动
                cancelAnimationFrame(this.stopID);
                map.reDraw();
            };
            /**
             * 出场，销毁所有车辆相关
             */
            const exit = () => {
                this.dispose();
            };
            /**
             *  创建补间动画
             */
            const createTween = (oldT, newT, tweenGroupCar, cb) => {
                // 需要执行动画的路线形成数组，然后进行移动，
                const t = new TWEEN__default["default"].Tween({
                    x: oldT.x,
                    y: oldT.y,
                    // z: oldT.z
                    z: 0
                }, tweenGroupCar)
                    // time = distance / speed，保持每段距离都匀速
                    .to({ x: newT.x, y: newT.y, z: newT.z || 0 }, shared.getDistance(oldT, newT) / speed)
                    .easing(TWEEN__default["default"].Easing.Linear.None)
                    .onStart(() => {
                    const { radian } = shared.getAngle(oldT, newT);
                    // 扣掉 90° 表示垂直向上
                    // map.cameraControls.rotateTo(radian - Math.PI / 2, Math.PI / 4, true);
                    // 导航指标的指向
                    car && (car.rotation.y = radian - Math.PI / 2);
                    // 正常化 controls 的角度，控制在0-360°之间，否则会累积角度
                    map.cameraControls.normalizeRotations();
                })
                    .onUpdate(object => {
                    // 实时判断前面是否有车辆，用射线扫描队列？
                    car.position.set(object.x, object.y, object.z);
                    // 📝 OBB 计算需要
                    car.updateMatrix();
                    car.updateMatrixWorld();
                    // [update OBB](https://github.com/mrdoob/three.js/blob/master/examples/webgl_math_obb.html#L217)
                    car.userData.obb.copy(car.userData.obbSetup);
                    car.userData.obb.applyMatrix4(car.matrixWorld);
                    // 过滤自己
                    const carArr = shared.getObjectsByName('car', map.scene).filter(item => item.userData.carNo !== car.userData.carNo);
                    const obb = car.userData.obb;
                    for (let i = 0, il = carArr.length; i < il; i++) {
                        const object = carArr[i];
                        const obbToTest = object.userData.obb;
                        if (obb.intersectsOBB(obbToTest)) {
                            console.log('堵车了 :>> ', '堵车了');
                            this.carAction = exports.CarAction.Jam;
                            // 标记堵车状态
                            Object.assign(car.userData, {
                                carAction: this.carAction
                            });
                            // 停止当前这段动画，不销毁
                            t.stop();
                            // 停止轮子滚动
                            cancelAnimationFrame(this.stopID);
                        }
                    }
                    map.reDraw();
                })
                    .onComplete(() => {
                    cb === null || cb === void 0 ? void 0 : cb();
                });
                return t;
            };
            // start(移动到起点，调整车头，开始动画)
            yield start(trackPoints);
            // 等待
            yield shared.sleep(300);
            if (action === exports.DemoAction.In) {
                // 模拟违章，不做任何事
                if (violation) {
                    this.carAction = exports.CarAction.Violation;
                    Object.assign(this.options.car.userData, {
                        carAction: this.carAction,
                        parkTime: '-'
                    });
                }
                else {
                    // 泊车
                    parking(parkPosition, parkObject);
                    this.carAction = exports.CarAction.Enter;
                    // 把行为存入模型的 userData
                    Object.assign(this.options.car.userData, {
                        carAction: this.carAction,
                        parkTime: new Date().toLocaleString()
                    });
                }
            }
            else {
                // 离场，销毁所有相关
                // 模拟交通堵塞，车子堵在出口没有出去
                if (jam) {
                    this.carAction = exports.CarAction.Jam;
                    Object.assign(this.options.car.userData, {
                        carAction: this.carAction
                    });
                }
                else {
                    this.carAction = exports.CarAction.Leave;
                    Object.assign(this.options.car.userData, {
                        carAction: this.carAction,
                        outTime: new Date().toLocaleString()
                    });
                    exit();
                }
            }
            // 控制时间1s后，否则可能会导致看不到停车效果，如下例子
            // await sandTable.demo(Action.In, {
            //   parkNo: 22739,
            //   carNo: '闽D22222'
            // });
            // sandTable.demo(Action.Out, {
            //   carNo: '闽D22222'
            // });
            yield shared.sleep(1000);
        });
    }
    /**
     * 销毁
     */
    dispose() {
        var _a;
        const { map, options: { car } } = this;
        (_a = this.tweenGroupCar) === null || _a === void 0 ? void 0 : _a.removeAll();
        map.tweenGroupMap.delete(this.tweenGroupCar);
        map.scene.remove(car);
        // 停止轮子滚动
        cancelAnimationFrame(this.stopID);
        map.reDraw();
    }
    /**
     * 绘制普通辅助线段
     */
    drawNormalLine(points, 
    // end: [number, number],
    ext) {
        const { map } = this;
        const line = new three.Line(new three.BufferGeometry().setFromPoints(points), new three.LineBasicMaterial({
            // visible: false,
            color: (ext === null || ext === void 0 ? void 0 : ext.color) || 'blue',
            opacity: 1,
            transparent: true,
            depthTest: false
        }));
        line.name = (ext === null || ext === void 0 ? void 0 : ext.name) || '';
        // line.renderOrder = 998;
        line.userData = {
        // id,
        // begin,
        // end
        };
        map.scene.add(line);
    }
}
Object.defineProperty(Car, "carQueue", {
    enumerable: true,
    configurable: true,
    writable: true,
    value: []
});

/**
 * experimental 沙盘
 */
class SandTable extends map.Service {
    constructor(map, options) {
        super();
        Object.defineProperty(this, "map", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: map
        });
        Object.defineProperty(this, "options", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: options
        });
        /**
         * TODO: 完善参考 Vue onMounted 的设计
         */
        Object.defineProperty(this, "complateHook", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
        // private carQueue: Array<{
        //   carNo: string;
        //   parkNo: number;
        //   parkObject: _THREE.Object3D;
        //   car: Car;
        // }> = [];
        Object.defineProperty(this, "carModal", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
        Object.defineProperty(this, "aStar", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
        Object.defineProperty(this, "infoWindowJam", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
        Object.defineProperty(this, "infoWindowViolation", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
        // 子类构造方法调用初始化方法
        this.__init();
    }
    __init() {
        return __awaiter(this, void 0, void 0, function* () {
            // const {
            //   car: carA,
            //   aabb: aabbA,
            //   hitbox: hitboxA,
            //   obb: obbA
            // } = await createCarA({ map: this.map });
            // const {
            //   car: carB,
            //   aabb: aabbB,
            //   hitbox: hitboxB,
            //   obb: obbB
            // } = await createCarB({ map: this.map });
            // carA.userData.obb = new OBB();
            // carB.userData.obb = new OBB();
            // const loop = () => {
            //   requestAnimationFrame(() => loop());
            //   carB.rotateY(-0.005 * Math.PI);
            //   carA.updateMatrix();
            //   carA.updateMatrixWorld();
            //   carB.updateMatrix();
            //   carB.updateMatrixWorld();
            //   // ❌ 方式1：无效
            //   // obbA.applyMatrix4(carA.matrixWorld);
            //   // obbB.applyMatrix4(carB.matrixWorld);
            //   // const collision = obbB.intersectsOBB(obbA);
            //   // ✅  方式2：有效
            //   carA.userData.obb.copy(obbA);
            //   carA.userData.obb.applyMatrix4(carA.matrixWorld);
            //   carB.userData.obb.copy(obbB);
            //   carB.userData.obb.applyMatrix4(carB.matrixWorld);
            //   // const collision = carA.userData.obb.intersectsOBB(carB.userData.obb);
            //   const collision = carB.userData.obb.intersectsOBB(carA.userData.obb);
            //   // console.log('collisioncollisioncollision222 :>> ', collision);
            //   this.map.reDraw();
            // };
            // loop();
            // 大插件中使用小插件
            // KTMap.use(InfoWindow);
            const { map, options: { BASE_URL } } = this;
            console.log('SandTable init success');
            const labelsLayer = new pluginLabelsLayer.LabelsLayer({
                name: 'labelsLayer',
                collision: true
                // collision: false
            });
            map.add(labelsLayer);
            this.initData((data) => __awaiter(this, void 0, void 0, function* () {
                // 提取需要的数据
                const { floorDatas } = data;
                const paths = floorDatas[0].data.filter((_) => _.type === 'path');
                // - 初始化 aStar
                this.aStar = new shared.AStar(paths);
                // - 绘制 ParkingLayer
                const parkingLayer = new pluginParkingLayer.ParkingLayer({
                    poiCollision: false,
                    data
                });
                map.add(parkingLayer);
                // - 绘制 LabelsLayer，目前设计没有车位号，所以没有数据。取车位的id号来充当车位号
                // 获取所有车位，利用 aabb 获取车位中心点，用来绘制确定要绘制的标签位置
                const parks = shared.getObjectsByName('park', map.scene);
                let position;
                const labelMarkers = [];
                for (const item of parks) {
                    position = shared.getBox3Center(item);
                    labelMarkers.push(new pluginLabelsLayer.LabelMarker({
                        name: item.userData.id + '',
                        text: `A-${item.userData.id}`,
                        fontSize: 10,
                        position,
                        // TODO:暂时先这样子处理，参考 findcar 旧项目，来约束 renderOrder
                        renderOrder: 10
                    }));
                }
                labelsLayer.add(...labelMarkers);
                // - 创建多辆车，准备数据
                this.carModal = yield Car.loadCarModal(BASE_URL);
                // - 注册信息窗体，用于 hover 展示车辆信息
                // const infoWindow = new KTMap.InfoWindow(map, {
                const infoWindow = new pluginInfoWindow.InfoWindow(map, {
                    type: 'info',
                    title: '车辆信息',
                    content: '',
                    trigger: 'hover',
                    targetName: 'car',
                    align: { offset: [10, 0] }
                });
                // - 悬浮至车子上，显示车子款式： GTR（2022）， 入场时间，已停时长，累计车费，车位号，车牌号
                infoWindow.on('hover', car => {
                    this.emitter.emit('carHover', { car, infoWindow });
                });
                // - 注册信息窗体，用于 click 展示当前车位监控，视频
                const infoWindow2 = new pluginInfoWindow.InfoWindow(map, {
                    type: 'info',
                    title: '监控信息',
                    trigger: 'click',
                    targetName: 'park',
                    align: { offset: [10, 0] }
                });
                infoWindow2.on('click', park => {
                    this.emitter.emit('parkClick', { park, infoWindow: infoWindow2 });
                });
                this.infoWindowJam = new pluginInfoWindow.InfoWindow(map, {
                    type: 'warning',
                    close: true,
                    title: '拥堵告警',
                    trigger: 'custom',
                    align: { points: ['bc'] }
                });
                this.infoWindowViolation = new pluginInfoWindow.InfoWindow(map, {
                    type: 'error',
                    close: true,
                    title: '违停告警',
                    trigger: 'custom',
                    align: { points: ['bc'] }
                });
                this.emitter.emit('complete');
            }));
        });
    }
    /**
     * 沙盘 demo
     */
    demo(action, ext) {
        return __awaiter(this, void 0, void 0, function* () {
            const { map, map: { scene, $map, camera }, options: { entry, exit, speed } } = this;
            switch (action) {
                case exports.DemoAction.In:
                    {
                        const { 
                        // 泊车车位编号
                        parkNo, 
                        // 车牌号
                        carNo, violation } = ext;
                        // 利用 carNo 判断当前队列是否已经有该车辆
                        if (Car.carQueue.find(item => item.carNo === carNo)) {
                            // TODO:提取到 warn 统一管理
                            console.warn('该车已存在队列中，请勿重复添加');
                            return;
                        }
                        const parkObject = this.getParkObjectById(parkNo);
                        if (parkObject) {
                            const carModal = this.carModal.clone();
                            const car = new Car(map, {
                                car: carModal,
                                aStar: this.aStar,
                                speed
                            });
                            // 存入基础信息
                            carModal.userData = Object.assign(carModal.userData, {
                                carNo,
                                parkNo: `A-${parkNo}`,
                                inTime: new Date().toLocaleString()
                            });
                            // 📝 OBB 计算需要
                            const aabb = new three.Box3().setFromObject(carModal);
                            // ✨ 扩大 aabb 范围，使得车子与车子之间有一段距离就可以检测出碰撞
                            aabb.expandByScalar(1.2);
                            const obb = new OBB_js.OBB().fromBox3(aabb);
                            carModal.userData = Object.assign(carModal.userData, {
                                obbSetup: obb,
                                obb: new OBB_js.OBB()
                            });
                            // 加入队列
                            Car.carQueue.push({
                                parkNo,
                                carNo,
                                parkObject,
                                car
                            });
                            const end = shared.getBox3Center(parkObject);
                            yield car.in({ begin: entry, end, parkObject, violation });
                            // 车子停止，判断是否有违停
                            if (car.carAction === exports.CarAction.Violation) {
                                this.emitter.emit('violation', {
                                    car: car.car,
                                    infoWindow: this.infoWindowViolation
                                });
                                const worldPosition = shared.getBox3Center(car.car);
                                // 手动控制显示位置
                                this.infoWindowViolation.show(worldPosition);
                            }
                        }
                        else {
                            // TODO:提取到 warn 统一管理
                            console.warn('找不到对应的车位编号');
                        }
                    }
                    break;
                case exports.DemoAction.Out:
                    {
                        const { 
                        // 泊车车位编号
                        parkNo, 
                        // 车牌号
                        carNo, jam } = ext;
                        // 出场必然会在队列中，直接从队列获取
                        const carItem = Car.carQueue.find(_ => _.carNo === carNo);
                        const car = carItem === null || carItem === void 0 ? void 0 : carItem.car;
                        const parkObject = carItem === null || carItem === void 0 ? void 0 : carItem.parkObject;
                        // 有车有车位且车子已经停泊
                        if (car && parkObject && car.carAction === exports.CarAction.Enter) {
                            const begin = shared.getBox3Center(parkObject);
                            yield car.out({ begin, end: exit, jam });
                            if (!jam) {
                                // 离场删除对应的数组元素
                                Car.carQueue = Car.carQueue.filter(_ => _.carNo !== carNo);
                            }
                            console.log('Car.carQueue :>> ', Car.carQueue);
                        }
                        else {
                            console.warn('离场出现错误');
                        }
                    }
                    break;
                case exports.DemoAction.Jam:
                    {
                        const isJam = () => {
                            // 超过3辆车堵车，就提示
                            const jamCars = Car.carQueue.filter(item => item.car.carAction === exports.CarAction.Jam);
                            if (jamCars.length >= 5) {
                                // 拥堵告警
                                this.emitter.emit('jam', {
                                    infoWindow: this.infoWindowJam,
                                    carQueue: jamCars
                                });
                                const firstCar = jamCars[0].car.car;
                                const worldPosition = shared.getBox3Center(firstCar);
                                // 手动控制显示位置
                                this.infoWindowJam.show(worldPosition);
                            }
                        };
                        // const { carQueue } = this;
                        (() => __awaiter(this, void 0, void 0, function* () {
                            for (let i = 0; i < Car.carQueue.length; i++) {
                                const item = Car.carQueue[i];
                                yield shared.sleep(1000);
                                this.demo(exports.DemoAction.Out, {
                                    carNo: item.carNo,
                                    // 第一辆才不出场，后续出场车辆碰撞检测
                                    jam: i === 0
                                });
                                isJam();
                            }
                        }))();
                    }
                    break;
            }
        });
    }
    /**
     * TODO:待优化提取
     */
    initData(cb) {
        return __awaiter(this, void 0, void 0, function* () {
            const { data } = this.options;
            if (typeof data === 'function') {
                try {
                    const res = yield data();
                    cb === null || cb === void 0 ? void 0 : cb(res);
                    // for (const fn of this.completeCbs) fn?.({ data: res, foo: 1, bar: 2 });
                }
                catch (_) { }
            }
            else {
                cb === null || cb === void 0 ? void 0 : cb(data);
                // for (const fn of this.completeCbs) fn?.({ data, foo: 1, bar: 2 });
            }
        });
    }
    /**
     * 根据车位编号id获取车位元素
     */
    getParkObjectById(id) {
        const { map } = this;
        return shared.getObjectsByName('park', map.scene).find(item => item.userData.id === id);
    }
}
Object.defineProperty(SandTable, "NAME", {
    enumerable: true,
    configurable: true,
    writable: true,
    value: 'SandTable'
});

exports.Car = Car;
exports.SandTable = SandTable;
