import Actions from "./enum/Actions.js"
import {eventsManager} from "./EventsManager.js"
import util from "./util.js"
import Person from "./enum/Person.js"
import ClickType from "./enum/ClickType.js"
import Logger from "./Logger.js"
import MessageHandleType from "./enum/MessageHandleType.js"

const logger = new Logger('actions-handler')
const QueueActions = [Actions.Transfer, Actions.ChangeSkin, Actions.GetOnVehicle, Actions.GetOffVehicle];

export default class ActionsHandler {
    constructor(room) {
        this.currentActiveAction = null
        this.room = room
    }

    // 在_handleAvatar时调用
    async avatarComponentsSync(avatarComponents) {
        this.sendData({
            data: {
                action_type: Actions.SetPlayerState,
                set_player_state_action: {
                    player_state: {
                        avatar_components: JSON.stringify(avatarComponents)
                    }
                }
            }
        })
    }

    async sendData(actionData) 
    {
        // console.error("[Action]", Actions[actionData.data.action_type])

        await this.beforeSend(actionData);
        const traceId = util.uuid();
        // 向后端发送action_type信息，获取signal和stream
        this.room.networkController.sendRtcData(le(oe({}, actionData.data), {
            trace_id: traceId,
            user_id: this.room.options.userId
        }));

        if (actionData.track === !1) {
            return Promise.resolve(null);
        }
        
        const { sampleRate=1, timeout=2e3, tag, data, special } = actionData;
        return eventsManager.track({
            timeout,
            traceId,
            event: data.action_type,
            tag,
            extra: data
        }, {
            special,
            sampleRate,
            noReport: this.room.viewMode === "serverless" || this.room.options.viewMode === "serverless"
        }).finally(()=>{
            QueueActions.includes(actionData.data.action_type) && (this.currentActiveAction = void 0)
        })
    }

    async beforeSend(e) 
    {
        var o;
        const t = (o = this.room._userAvatar) == null ? void 0 : o.isMoving
          , r = e.data.action_type;
        if (QueueActions.includes(r)) {
            if (this.currentActiveAction)
                return logger.error(`${Actions[this.currentActiveAction]} still pending, reject ${Actions[r]}`),
                Promise.reject(new FrequencyLimitError(`${Actions[r]} action request frequency limit`));
            this.currentActiveAction = r
        }
        if (t && QueueActions.includes(e.data.action_type))
            try {
                await this.stopMoving()
            } catch (a) {
                this.currentActiveAction = void 0,
                logger.error("before action stopMoving failed", a)
            }
    }

    // 点击行走至某一点时执行
    async moveTo({point, extra="", motionType}) 
    {
        return this.sendData({
            data: {
                action_type: Actions.Clicking,
                clicking_action: {
                    clicking_point: point,
                    clicking_type: ClickType.IgnoreView,
                    extra: encodeURIComponent(extra),
                    attitude: motionType
                },
                clicking_state: this.room._currentClickingState
            }
        })
    }

    transfer({renderType, player, camera, areaName, attitude, pathName, person:personType, noMedia, timeout, tag, special}) 
    {
        return this.sendData({
            data: {
                action_type: Actions.Transfer,
                transfer_action: {
                    render_type: renderType,
                    player,
                    camera,
                    areaName,
                    attitude,
                    pathName,
                    person: {
                        type: personType
                    },
                    noMedia,
                    tiles: [0, 1, 2, 4]
                }
            },
            special,
            timeout: timeout || 4e3,
            tag
        }).then(_ => (
            typeof personType != "undefined" && this.room.updateCurrentNetworkOptions({
                person: personType,
                rotationRenderType: renderType
            }),
            _
        ))
    }

    // 用于Panorama.exit()
    changeRotationRenderType({renderType, player, camera, areaName, attitude, pathName}) 
    {
        return this.transfer({
            renderType,
            player,
            camera,
            areaName,
            attitude,
            pathName,
            tag: "changeToRotationVideo"
        })
    }

    // 用于请求第一人称视角下的pano数据
    requestPanorama({camera, player, areaName, attitude, pathName, tag}, noMedia, timeout) 
    {
        return this.transfer({
            renderType: RenderType.ClientRotationPano,
            player,
            camera,
            person: Person.First,
            areaName,
            attitude,
            pathName,
            noMedia: noMedia,
            timeout: timeout,
            tag: tag || "requestPanorama",
            special: !noMedia
        })
    }

    // 用于XverseAvatar.setMotionType()。方法未执行
    setMotionType(type) 
    {
        return this.transfer({
            attitude: type,
            tag: "setMotionType"
        })
    }

    // 设置角色昵称。用于XverseAvatar.setNickname()
    setNickName(nickname) 
    {
        return this.sendData({
            data: {
                action_type: Actions.ChangeNickname,
                change_nickname_action: {
                    nickname
                }
            }
        })
    }

    // 方法未执行
    getReserveSeat({routeId, name}) 
    {
        return this.sendData({
            data: {
                action_type: Actions.ReserveSeat,
                reserve_seat_action: {
                    route_id: routeId,
                    name
                }
            }
        })
    }

    // 方法未执行
    getReserveStatus({routeId, name, need_detail}) 
    {
        return this.sendData({
            data: {
                action_type: Actions.GetReserveStatus,
                get_reserve_status_action: {
                    route_id: routeId,
                    name,
                    need_detail
                }
            },
            timeout: 2e3
        }).then(o=>o.reserveDetail)
    }

    // 停止移动。在playAnimation时调用
    stopMoving() 
    {
        return this.sendData({
            data: {
                action_type: Actions.StopMoving,
                stop_move_action: {}
            }
        })
    }

    // 方法未执行
    getOnVehicle({routeId, name, camera}) 
    {
        return this.sendData({
            data: {
                action_type: Actions.GetOnVehicle,
                get_on_vehicle_action: {
                    route_id: routeId,
                    name,
                    camera
                }
            }
        })
    }

    // 方法未执行
    getOffVehicle({renderType, player, camera}) 
    {
        return this.sendData({
            data: {
                action_type: Actions.GetOffVehicle,
                get_off_vehicle_action: {
                    render_type: renderType,
                    player,
                    camera
                }
            }
        })
    }

    // 在_handleAvatar时调用
    confirmEvent(id) 
    {
        return this.sendData({
            data: {
                action_type: Actions.ConfirmEvent,
                confirm_event_action: {
                    id
                }
            },
            track: !1
        })
    }

    // 心跳
    echo(echoMsg) 
    {
        return this.sendData({
            data: {
                action_type: Actions.Echo,
                echo_msg: {
                    echoMsg
                }
            },
            track: !1
        })
    }

    // 方法未执行
    async changeSkin(skinData) 
    {
        const { skinId, mode, landingType=LandingType.Stay, landingPoint, landingCamera, renderType, areaName, attitude, pathName, person, noMedia, timeout, 
            roomTypeId="", special } = skinData

        const newSkin = this.room.skinList.filter(skin => skin.id === skinId)[0];
        if (!newSkin) {
            const y = `skin ${skinId} is invalid`;
            return logger.error(y),
            Promise.reject(new ParamError(y))
        }

        return this.sendData({
            data: {
                action_type: Actions.ChangeSkin,
                change_skin_action: {
                    skinID: skinId,
                    mode: mode === ChangeMode.Preview ? ChangeMode.Preview : ChangeMode.Confirm,
                    skin_data_version: skinId + newSkin.versionId,
                    landing_type: landingType,
                    landing_point: landingPoint,
                    landing_camera: landingCamera,
                    render_wrapper: {
                        render_type: renderType
                    },
                    areaName,
                    attitude,
                    noMedia,
                    person,
                    pathName,
                    roomTypeId
                }
            },
            timeout: timeout || 6e3,
            special: special === void 0 ? renderType === RenderType.ClientRotationPano : special
        })
        .then(async y=>{
            if (renderType === RenderType.ClientRotationPano && y) {
                const route = await this.room.modelManager.findRoute(skinId, pathName)
                  , {camera} = util.getRandomItem(route.birthPointList) || {};
                await this.room.panorama.handleReceivePanorama(y, camera)
            }
            return this.handleChangeSkin(skinData)
        })
        .catch(e => noMedia ? this.handleChangeSkin(skinData) : Promise.reject(e))
    }

    handleChangeSkin({skinId, mode, renderType, areaName, attitude, pathName}) 
    {
        return this.room.sceneManager.staticmeshComponent.getCgMesh().show(),
        this.room.sceneManager.cameraComponent.switchToCgCamera(),
        this.room.engineProxy._updateSkinAssets(skinId).then(()=>{
            this.room.sceneManager.staticmeshComponent.getCgMesh().hide(),
            this.room.sceneManager.cameraComponent.switchToMainCamera(),
            this.room.pathManager.currentArea = areaName,
            logger.info("changeSkin _updateSkinAssets susccss"),
            this.room.updateCurrentNetworkOptions({ pathName, attitude, areaName }),
            this.room.skinChangedHook(),
            this.room.emit("skinChanged", { skin: { id: skinId }, mode }),
            renderType === RenderType.ClientRotationPano && this.room.sceneManager.cameraComponent.allowMainCameraController()
        })
    }

    // 相机旋转
    rotate({pitch, yaw}) 
    {
        if (
            this.room.disableRotate || 
            this.room.isPano || 
            (this.room._userAvatar && this.room._userAvatar._isChangingComponentsMode)
        ) return;
        
        this.sendData({
            data: {
                action_type: Actions.Rotation,
                rotation_action: {
                    vertical_move: pitch,
                    horizontal_move: -yaw
                }
            },
            sampleRate: .02
        })
    }

    // 用于XverseAvatar.turnTo()。方法未执行
    turnTo({point, timeout=2e3, offset=8} = {}) 
    {
        return this.sendData({
            data: {
                action_type: Actions.TurnTo,
                turn_to_action: {
                    turn_to_point: point,
                    offset
                }
            },
            timeout
        })
    }

    // 用于XverseAvatar.rotateTo()。方法未执行
    rotateTo({point, offset=0, speed=3} = {}) 
    {
        return this.sendData({
            data: {
                action_type: Actions.RotateTo,
                rotate_to_action: {
                    rotate_to_point: point,
                    offset,
                    speed
                }
            }
        })
    }

    broadcast({data, msgType=MessageHandleType.MHT_FollowListMulticast, targetUserIds}) 
    {
        if (msgType === MessageHandleType.MHT_CustomTargetSync && !Array.isArray(targetUserIds))
            return Promise.reject(new ParamError(`param targetUserIds is required  when msgType is ${MessageHandleType[msgType]}`));

        let sendData = {
            action_type: Actions.Broadcast,
            broadcast_action: {
                data: JSON.stringify(data),
                user_id: this.room.options.userId,
                msgType
            }
        }
        if (msgType === MessageHandleType.MHT_CustomTargetSync && Array.isArray(targetUserIds)) {
            sendData.broadcast_action.target_user_ids = targetUserIds
        }

        return this.room.actionsHandler.sendData({
            data: sendData,
            tag: data.broadcastType
        })
    }

    // 显示呼吸点。用于Debug.getPointsAndRender()
    getNeighborPoints({point, containSelf=!1, searchRange=500}) 
    {
        return this.sendData({
            data: {
                action_type: Actions.GetNeighborPoints,
                get_neighbor_points_action: {
                    point,
                    level: 1,
                    containSelf,
                    searchRange
                }
            }
        }).then(a=>a.nps)
    }

    // 方法未执行
    playCG(cgName) 
    {
        return this.sendData({
            data: {
                action_type: Actions.PlayCG,
                play_cg_action: {
                    cg_name: cgName
                }
            }
        })
    }

    // 方法未执行
    audienceToVisitor({avatarId, avatarComponents, player, camera}) 
    {
        return logger.debug("send data: audience to visitor"),
        this.sendData({
            data: {
                action_type: Actions.AudienceChangeToVisitor,
                audienceChangeToVisitorAction: {
                    avatarID: avatarId,
                    avatarComponents,
                    player,
                    camera
                }
            }
        })
    }

    // 方法未执行
    visitorToAudience({renderType, player, camera, areaName, attitude, pathName, person:personType, noMedia}) 
    {
        return logger.debug("send data: visitor to audience"),
        this.sendData({
            data: {
                action_type: Actions.VisitorChangeToAudience,
                visitorChangeToAudienceAction: {
                    transferAction: {
                        render_type: renderType,
                        player,
                        camera,
                        areaName,
                        attitude,
                        pathName,
                        person: {
                            type: personType
                        },
                        noMedia,
                        tiles: [0, 1, 2, 4]
                    }
                }
            }
        })
    }

    // 方法未执行
    removeVisitor({removeType, userIDList, extraInfo=""}) 
    {
        return logger.debug("send data: remove visitor"),
        this.sendData({
            data: {
                action_type: Actions.RemoveVisitor,
                removeVisitorAction: {
                    removeVisitorEvent: removeType,
                    userIDList,
                    extraInfo: encodeURIComponent(extraInfo)
                }
            }
        })
    }

    // 方法未执行
    getUserWithAvatar(userType, roomID) 
    {
        return logger.debug("send data: get user with avatar"),
        this.sendData({
            data: {
                action_type: Actions.GetUserWithAvatar,
                getUserWithAvatarAction: {
                    userType,
                    roomID
                }
            }
        }).then(n=>n.userWithAvatarList)
    }

    // 每2秒获取一次新数据。在Xverse_Room.afterJoinRoom()中调用
    getNewUserState(userType) 
    {
        return this.sendData({
            data: {
                action_type: Actions.GetNewUserState,
                getNewUserStateAction: {
                    userType
                }
            },
            sampleRate: 0
        }).then(o => o)
    }

    // 方法未执行
    setSyncPolicy({syncPolicy}) 
    {
        return this.sendData({
            data: {
                action_type: Actions.SetSyncPolicy,
                setSyncPolicyAction: {
                    syncPolicy
                }
            }
        })
    }

    // 拖动手柄
    joystick({degree, level=1}) 
    {
        const uuid = util.uuid();
        let angle = -degree + 90 + 360;
        angle >= 360 && (angle -= 360);

        return this.sendData({
            data: {
                action_type: Actions.Joystick,
                dir_action: {
                    move_angle: angle,
                    speed_level: level
                },
                trace_id: uuid,
                user_id: this.room.options.userId,
                packet_id: uuid
            },
            track: !1
        })
    }
}