import { DefaultLevelControl } from "./DefaultLevelControl";
import { LevelControlHelper } from "../LevelControlHelper";
import { Selector } from "@uino/thing";

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

/**
 * @class BuildingLevelControl
 * 园区建筑层级控制类
 * @extends THING.DefaultLevelControl
 * @public
 */
class BuildingLevelControl extends DefaultLevelControl {

    static levelCondition = 'tags:or(Building)';

    static levelName = 'building';

    constructor(options) {
        super(options);

        let _private = this[__.private] = {};
        _private.hideObjects = [];
        _private.enableExpandFloors = false;
        _private.notCheckOutline = false;
    }

    async onEnter(param) {
        let _private = this[__.private];
        let prev = param.prev;

        if (prev && prev.isCampus) {
            LevelControlHelper.setEnvMap(prev, 'inner');
        }

        let promise = super.onEnter(param);

        const building = param.current;
        if (_private.enableExpandFloors) {
            building.expandFloors();
        }

        return promise;
    }

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

        super.onLeave(param);

        return new Promise((resolve) => {
            const building = param.current;
            if (_private.enableExpandFloors && building.expanded) {
                building.unexpandFloors({
                    duration: 1, onComplete: resolve
                });
            } else {
                resolve();
            }
        });
    }

    onSetEffect(param) {
        const object = param.current;
        if (!object) {
            return;
        }

        let _private = this[__.private];

        // Hide brothers
        const brothers = object.brothers;
        brothers.forEach(brother => {
            LevelControlHelper.traverseByIgnore(brother, 'ignoreVisible', (child) => {
                child.setVisible(false, false);
            });
        });

        object.children.forEach(child => {
            const objTags = child.tags;

            if (objTags.has('Floor')) {
                child.setVisible(true, false);

                LevelControlHelper.traverseByIgnore(child, 'ignoreVisible', (floorChild) => {
                    if (floorChild == child) {
                        return;
                    }
                    const floorChildTags = floorChild.tags;
                    if (floorChildTags.has('BuildingElement')) {
                        floorChild.setVisible(!(floorChildTags.has('RoomRoof') || floorChildTags.has('Roof')), false);
                    } else {
                        floorChild.setVisible(true, false);
                    }
                });
            }
            else if (objTags.has('Facade') || objTags.has('Roof')) {
                LevelControlHelper.traverseByIgnore(child, 'ignoreVisible', (facadeChild) => {
                    facadeChild.setVisible(false, false);
                });
            }
            else {
                LevelControlHelper.traverseByIgnore(child, 'ignoreVisible', (childchild) => {
                    childchild.setVisible(true, false);
                });
            }
        });

        // Hide uncles
        let parent = object.parent;
        const uncles = parent.brothers.queryByTags('Campus');
        uncles.forEach(uncle => {
            LevelControlHelper.traverseByIgnore(uncle, 'ignoreVisible', (child) => {
                if (child.isLight || child.isCamera) {
                    return;
                }

                _private.hideObjects.push(child);
                child.setVisible(false, false);
            });
        });

    }

    onResetEffect(param) {
        let current = param['current'];
        let next = param['next'];
        if (next && current.parent != next) {
            return;
        }

        let _private = this[__.private];
        _private.hideObjects.forEach(element => {
            element.setVisible(true, false);
        });
        _private.hideObjects.length = 0;
    }

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

        let _private = this[__.private];
        const be = object.find('tags:or(Wall,BuildingElement)');
        _private.notCheckOutline = !!!be;

        super.onSetOutlineColor(object, color);
    }

    shouldOutline(object) {
        let _private = this[__.private];
        if (_private.notCheckOutline) {
            return true;
        }

        if (object) {
            // Check type
            if (object.tags.has('Door') || object.tags.has('Window')) {
                return true;
            }

            // Check tags
            let tags = object.tags;
            if (tags.has('Wall')) {
                return true;
            }
            else if (tags.has('Slab')) {
                return true;
            }
            else if (tags.has('BuildingElement')) {
                return true;
            }
        }

        return false;
    }

    /**
     * 启用/禁用进入建筑物展开楼层和离开建筑物收起楼层
     * @type {Boolean}
     * @public
     */
    get enableExpandFloors() {
        let _private = this[__.private];
        return _private.enableExpandFloors;
    }
    set enableExpandFloors(value) {
        let _private = this[__.private];
        _private.enableExpandFloors = value;
    }

    onFlyTo(flyParam) {
        const target = flyParam.target;

        if (target && target.isObject3D) {
            const floors = target.children.queryByTags('Floor');
            const walls = new Selector();
            floors.forEach(floor => {
                floor.children.forEach(child => {
                    if (child.tags && child.tags.has('Wall')) {
                        walls.push(child);
                    }
                })
            });

            if (walls.length === 0) {
                flyParam.target = target;

                return super.onFlyTo(flyParam);;
            }

            const boundingBox = walls.getBoundingBox();

            flyParam.position = LevelControlHelper.getNearestObjectPosition({
                target: target,
                boundingBox: boundingBox,
                camera: this.app.camera,
            });

            flyParam.target = boundingBox.center;
        }

        return super.onFlyTo(flyParam);
    }

}

export { BuildingLevelControl };