//const log$E = new Logger$1("Avatar")

import BillboardStatus from "./enum/BillboardStatus.js"
import {avatarLoader} from "./XAvatarLoader.js"
import XAnimationController from "./XAnimationController.js"
import XAvatarComopnent from "./XAvatarComopnent.js"
import XStateMachine from "./XStateMachine.js"
import XAvatarBillboardComponent from "./XAvatarBillboardComponent.js"

const castRayOffsetY = .01;
const castRayTeleportationOffset = 10;

export default class XAvatar {
    constructor({id, avatarType, priority, avatarManager, assets, status}) {
        this.id = "-1",
        this.priority = 0,
        this.isRender = !1,
        this.distLevel = 0,
        this.isInLoadingList = !1,
        this.isHide = !1,
        this.clothesList = [],
        this.isSelected = !1,
        this.pendingLod = !1,
        this._previousReceivedPosition = new BABYLON.Vector3(0,1e4,0),
        this.rootNode = void 0,
        this.distToCam = 1e11,
        this.enableNickname = !0,
        this.distance = 1e11,
        this.isCulling = !1,
        this.reslevel = 0,
        this.isInLoadingQueue = !1,
        this._scene = void 0,
        this._transparent = 0,

        this.hide = ()=>{
            this.isHide = !0
            this._hide()
            return !this.isRender
        },

        this._show = ()=>{
            this.isHide || (
                this.setIsPickable(!0),
                this.bbComponent._attachmentObservers.forEach((b,k)=>{
                    k.setEnabled(!0)
                }),
                this.priority == 0 && (
                    this.rootNode.setEnabled(!0),
                    this.isRender = !0,
                    this.avatarManager._updateBillboardStatus(this, BillboardStatus.SHOW),
                    this.component.accessories.forEach(b=>{
                        b.rootComponent.setEnabled(!0)
                    }),
                    this.controller == null || this.controller.playAnimation(this.controller.onPlay, this.controller.loop)
                ),
                this.component.accessories.forEach(b=>{
                    b.rootComponent.setEnabled(!0)
                })
            )
        },

        this.show = ()=>{
            this.isHide = !1
            this._show()
            return !!this.isRender
        },

        this.setAnimations = _=>{
            this.controller.animations = _
        },
        
        this.attachToAvatar = ( _, b=!1, k={x:0,y:0,z:0}, j=!1, $, _e ) => {
            this.bbComponent.attachToAvatar(this, _, b, k, j, _e)
        },

        this.detachFromAvatar = ( _, b=!1 )=>{
            this.bbComponent.detachFromAvatar(this, _, b)
        },
        this.getBbox = (_={})=>{
            this.bbComponent.getBbox(this, _)
        },

        this.id = id,
        this._avatarManager = avatarManager,
        this._scene = this.avatarManager.scene,
        this.clothesList = assets,
        this._avatarType = avatarType,
        this.priority = priority || 0,
        
        this.controller = new XAnimationController(this),
        this.component = new XAvatarComopnent,
        this.stateMachine = new XStateMachine(this._scene),
        this.bbComponent = new XAvatarBillboardComponent(this._scene),
        this.rootNode = new BABYLON.TransformNode(id, this._avatarManager.scene),

        this._avatarScale = status.avatarScale == null ? 1 : status.avatarScale,
        this._avatarRotation = status.avatarRotation == null ? { pitch: 0, yaw: 0, roll: 0 } : status.avatarRotation,
        this._avatarPosition = status.avatarPosition == null ? { x: 0, y: 0, z: 0 } : status.avatarPosition,
        this._isRayCastEnable = avatarSetting.isRayCastEnable,
        
        this.setPosition(this._avatarPosition, !0),
        this.setRotation(this._avatarRotation),
        this.setScale(this.scale),
        this._isRayCastEnable = avatarSetting.isRayCastEnable,

        this._scene.registerBeforeRender(()=>{
            this.tick()
        })
    }

    tick() {
        this.cullingTick()
    }

    cullingTick() {
        this.isCulling && (this.rootNode == null || this.rootNode.getChildMeshes().forEach(i=>{
            this.distToCam < 50 ? i.visibility = 0 : i.visibility = this._transparent
        }))
    }

    setTransParentThresh(e) {
        this._transparent = e
    }

    get isNameVisible() {
        return this.bbComponent.isNameVisible
    }

    get isBubbleVisible() {
        return this.bbComponent.isBubbleVisible
    }

    get isGiftButtonsVisible() {
        return this.bbComponent.isGiftButtonsVisible
    }

    get words() {
        return this.bbComponent.words
    }

    get nickName() {
        return this.bbComponent.nickName
    }

    get giftButtons() {
        return this.bbComponent.giftButtons
    }

    get bubble() {
        return this.bbComponent.bubble
    }

    get nameBoard() {
        return this.bbComponent.nameBoard
    }

    get avatarManager() {
        return this._avatarManager
    }

    set withinVisibleRange(e) {
        this.bbComponent.withinVisualRange = e
    }

    setNicknameStatus(e) {
        return this.bbComponent.setNicknameStatus(e)
    }

    setBubbleStatus(e) {
        return this.bbComponent.setBubbleStatus(e)
    }

    setButtonsStatus(e) {
        return this.bbComponent.setBubbleStatus(e)
    }

    setGiftButtonsVisible(e) {
        return this.bbComponent.setGiftButtonsVisible(e)
    }

    get avatarType() {
        return this._avatarType
    }

    attachBody(e) {
        return this.component.addBodyComp(this, e)
    }

    attachDecoration(e) {
        return this.component.addClothesComp(this, e)
    }
    
    detachDecoration(e) {
        return this.component.clearClothesComp(e)
    }

    detachDecorationAll() {
        return this.component.clearAllClothesComps()
    }

    get skeleton() {
        return this.component.skeleton
    }

    get position() {
        return this._avatarPosition
    }

    get rotation() {
        return this._avatarRotation
    }

    get scale() {
        return this._avatarScale
    }

    _hide_culling() {
        this.bbComponent.updateBillboardStatus(this, BillboardStatus.HIDE),
        this.isCulling = !0
    }

    _show_culling() {
        this.isCulling && (this.rootNode && this.rootNode.getChildMeshes().forEach(e=>{
            e.visibility = 1
        }),
        this.bbComponent.updateBillboardStatus(this, BillboardStatus.SHOW),
        this.isCulling = !1)
    }

    _hide() {
        !this.isHide || (this.setIsPickable(!1),
        this.bbComponent._attachmentObservers.forEach((e,i)=>{
            i.setEnabled(!1)
        }
        ),
        this.priority == 0 ? (this.rootNode.setEnabled(!1),
        this.isRender = !1,
        this.bbComponent.updateBillboardStatus(this, BillboardStatus.HIDE),
        this.component.accessories.forEach(e=>{
            e.rootComponent.setEnabled(!1)
        }
        )) : this.isRender && (this.avatarManager.currentLODUsers[this.distLevel]--,
        this.removeAvatarFromScene()),
        this.component.accessories.forEach(e=>{
            e.rootComponent.setEnabled(!1)
        }))
    }

    rotate(e, i, o) {
        return this.stateMachine.roll(this, e, i, o)
    }

    set isRayCastEnable(e) {
        this._isRayCastEnable = e
    }

    get isRayCastEnable() {
        return this._isRayCastEnable
    }

    getAvatarId() {
        return this.id
    }

    getAvaliableAnimations() {
        const e = avatarLoader.avaliableAnimation.get(this.avatarType);
        return e || []
    }

    // todo i用于控制是否接触地面。目前设默认为true，实时检测模型地面高度
    setPosition(e, i=!0) {
        this._avatarPosition = e;
        if (this.rootNode) {
            const o = ue4Position2Xverse(this._avatarPosition);
            let s = !1;
            if(this.avatarManager.getMainAvatar() ){
                if(this.id == this.avatarManager.getMainAvatar().id){
                    Math.abs(o.y - this._previousReceivedPosition.y) > castRayOffsetY && (s = !0);
                    o.subtract(this._previousReceivedPosition).length() > castRayTeleportationOffset && (s = !0)
                }
            }
            if(this._isRayCastEnable) 
                if(s || i) {
                    // 检测模型地面高度
                    this._castRay(e).then(c=>{
                        this.rootNode.position = o;
                        this.rootNode.position.y -= c;
                    }
                    ).catch(c=>{
                        Promise.reject(c)
                    }) 
                } else {
                    // 保持人物高度不变
                    this.rootNode.position.x = o.x
                    this.rootNode.position.z = o.z
                }
            else this.rootNode.position = o
            this._previousReceivedPosition = o.clone()
        }
        return Promise.resolve(e)
    }

    setRotation(e) {
        if (this._avatarRotation = e,
        this.rootNode) {
            const i = {
                pitch: e.pitch,
                yaw: e.yaw + 180,
                roll: e.roll
            }
              , o = ue4Rotation2Xverse(i);
            this.rootNode.rotation = o
        }
    }

    setAvatarVisible(e) {
        this.rootNode && (this.rootNode.setEnabled(e),
        this.rootNode.getChildMeshes().forEach(i=>{
            i.setEnabled(e)
        }
        ))
    }

    setScale(e) {
        this._avatarScale = e,
        this.rootNode && (this.rootNode.scaling = new BABYLON.Vector3(e,e,e)),
        this.bbComponent.bbox && this.getBbox()
    }

    _removeAvatarFromScene() {
        var e, i;
        this.isRender = !1,
        (e = this.controller) == null || e.detachAnimation(),
        this.component.dispose(this),
        (i = this.avatarManager.sceneManager) == null || i.lightComponent.removeShadow(this),
        this.component.accessories.forEach(o=>{
            o.rootComponent.setEnabled(!1)
        })
    }

    removeAvatarFromScene() {
        this._removeAvatarFromScene(),
        this._disposeBillBoard()
    }

    _disposeBillBoard() {
        this.bbComponent.disposeBillBoard(this)
    }

    addComponent(e, i, o, s) {
        return i === "pendant" ? this.component.attachPendant(this, e) : this.component.changeClothesComp(this, e, i, o, s)
    }

    removeComponent(e, i) {
        if (e === "pendant")
            i ? this.component.detachPendant(i) : this.component.accessories.forEach((o,s)=>{
                this.component.detachPendant(s)
            }
            );
        else {
            const o = this.component.resourceIdList.find(s=>s.type == e);
            o && (this.detachDecoration(o),
            this.clothesList = this.clothesList.filter(s=>s.type != e))
        }
    }

    getComponentByType(e, i) {
        if (e === "pendant")
            if (i) {
                const o = this.component.accessories.get(i);
                return o || []
            } else
                return Array.from(this.component.accessories).map(o=>o[1]);
        else
            return this.component.resourceIdList.find(o=>o.type == e)
    }

    _castRay(e) {
        return new Promise((i,o)=>{
            var et;
            const s = ue4Position2Xverse(e)
              , c = new BABYLON.Vector3(0,-1,0)
              , d = 1.5 * this.scale
              , _ = 100 * d
              , b = d
              , k = new BABYLON.Vector3(s.x,s.y + b,+s.z)
              , j = new BABYLON.Ray(k,c,_)
              , $ = (et = this.avatarManager.sceneManager) == null ? void 0 : et.getGround(e);
            if (!$ || $.length <= 0)
                return log$F.warn(`\u89D2\u8272 id= ${this.id} \u627E\u4E0D\u5230\u5730\u9762\uFF0C\u5F53\u524D\u9AD8\u5EA6\u4E3A\u4E0B\u53D1\u9AD8\u5EA6`),
                i(0);
            let _e = j.intersectsMeshes($);
            if (_e.length > 0)
                return i(_e[0].distance - b);
            if (c.y = 1,
            _e = j.intersectsMeshes($),
            _e.length > 0)
                return i(-(_e[0].distance - b))
        })
    }

    setPickBoxScale(e) {
        return this.bbComponent.setPickBoxScale(e)
    }

    setIsPickable(e) {
        return this.bbComponent.setIsPickable(this, e)
    }

    createPickBoundingbox(e) {
        return this.bbComponent.createPickBoundingbox(this, e)
    }

    scaleBbox(e) {
        this.bbComponent.bbox && this.bbComponent.bbox.scale(e)
    }

    rotateTo(e, i, o) {
        return this.stateMachine.rotateTo(this, e, i, o)
    }

    faceTo(e, i) {
        return this.stateMachine.lookAt(this, e, i)
    }

    removeObserver() {
        this.stateMachine.disposeObsever()
    }

    moveHermite(e, i, o, s, c, d) {
        return this.stateMachine.moveToHermite(this, e, i, o, s, c, d)
    }

    moveCardinal(e, i, o, s, c, d, _=!1) {
        return this.stateMachine.moveToCardinal(this, e, i, o, s, c, d, _)
    }

    move(e, i, o, s, c, d=!1) {
        return this.stateMachine.moveTo(this, e, i, o, s, c, d)
    }

    initNameboard(e=1) {
        return this.bbComponent.initNameboard(this, e)
    }

    initBubble(e=1) {
        return this.bbComponent.initBubble(this, e)
    }

    say(e, {
        id, isUser, background, 
        font="Arial", fontsize=38, fontcolor="#ffffff", fontstyle="bold", linesize=22, linelimit, 
        offsets={x: 0, y: 0, z: 40}, scale=this._avatarScale, compensationZ=11.2, reregistAnyway=!0
    }) {
        return this.bbComponent.say(this, e, {
            id, isUser, background, 
            font, fontsize, fontcolor, fontstyle, linesize, linelimit,
            offsets, scale, compensationZ, reregistAnyway
        })
    }
    
    silent() {
        return this.bbComponent.silent()
    }
    
    setNickName(e, {
        id, isUser, background, 
        font="Arial", fontsize=40, fontcolor="#ffffff", fontstyle="bold", linesize=22, linelimit, 
        offsets={x: 0, y: 0, z: 15}, scale=this._avatarScale, compensationZ=0, reregistAnyway=!1
    }) {
        return this.bbComponent.setNickName(this, e, {
            id, isUser, background,
            font, fontsize, fontcolor, fontstyle, linesize, linelimit,
            offsets, scale, compensationZ, reregistAnyway
        })
    }

    generateButtons(e=null, i=this._avatarScale, o=85) {
        return this.bbComponent.generateButtons(this, e, i, o)
    }

    clearButtons() {
        return this.bbComponent.clearButtons()
    }

    attachExtraProp(e, i, o, s) {
        return this.component.addDecoComp(this, e, i, o, s)
    }
    
    showExtra(e) {
        return this.component.showExtra(e)
    }

    hideExtra(e) {
        return this.component.hideExtra(e)
    }

    disposeExtra() {
        return this.component.disposeExtra()
    }

    getSkeletonPositionByName(e) {
        var i;
        if (this.skeleton) {
            const o = this.skeleton.bones.find(s=>s.name.replace("Clone of ", "") == e);
            if (o && o.getTransformNode() && ((i = o.getTransformNode()) == null ? void 0 : i.position)) {
                const s = o.getTransformNode().position;
                return xversePosition2Ue4({
                    x: s.x,
                    y: s.y,
                    z: s.z
                })
            }
        }
    }

    shootTo(e, i, o=2, s=10, c={
        x: 0,
        y: 0,
        z: 150
    }) {
        return this.stateMachine.sendObjectTo(this, e, i, o, s, c)
    }
}