import { BaseLevelControl, EventType } from "@uino/thing";
import { LevelControlHelper } from "../LevelControlHelper";
import { LevelControls } from "../LevelControls";

// Store the current orderID of the level event
let _orderID = 1;

// Generate a new orderID
function _getOrderID() {
    return '__CampusLevelControl__TagName__' + (++_orderID);
}

const __ = {
    private: Symbol('private')
}

/**
 * @class DefaultLevelControl
 * 园区的默认层级控制类。
 * @extends THING.BaseLevelControl
 * @public
 */
class DefaultLevelControl extends BaseLevelControl {

    static levelCondition = '';

    static levelName = '';

    static register() {
        LevelControls.setLevelControlClass(this.levelName, this.levelCondition, this);
    }

    /**
     * Constructor for DefaultLevelControl class.
     * @param {Object} options - Options for the level control.
     */
    constructor(options) {
        super(options);
        let _private = this[__.private] = {};

        _private.enableFly = true;
        _private.enableSkipLevel = true;
        _private.enableOutlineColor = true;
        _private.enableMouseEvent = true;
        _private.enterLevelEventType = EventType.DBLClick;
        _private.leaveLevelEventType = EventType.DBLClick;
        _private.outlineColor = [1, 0.5019607843137255, 0];
        _private.preferCachedViewpoint = false;

        _private.isCameraFirst = false;

        _private.viewpointCache = new Map();
        _private.events = [];
        _private.tagName = _getOrderID();
        _private.mouseEnterObject = null;
        _private._isSkipLevel = false;
        _private.enableBrotherMouseEvent = true;

        /**
         * Function to cache viewpoint information.
         * @param {Object} param - Parameters containing the object and level options.
         * @private
         */
        _private.cacheViewpoint = (param) => {
            const current = param.current;
            if (!current) {
                return;
            }

            const next = param.next;
            if (!next) {
                return;
            }

            if (next.isChildOf(current)) {
                const camera = current.app.camera;
                _private.viewpointCache.set(current.uuid, {
                    position: camera.position,
                    target: camera.target
                });
            }
        }

        /**
         * Function to retrieve viewpoint information from the level.
         * @param {Object} object - The object to get the viewpoint from.
         * @returns {Object} - The viewpoint information.
         * @private
         */
        _private.getViewpointFromLevel = (object) => {
            if (object) {
                const level = object.level;
                if (level) {
                    return level.viewpoint;
                }
            }
        }

        /**
         * Function to go back to the previous level.
         * @private
         */
        _private.backLevel = () => {
            const app = this.app;
            const current = app.level.current;
            let parent = current.parent;

            if (parent) {
                app.levelManager.change(parent);
            }
        }

        /**
         * Function to register fly events.
         * @param {Object} camera - The camera object for the level.
         * @returns {Function} - Function to unregister the fly events.
         * @private
         */
        _private.registerFlyToEvent = (camera) => {
            const flyEventTag = _getOrderID() + '_fly_tag_';
            _private.registerEvent(EventType.MouseUp, () => {
                camera.stopFlying();
            }, flyEventTag);
            _private.registerEvent(EventType.MouseDown, () => {
                camera.stopFlying();
            }, flyEventTag);
            _private.registerEvent(EventType.Wheel, () => {
                camera.stopFlying();
            }, flyEventTag);

            return () => {
                _private.unregisterEvent(EventType.MouseUp, flyEventTag);
                _private.unregisterEvent(EventType.MouseDown, flyEventTag);
                _private.unregisterEvent(EventType.Wheel, flyEventTag);
            }
        }

        /**
         * Process fly param.
         * @param {Object} param - Enter the parameters of the level.
         * @returns {Object} - The fly Params.
         * @private
         */
        _private.processFlyParam = (param) => {
            const current = param.current;
            const camera = current.app.camera;
            const unregisterCallback = _private.registerFlyToEvent(camera);

            let flyConfig = {
                target: current,
                onStop: () => {
                    unregisterCallback();
                },
                onComplete: () => {
                    unregisterCallback();
                }

            };

            let viewpoint = _private.viewpointCache.get(current.uuid);
            if (!viewpoint || !_private.preferCachedViewpoint) {
                const vp = _private.getViewpointFromLevel(current);
                if (vp) {
                    viewpoint = vp;
                }
            }

            _private.viewpointCache.delete(current.uuid);

            if (viewpoint) {
                flyConfig.target = viewpoint.target;
                flyConfig.position = viewpoint.position;
            }

            return flyConfig;
        }

        _private.registerMouseEvent = () => {
            _private.registerEvent(EventType.MouseEnter, (ev) => {
                this.onMouseEnter(ev);
            });
            _private.registerEvent(EventType.MouseLeave, (ev) => {
                this.onMouseLeave(ev);
            });
            _private.registerEvent(_private.enterLevelEventType, (ev) => {
                if (ev.button === 0) {
                    if (_private.mouseEnterObject) {
                        let enterObj = _private.mouseEnterObject;
                        this.onMouseLeave();
                        this.onEnterObjectLevel(enterObj);
                    }
                }
            }, _private.tagName + '_enter');
            _private.registerEvent(_private.leaveLevelEventType, (ev) => {
                if (ev.button === 2) {
                    if (_private.mouseEnterObject) {
                        this.onMouseLeave();
                    }
                    this.onBackToParentLevel()
                }
            }, _private.tagName + '_leave');
        }

        _private.unregisterMouseEvent = () => {
            _private.unregisterEvent(EventType.MouseEnter);
            _private.unregisterEvent(EventType.MouseLeave);
            _private.unregisterEvent(_private.enterLevelEventType, _private.tagName + '_enter');
            _private.unregisterEvent(_private.leaveLevelEventType, _private.tagName + '_leave');
        }

        /**
         * Register an event listener.
         * @param {string} key - The event key.
         * @param {Function} callback - The callback function for the event.
         * @param {string} [tag] - The event tag (optional).
         * @private
         */
        _private.registerEvent = (key, callback, tag) => {
            let tagName = tag || _private.tagName;
            this.app.on(key, callback, tagName);
            _private.events.push({ name: key, tag: tagName });
        }

        /**
         * Unregister an event listener.
         * @param {string} eventName - The name of the event to unregister.
         * @param {string} [tag] - The event tag (optional).
         * @private
         */
        _private.unregisterEvent = (eventName, tag) => {
            if (!eventName) {
                return;
            }

            const app = this.app;
            let events = _private.events;
            for (let i = 0; i < events.length; i++) {
                const element = events[i];
                if (element.name === eventName) {
                    if (tag) {
                        if (tag === element.tag) {
                            app.off(eventName, tag);
                            events._removeAt(i);
                        }
                    } else {
                        app.off(eventName, element.tag);
                        events._removeAt(i);
                    }
                    return;
                }
            }
        }

        /**
         * Unregister all event listeners.
         * @private
         */
        _private.unregisterAllEvents = () => {
            const app = this.app;
            _private.events.forEach(eventObject => {
                app.off(eventObject.name, eventObject.tag);
            });
        }

        _private.setLevelChangeEventType = (key, value) => {
            if (value === _private[key]) {
                return
            }

            if (this.isRunning) {
                _private.unregisterMouseEvent()
            }

            if (value === EventType.DBLClick) {
                _private[key] = EventType.DBLClick;
            } else {
                _private[key] = EventType.Click;
            }

            if (this.isRunning) {
                _private.registerMouseEvent()
            }
        }

    }

    //#region Override

    /**
     * 当进入层级时。
     * @param {Object} param - 进入层级的参数。
     * @returns {Promise}
     * @public
     */
    async onEnter(param) {
        super.onEnter(param);
        let _private = this[__.private];

        _private.levelParam = param;
        let options = param.options;

        let isSkip = false;

        if (options.jumping && param.target !== param.current) {
            isSkip = true;
        }

        if (_private.enableSkipLevel) {
            _private._isSkipLevel = this.onSkipLevel(param);
            if (_private._isSkipLevel) {
                isSkip = true;
            }
        }

        //  跳过 只执行 on set effect
        if (isSkip) {
            this.onSetEffect(param);
            return;
        }

        // 相机优先
        if (_private.isCameraFirst) {
            await this.onSetCamera(param);
        }

        await this.onSetEffect(param);

        if (this.app.root.dynamicLoad.enable) {
            await this.app.root.dynamicLoad.waitForComplete();
        }

        // 如果动态加载结束后,当前层级已经退出,就不能再注册事件以及相机飞行
        if (!this.isRunning) {
            return;
        }

        this.onRegisterEvent(param);

        // 相机不优先
        if (!_private.isCameraFirst) {
            await this.onSetCamera(param);
        }

    }

    /**
     * 当离开层级时。
     * @param {Object} param - 离开层级的参数。
     * @public
     */
    async onLeave(param) {
        super.onLeave(param);
        const _private = this[__.private];

        // 如果跳过了只执行 reset effect
        await this.onResetEffect(param);

        if (_private.enableSkipLevel && _private._isSkipLevel) {
            _private._isSkipLevel = false;
            return;
        }

        _private.cacheViewpoint(param);

        this.onUnregisterEvent(param);

        return Promise.resolve();
    }

    onEnable(param) {
        super.onEnable(param);
        this.onRegisterEvent(param);
    }

    onDisable(param) {
        super.onDisable(param);
        this.onUnregisterEvent(param);
    }

    /**
     * 当注册事件时。
     * @param {Object} param - 进入层级的参数。
     * @public
     */
    onRegisterEvent(param) {
        let _private = this[__.private];

        if (_private.enableMouseEvent) {
            _private.registerMouseEvent();
        }
    }

    /**
     * 当取消注册事件时。
     * @param {Object} param - 离开层级的参数。
     * @public
     */
    onUnregisterEvent(param) {
        let _private = this[__.private];
        _private.unregisterAllEvents();
    }

    /**
     * 进入层级时应用效果。
     * @param {Object} param - 进入层级的参数。
     * @public
     */
    onSetEffect(param) {

    }

    /**
     * 离开层级时重置效果。
     * @param {Object} param - 离开层级的参数。
     * @public
     */
    onResetEffect(param) {
        this.onMouseLeave();
    }

    //#endregion

    //#region Public function

    /**
     * 处理对象。
     * @param {Object} object - 要处理的对象。
     * @returns {Object|null} - 要进一步处理处理过的对象,如果没有合适的对象则返回null。
     * @public
     */
    filterObject(object) {
        const _private = this[__.private];

        if (object.tags.has('IgnoreLevel')) {
            return null;
        }

        if (object.tags.has('Ground') || object.tags.has('Misc')) {
            return null;
        }

        const parents = object.parents;

        // 如果父节点包含Ground或Misc,则不处理
        if (parents.find('tags:or(Misc,Ground)')) {
            return null;
        }

        const current = object.app.levelManager.current;

        // 兄弟节点
        if (object.isBrotherOf(current)) {
            return processResult(object);
        }

        // 查找当前层级在父节点列表中的索引
        const currentLevelIndex = parents.indexOf(current);

        // 如果传入对象的父是当前层级
        if (currentLevelIndex === 0) {
            return processResult(object);
        }
        // 如果当前层级是对象的某个父节点
        else if (currentLevelIndex > 0) {
            // 返回当前层级的直接子节点
            return processResult(parents[currentLevelIndex - 1]);
        }

        const parent = current.parent;
        const brotherIndex = parents.indexOf(parent);
        if (brotherIndex > 0) {
            return processResult(parents[brotherIndex - 1]);
        }

        return null;

        function processResult(result) {
            // 如果开启了过滤兄弟节点,并且当前是兄弟节点,则不处理
            if (!_private.enableBrotherMouseEvent && result.isBrotherOf(current)) {
                return null;
            }

            if (result != current) {
                return result;
            }

            return result;
        }
    }

    getLevelParam() {
        if (this.isRunning) {
            let _private = this[__.private];
            return _private.levelParam;
        }

        return null;
    }

    //#endregion

    //#region Public attribute

    onSetCamera(param) {
        let _private = this[__.private];

        let flyParam = _private.processFlyParam(param);
        if (_private.enableFly) {
            return this.onFlyTo(flyParam);
        } else {
            this.app.camera.fit(flyParam);
        }

        return null;
    }

    /**
     * 启动飞行动画到指定参数。
     * @param {THING.LerpFlyToArgs} flyParam - 包含飞行动画参数的对象。
     * @returns {Promise}
     * @public
     */
    onFlyTo(flyParam) {
        return new Promise((resolve, reject) => {
            let complete = flyParam.onComplete;
            flyParam.onComplete = flyParam.onStop = () => {
                complete && complete();
                resolve();
            }
            this.app.camera.flyTo(flyParam);
        });
    }

    /**
     * 检查给定路径是否涉及跳过层级。(默认情况下,兄弟节点会被跳过)
     * @param {Object} param - 包含层级变化参数的对象。
     * @returns {boolean} 返回true表示跳过,返回false表示不跳过。
     * @public
     */
    onSkipLevel(param) {
        const prevLevel = param.prev;
        const path = param.path
        if (!prevLevel || !path) {
            return false;
        }

        const finalLevel = path[path.length - 1];
        if (finalLevel && prevLevel.isBrotherOf(finalLevel)) {
            return true;
        }

        return false;
    }

    /**
     * 处理鼠标进入对象事件。(默认设置轮廓颜色)
     * @param {THING.EventCallback} ev - 鼠标进入事件。
     * @public
     */
    onMouseEnter(ev) {
        let _private = this[__.private];
        const object = ev.object;
        if (!object) {
            return;
        }

        if (LevelControlHelper.getIgnore(object, 'ignoreEvent')) {
            return;
        }

        const mapping = this.filterObject(object);
        if (!mapping) {
            return;
        }

        if (LevelControlHelper.getIgnore(mapping, 'ignoreEvent')) {
            return;
        }

        _private.mouseEnterObject = mapping;

        if (_private.enableOutlineColor) {

            this.onSetOutlineColor(mapping, _private.outlineColor);

            _private.mouseEnterObject.on(EventType.BeforeDestroy, () => {
                _private.mouseEnterObject = null;
            }, '__CampusLevelControl__Destroy__');
        }

        return mapping;
    }

    /**
     * 处理鼠标离开对象事件。(默认清除轮廓颜色)
     * @param {THING.EventCallback} ev - 鼠标离开事件。
     * @public
     */
    onMouseLeave(ev) {
        let _private = this[__.private];
        let enterObj = _private.mouseEnterObject;

        if (!enterObj) {
            return;
        }

        if (_private.enableOutlineColor) {
            this.onSetOutlineColor(enterObj, null);
        }

        enterObj.off(EventType.BeforeDestroy, '__CampusLevelControl__Destroy__');
        _private.mouseEnterObject = null;

        return enterObj;
    }

    /**
     * 进入对象层级的回调函数
     * @param {THING.Object3D} ev - 对象。
     * @public
     */
    onEnterObjectLevel(object) {
        object.app.level.change(object);
    }

    /**
     * 返回到父级层级的回调函数
     * @public
     */
    onBackToParentLevel() {
        let _private = this[__.private];
        _private.backLevel();
    }

    onSetOutlineColor(object, color) {
        if (!object) {
            return;
        }

        LevelControlHelper.traverseByIgnore(object, 'ignoreStyle', (child) => {
            if (color && !child.visible) {
                return;
            }

            const style = child.body.style;
            if (!style) {
                return;
            }

            if (!color && !style.outlineColor) {
                return;
            }

            if (!this.shouldOutline(child)) {
                return;
            }
            style.outlineColor = color;
        });
    }

    /**
     * Determines whether an object and its children should be outlined on mouse enter.
     * @param {THING.Object3D} object - The 3D object to check for outlining.
     * @returns {Boolean} Returns `true` if the object should be outlined, `false` otherwise.
     */
    shouldOutline(object) {
        return true;
    }

    /**
     * 启用/禁用相机飞行
     * @type {Boolean}
     * @public
     */
    get enableFly() {
        let _private = this[__.private];
        return _private.enableFly;
    }
    set enableFly(value) {
        let _private = this[__.private];
        _private.enableFly = value;
    }

    /**
     * 启用/禁用跳过层级
     * @type {Boolean}
     * @public
     */
    get enableSkipLevel() {
        let _private = this[__.private];
        return _private.enableSkipLevel;
    }
    set enableSkipLevel(value) {
        let _private = this[__.private];
        _private.enableSkipLevel = value;
    }

    /**
     * 启用/禁用设置轮廓颜色
     * @type {Boolean}
     * @public
     */
    get enableOutlineColor() {
        let _private = this[__.private];
        return _private.enableOutlineColor;
    }
    set enableOutlineColor(value) {
        let _private = this[__.private];
        _private.enableOutlineColor = value;
    }

    /**
     * 启用/禁用鼠标事件
     * @type {Boolean}
     * @public
     */
    get enableMouseEvent() {
        let _private = this[__.private];
        return _private.enableMouseEvent;
    }
    set enableMouseEvent(value) {
        let _private = this[__.private];

        if (_private.enableMouseEvent === value) {
            return;
        }

        _private.enableMouseEvent = value;

        if (!this.isRunning) {
            return;
        }

        if (value) {
            _private.registerMouseEvent();
        } else {
            _private.unregisterMouseEvent();
        }
    }

    /**
     * 获取/设置轮廓颜色
     * @type {Array<Number>}
     * @public
     */
    get outlineColor() {
        let _private = this[__.private];
        return _private.outlineColor;
    }
    set outlineColor(value) {
        let _private = this[__.private];
        _private.outlineColor = value;
    }

    /**
     * 获取/设置进入层级的鼠标事件(仅限 'THING.EventType.Click' 或 'THING.EventType.DBLClick')
     * @type {THING.EventType}
     * @public
     */
    get enterLevelEventType() {
        let _private = this[__.private];
        return _private.enterLevelEventType;
    }
    set enterLevelEventType(value) {
        let _private = this[__.private];
        _private.setLevelChangeEventType('enterLevelEventType', value)
    }

    /**
     * 获取/设置离开层级的鼠标事件(仅限 'THING.EventType.Click' 或 'THING.EventType.DBLClick')
     * @type {THING.EventType}
     * @public
     */
    get leaveLevelEventType() {
        let _private = this[__.private];
        return _private.leaveLevelEventType;
    }
    set leaveLevelEventType(value) {
        let _private = this[__.private];
        _private.setLevelChangeEventType('leaveLevelEventType', value)
    }

    /**
     * 优先使用缓存视角
     * @type {Boolean}
     * @public
     */
    get preferCachedViewpoint() {
        const _private = this[__.private];
        return _private.preferCachedViewpoint;
    }
    set preferCachedViewpoint(value) {
        const _private = this[__.private];
        _private.preferCachedViewpoint = !!value;
    }

    /**
     * 相机优先 (进入时最先执行相机设置)
     * @type {Boolean}
     * @public
     */
    get isCameraFirst() {
        const _private = this[__.private];
        return _private.isCameraFirst;
    }
    set isCameraFirst(value) {
        const _private = this[__.private];
        _private.isCameraFirst = !!value;
    }

    /**
     * 启用/禁用兄弟节点鼠标事件
     * @type {Boolean}
     * @public
     */
    get enableBrotherMouseEvent() {
        const _private = this[__.private];
        return _private.enableBrotherMouseEvent;
    }
    set enableBrotherMouseEvent(value) {
        const _private = this[__.private];
        _private.enableBrotherMouseEvent = !!value;
    }

    /**
     * 获取是否是默认层级控制
     * @type {Boolean}
     * @public
     */
    get isDefaultLevelControl() {
        return true;
    }

    //#endregion

}

export { DefaultLevelControl };