﻿const swordGuid: string[] = [
    "31178", "102928", "178618", "269895", "31710", "31712",
    "218730", "31734", "300434", "297029", "122952", "31726",
    "122949", "29050", "122956", "122955", "186519", "122959",
    "118145", "29052", "29057", "103078", "122961", "31179"
]

/**
 * 拖手技
 */
export class SwordDevil {

    private centerPos: Vector

    public sandevistan: Sandevistan

    private actor: Character


    constructor() {
        this.init()
    }
    private async init() {
        for (let guid of swordGuid) {
            AssetUtil.asyncDownloadAsset(guid)
        }
        this.actor = await GameObject.asyncSpawn("Character") as Character
        await this.actor.asyncReady()
        this.actor.setDescription(Player.localPlayer.character.getDescription())
        this.sandevistan = new Sandevistan(this.actor)
        EffectService.playOnGameObject("153613", this.actor, { duration: 200, scale: new Vector(3, 3, 3) })
    }

    /**
     * 一阶段（光环汇聚）
     */
    public async stage1() {
        SoundTool.instance.playSound("200362")
        // 获取中心位置（每次进入一阶段，只重置一次）
        this.centerPos = Player.localPlayer.character.worldTransform.position.clone().add(new Vector(0, 0, 500))
        // 创建8个光球特效
        let effectCir: CircularObj[] = []
        for (let i = 0; i < 8; i++) {
            let effect = await GameObject.asyncSpawn("157110") as Effect
            // 设置颜色
            effect.setColor("Color", LinearColor.colorHexToLinearColor("9100FFFF"))
            // 设置持续时间
            effect.duration = 3.5
            // 播放特效
            effect.play()
            // 创建旋转体
            let cir = new CircularObj(effect, this.centerPos, 500, i * (360 / 8))
            // 圆周运动
            cir.circular(1, 3000)
            cir.shrink(0, 2500)
            effectCir.push(cir)
        }
        // 延迟后进入下个阶段
        await TimeUtil.delaySecond(3)
        // 删除光球
        effectCir.forEach((value) => {
            value.destory()
        })
        this.stage2()
    }

    /**
     * 二阶段（剑展开 以及 剑落下）
     */
    private async stage2() {
        SoundTool.instance.playSound("207720")
        SoundTool.instance.playSound("200434")
        // 中心光效
        EffectService.playAtPosition("89074", this.centerPos, { loopCount: 1, scale: new Vector(1, 1, 1) })
        // 创建24把剑
        let swordArray: CircularObj[] = []
        // 计数变量
        let index = 0
        // 创建剑，并让剑进行旋转收缩
        for (let guid of swordGuid) {
            let obj = await GameObject.asyncSpawn(guid)
            // 设置旋转
            obj.worldTransform.rotation = new Rotation(0, 180, 0)
            // 创建旋转体
            let cir = new CircularObj(obj, this.centerPos, 0, 360 / 24 * index)
            // 圆周运动
            cir.circular(0.5, 3000)
            // 收缩运动
            cir.shrink(500, 2000)
            swordArray.push(cir)
            index++
        }
        // 等待剑旋转结束
        await TimeUtil.delaySecond(3.1)
        SoundTool.instance.playSound("199574")
        // 计数变量
        let i = 0
        // 遍历所有剑
        for (let cir of swordArray) {
            // 给剑添加特效
            cir.addEffect("154050", 13, LinearColor.colorHexToLinearColor("0033FFFF"))
            // 单数剑进行下落
            if (i % 2 != 0) {
                cir.down(500, 100)
            }
            i++
        }
        // 添加一个剑阵特效
        EffectService.playAtPosition("88783", this.centerPos.clone().add(new Vector(0, 0, -570)), { duration: 13, scale: new Vector(8, 8, 1) })
        // 延迟0.1秒开始下一个阶段
        await TimeUtil.delaySecond(0.1)
        this.stage3(swordArray)
    }



    private async stage3(swordArray: CircularObj[]) {
        swordArray.forEach((value, index) => {
            value.freshFixedPosition()
            if (index % 2 == 0) {
                value.idleAnim()
            }
        })
        EffectService.playAtPosition("271322", this.actor.worldTransform.position)
        this.actor.complexMovementEnabled = false
        this.actor.loadAnimation("280691").play()


        const actorCir = new CircularObj(this.actor, this.centerPos, 500, 0)
        EffectService.playAtPosition("271322", this.actor.worldTransform.position)
        actorCir.obj.worldTransform.position = this.centerPos.clone()

        await TimeUtil.delaySecond(0.1)
        this.sandevistan.playAnim("33572")

        // actorCir.moveTo(swordArray[0].obj.worldTransform.position.clone(), 1000, false)

        this.cameraMove(this.centerPos.clone().add(new Vector(0, 500, 0)), swordArray)
        await TimeUtil.delaySecond(2)

        this.sandevistan.follow(50, 900)
        // 挥砍顺序
        const crossIndex: number[] = [
            0, 11, 22, 9, 20, 7,
            16, 1, 14, 5, 18, 3,
            12, 23, 10, 21, 6, 15,
            2, 13, 4, 17, 8, 19
        ]



        // 初始间隔
        let gap = 1000
        for (let i = 0; i < crossIndex.length - 1; i++) {
            const sword = swordArray[crossIndex[i]]
            const nextSword = swordArray[crossIndex[i + 1]]
            SoundTool.instance.playSound("137574")
            this.sandevistan.playAnim("29747")
            this.sandevistan.currentWeaponGuid = sword.obj.assetId
            sword.obj.setVisibility(false)
            actorCir.moveTo(nextSword.fixedPosition.clone(), 300)

            this.joinEffect(sword.fixedPosition, nextSword.fixedPosition)
            await TimeUtil.delaySecond(gap / 1000)
            gap *= 0.9
        }

        swordArray[19].obj.setVisibility(false)
    }

    private async joinEffect(start: Vector, end: Vector) {
        const dir = end.clone().subtract(start).toRotation()
        const center = start.clone().add(dir.getForce().multiply(200))
        // EffectService.playAtPosition("88760", start.clone(), { rotation: dir, scale: new Vector(1, 1, 1), duration: 20 }) // 291299
        EffectService.playAtPosition("291299", center.clone(), { rotation: dir, scale: new Vector(2, 2, 2) }) // 291299
    }

    private async cameraMove(pos: Vector, swordArray: CircularObj[]) {
        await TimeUtil.delaySecond(12)
        this.sandevistan.stopFollow()
        this.sandevistan.hideAllShadow()
        this.lastAttack()
        await TimeUtil.delaySecond(1)
        // Player.localPlayer.character.complexMovementEnabled = true
        swordArray.forEach((value) => {
            value.destory()
        })
    }

    private lastAttack() {
        const char = this.actor
        const dir = this.centerPos.clone().subtract(char.worldTransform.position.clone()).toRotation()
        char.worldTransform.rotation = new Rotation(0, 0, dir.z)
        char.loadAnimation("268667").play()
        EffectService.playAtPosition("297400", this.centerPos.clone(), { scale: new Vector(2), rotation: new Rotation(0, 90, 0) })
        EffectService.playAtPosition("297400", this.centerPos.clone(), { scale: new Vector(2), rotation: new Rotation(0, 90, 90) })
        EffectService.playAtPosition("297233", this.centerPos.clone().add(new Vector(0, 0, -500)), { scale: new Vector(2) })
        const camera = Camera.currentCamera
        const rot = this.actor.worldTransform.rotation.clone()
        rot.y = -20
        camera.springArm.length = 800
        SoundTool.instance.playSound("137576")

        setTimeout(() => {
            this.destory()
        }, 1000);
    }

    public destory() {
        this.actor.destroy()
    }

}

/**
 * 义体装置 -- 斯安威斯坦
 */
export class Sandevistan {
    /**主角 */
    public actor: Character

    /**影子列表 */
    private shadowArray: Shadow[] = []

    /**跟随计时器 */
    private followInter

    public currentWeaponGuid: string = ""

    constructor(actor: Character) {
        this.actor = actor
    }

    public playAnim(guid: string) {
        this.actor.loadAnimation(guid).play()
    }

    public hideAllShadow() {
        for (let shadow of this.shadowArray) {
            shadow.npc.setVisibility(false)
        }
    }

    /**
     * 影子开始跟随
     * @param gap 间隔多久出现一个影子（毫秒）
     * @param duration 影子存在时间（毫秒）
     */
    public follow(gap: number, duration: number) {
        // 先停止上一个跟随
        this.stopFollow()
        // 记录进度
        let progress = 0
        // 开始循环
        this.followInter = setInterval(() => {
            // 获取一个影子
            let shadow = this.getShadow()
            // 默认动画ID
            let guid = "159213"
            // 默认动画时长
            let length = 2
            // 获取主角当前的动画
            const anim = this.actor.currentAnimation
            // 如果动画存在，且没有正在播放
            if (anim != null && anim.isPlaying) {
                // 获取动画ID
                guid = anim.assetId
                // 获取动画长度
                length = anim.length
            }
            // 在角色的位置展示影子
            shadow.show(duration, guid, progress / (length * 1000), this.actor.worldTransform.position, this.actor.worldTransform.rotation)
            shadow.freshWeapon(this.currentWeaponGuid)
            // 更新进度
            progress += gap
            // 如果进度满了
            if (progress > length * 1000) {
                // 重置进度
                progress = 0
            }
        }, gap)
    }

    /**停止跟随 */
    public stopFollow() {
        if (this.followInter) {
            clearInterval(this.followInter)
        }
    }

    /**获取一个影子 */
    private getShadow() {
        let shadow = this.shadowArray.pop()
        if (shadow == null) {
            shadow = new Shadow(this.actor)
            shadow.onHide.add(() => {
                this.shadowArray.push(shadow)
            })
        }
        return shadow
    }

    public destory() {
        this.shadowArray.forEach((value)=>{
            value.npc.destroy()
        })
    }
}


class Shadow {
    /**角色对象 */
    public npc: Character

    /**直线运动Tween */
    private moveTween: Tween<Vector>

    /**当隐藏时 */
    public readonly onHide: Action = new Action()

    /**隐藏倒计时 */
    private hideTimeOut

    /**当前动画 */
    private currentAnim: Animation

    private currentWeapon: GameObject

    private currentColor: LinearColor

    private actor: Character

    constructor(actor: Character) {
        this.actor = actor
        this.init()
    }

    private async init() {
        // 实例化角色对象
        let npc = await GameObject.asyncSpawn("Character") as Character
        // 等待角色加载完毕
        await npc.asyncReady()
        npc.setDescription(this.actor.getDescription())
        // 关闭碰撞
        npc.collisionWithOtherCharacterEnabled = false
        // 关闭复杂移动
        npc.complexMovementEnabled = false
        // 隐藏头顶昵称
        npc.displayName = ""
        this.npc = npc
        // 设置颜色
        this.setColor(LinearColor.colorHexToLinearColor("#009FAB"))
    }

    /**
     * 展示影子
     * @param duration 持续时间（毫秒）
     * @param guid 动画guid
     * @param percent 动画百分比
     */
    public async show(duration: number, guid: string, percent: number, pos: Vector, rot: Rotation) {
        // 清除上一个倒计时
        if (this.hideTimeOut) {
            clearTimeout(this.hideTimeOut)
        }
        // 等待角色加载完毕
        await this.readyNpc()
        // 显示角色
        this.npc.setVisibility(true)
        // 设置位置
        this.npc.worldTransform.position = pos
        // 设置旋转
        this.npc.worldTransform.rotation = rot
        // 冻结动画
        this.freezeAnim(guid, percent)
        // 延迟一段时间隐藏角色
        this.hideTimeOut = setTimeout(() => {
            this.npc.setVisibility(false)
            this.onHide.call()
        }, duration);
    }

    /**
     * 冻结动画
     * @param guid 动画Guid
     * @param percent 百分比
     */
    public async freezeAnim(guid: string, percent: number) {
        await this.readyNpc()
        await this.reLoadAnim(guid)
        this.currentAnim.stop()
        this.currentAnim.startTime = this.currentAnim.length * percent
        this.currentAnim.play()
        this.currentAnim.pause()
    }

    /**
     * 重新加载动画
     * @param guid 动画guid
     * @returns 
     */
    private async reLoadAnim(guid: string) {
        // 如果动画已经存在，且与此次加载的动画不一致
        if (this.currentAnim != null && this.currentAnim.assetId == guid) { return }
        // 下载资源
        await AssetUtil.asyncDownloadAsset(guid)
        // 加载动画
        this.currentAnim = this.npc.loadAnimation(guid)
    }

    /**
     * 模型变色
     * @param color 颜色值
     */
    public async setColor(color: LinearColor) {
        this.currentColor = color
        await this.readyNpc()
        v2Color(this.npc, color)
    }

    /**
     * 挂载物品
     */
    public async attach(obj: GameObject, slot: HumanoidSlotType) {
        await this.readyNpc()
        this.npc.attachToSlot(obj, slot)
        obj.localTransform.position = Vector.zero
    }

    /**
     * 直线运动
     * @param pos 目标位置
     * @param time 总时长
     */
    public moveTo(pos: Vector, time: number) {
        if (this.moveTween) { this.moveTween.stop() }
        this.moveTween = new Tween(this.npc.worldTransform.position.clone()).to(pos.clone(), time).onUpdate((value) => {
            this.npc.worldTransform.position = value
        }).start().onComplete(() => {
            this.npc.setVisibility(false)
        })
    }

    public async freshWeapon(guid: string) {
        if (guid == "") {
            if (this.currentWeapon) {
                this.currentWeapon.destroy()
            }
            this.currentWeapon = null
            return
        }

        if (this.currentWeapon && this.currentWeapon.assetId != guid) {

        } else {
            if (this.currentWeapon) {
                this.currentWeapon.destroy()
            }
            this.currentWeapon = await GameObject.asyncSpawn(guid);
            // (this.currentWeapon as Model).color = this.currentColor
            await this.readyNpc()
            this.npc.attachToSlot(this.currentWeapon, HumanoidSlotType.RightHand)
        }
    }

    /**等待角色加载完毕 */
    private readyNpc() {
        return new Promise((res) => {
            if (this.npc != null) {
                res(this.npc)
            } else {
                let handle = setInterval(() => {
                    if (this.npc != null) {
                        res(this.npc)
                        clearInterval(handle)
                    }
                }, 30)
            }
        })
    }

}


class CircularObj {
    public obj: GameObject
    private center: Vector
    private radius: number
    private startAngle: number

    /**圆周运动Tween */
    private circularTween: Tween<{ angle: number }>
    /**收缩运动Tween */
    private shrinkTween: Tween<{ radius: number }>
    /**下落运动Tween */
    private downTween: Tween<{ height: number }>
    /**直线运动Tween */
    private moveTween: Tween<Vector>
    /**Idle运动Tween */
    private idleTween: Tween<{ ry: number, pz: number }>
    private idlePos = Vector.zero
    private idleRot = Rotation.zero
    /**位置缓存 */
    private tempPosition = Vector.zero

    /**
     * 创建进行旋转的物体
     * @param obj 物体
     * @param center 中心点位置
     * @param radius 半径
     * @param startAngle 初始角度（°）
     */
    constructor(obj: GameObject, center: Vector, radius: number, startAngle: number) {
        this.obj = obj
        this.center = center.clone()
        this.radius = radius
        this.startAngle = startAngle
        let x = center.x + this.center.x + Math.cos(startAngle * (Math.PI / 180)) * this.radius
        let y = center.y + this.center.y + Math.sin(startAngle * (Math.PI / 180)) * this.radius
        obj.worldTransform.position = new Vector(x, y, center.z)
    }

    /**
     * 圆周运动
     * @param cycles 圈数
     * @param time 总时长
     */
    public circular(cycles: number, time: number) {
        if (this.circularTween) { this.circularTween.stop() }
        this.circularTween = new Tween({ angle: this.startAngle }).to({ angle: this.startAngle + 360 * cycles }, time).onUpdate((value) => {
            if (!this.obj) { return }
            this.tempPosition.x = this.center.x + Math.cos(value.angle * (Math.PI / 180)) * this.radius
            this.tempPosition.y = this.center.y + Math.sin(value.angle * (Math.PI / 180)) * this.radius
            this.tempPosition.z = this.center.z
            this.obj.worldTransform.position = this.tempPosition
        }).start()
    }

    /**
     * 收缩运动
     * @param radius 收缩半径
     * @param time 总时长
     */
    public shrink(radius: number, time: number) {
        if (this.shrinkTween) { this.shrinkTween.stop() }
        this.shrinkTween = new Tween({ radius: this.radius }).to({ radius: radius }, time).onUpdate((value) => {
            this.radius = value.radius
        }).start()
    }

    /**
     * 下落运动
     * @param height 下落高度
     * @param time 总时长
     */
    public down(height: number, time: number) {
        if (this.downTween) { this.downTween.stop() }
        this.downTween = new Tween({ height: this.obj.worldTransform.position.z }).to({ height: this.obj.worldTransform.position.z - height }, time).onUpdate((value) => {
            this.tempPosition.x = this.obj.worldTransform.position.x
            this.tempPosition.y = this.obj.worldTransform.position.y
            this.tempPosition.z = value.height
            this.obj.worldTransform.position = this.tempPosition
        }).start().onComplete(() => {
            EffectService.playAtPosition("32250", this.obj.worldTransform.position.add(new Vector(0, 0, -70)), { duration: 10 })
        })
    }

    /**
     * 直线运动
     * @param pos 目标位置
     * @param time 总时长
     */
    public moveTo(pos: Vector, time: number, useRot: boolean = true) {
        if (this.moveTween) { this.moveTween.stop() }
        if (useRot) {
            this.obj.worldTransform.rotation = pos.clone().subtract(this.obj.worldTransform.position).toRotation()
        }
        this.moveTween = new Tween(this.obj.worldTransform.position.clone()).to(pos.clone(), time).onUpdate((value) => {
            this.obj.worldTransform.position = value
        }).start()
    }

    /**左右摇摆，上下浮动 */
    public idleAnim() {
        if (this.idleTween) { this.idleTween.stop() }
        this.idlePos = this.obj.worldTransform.position.clone()
        this.idleRot = this.obj.worldTransform.rotation.clone()
        const ry = this.idleRot.y
        const pz = this.idlePos.z
        const randomRY = Math.random() > 0.5 ? 70 : -70
        this.idleTween = new Tween({ ry: ry, pz: pz }).to({ ry: [ry + randomRY, ry - randomRY], pz: [pz, pz + 100, pz - 50] }, 8000)
            .interpolation(TweenUtil.Interpolation.Bezier)
            .onUpdate((value) => {
                this.idlePos.z = value.pz
                this.idleRot.y = value.ry
                this.obj.worldTransform.position = this.idlePos
                this.obj.worldTransform.rotation = this.idleRot
            }).start()
    }

    /**添加特效 */
    public async addEffect(guid: string, duration: number, color: LinearColor) {
        let effect = await GameObject.asyncSpawn(guid) as Effect
        // 设置颜色
        // effect.setColor("Color", color)
        // 设置持续时间
        effect.duration = duration
        effect.parent = this.obj
        effect.localTransform.position = Vector.zero
        // 播放特效
        effect.play()
    }


    public fixedPosition: Vector
    public freshFixedPosition() {
        this.fixedPosition = this.obj.worldTransform.position.clone()
    }

    public destory() {
        if (this.idleTween) { this.idleTween.stop() }
        if (this.downTween) { this.downTween.stop() }
        if (this.shrinkTween) { this.shrinkTween.stop() }
        if (this.moveTween) { this.moveTween.stop() }
        if (this.circularTween) { this.circularTween.stop() }
        this.obj.destroy()
    }
}


function v2Color(char: Character, color: LinearColor) {
    // 衣服
    const clothing = char.description.advance.clothing
    for (let key in clothing) {
        let part = clothing[key][`part`]
        if (part.length > 0) {
            for (let item of part) {
                // color分支
                if (item[`color`]) {
                    item[`color`][`areaColor`] = color
                }
                // design分支
                if (item[`design`]) {
                    item[`design`][`designColor`] = color
                }
                // pattern分支
                if (item[`pattern`]) {
                    item[`pattern`][`patternColor`] = color
                }
            }
        }
    }
    // 头发
    char.description.advance.hair.backHair.color.color = color
    char.description.advance.hair.frontHair.color.color = color
    // 肤色
    char.description.advance.makeup.skinTone.skinColor = color
    // 闭眼
    char.description.advance.headFeatures.eyes.overall.eyeVerticalScale = 0
}


function generateTransitionColor(startColor: [number, number, number], endColor: [number, number, number], totalColors: number, targetColorIndex: number) {
    const [startRed, startGreen, startBlue] = startColor;
    const [endRed, endGreen, endBlue] = endColor;

    const redIncrement = (endRed - startRed) / (totalColors - 1);
    const greenIncrement = (endGreen - startGreen) / (totalColors - 1);
    const blueIncrement = (endBlue - startBlue) / (totalColors - 1);

    const red = startRed + (redIncrement * targetColorIndex);
    const green = startGreen + (greenIncrement * targetColorIndex);
    const blue = startBlue + (blueIncrement * targetColorIndex);

    return new LinearColor(red, green, blue);
}

class SoundTool {
    // 单例
    private static _instance: SoundTool
    public static get instance() {
        if (SoundTool._instance == null) {
            SoundTool._instance = new SoundTool()
        }
        return SoundTool._instance
    }

    private soundMap: Map<string, Sound[]> = new Map()

    public async playSound(guid: string, volume: number = 1) {
        let soundArray = this.soundMap.get(guid)
        let sound: Sound
        if (soundArray && soundArray.length > 0) {
            sound = soundArray.pop()
        } else {
            this.soundMap.set(guid, []);
            sound = await GameObject.asyncSpawn(guid) as Sound
            sound.onFinish.add(() => {
                this.soundMap.get(guid).push(sound)
            })
        }
        sound.volume = volume
        sound.play()
    }

}