﻿
/**影子颜色模式 */
export enum SandevistanShadowColorMode {
    /**随机颜色 */
    Random,
    /**过渡颜色 */
    Transition,
    /**固定颜色 */
    Fixed
}

/**影子位置模式 */
export enum SandevistanShadowPositionMode {
    /**跟随 */
    Follow,
    /**圆周 */
    Circular
}

/**
 * 义体装置 -- 斯安威斯坦
 */
export class Sandevistan {
    /**主角 */
    public actor: Character

    /**影子列表 */
    private shadowArray: Shadow[] = []

    /**跟随计时器 */
    private followInter

    private currentWeaponGuid: string = ""

    public fixedColor: string = "#009FAB"

    public startColor: string = "#1F8EFF"
    public endColor: string = "#EEF7FF"

    public shadowColorMode: SandevistanShadowColorMode = SandevistanShadowColorMode.Fixed
    public shadowPositionMode: SandevistanShadowPositionMode = SandevistanShadowPositionMode.Follow

    constructor(actor: Character) {
        this.actor = actor
    }

    /**
     * 换装（换装估计是有bug，必须在停止跟随，且影子全部消失的情况下再换装）
     */
    public reloadDescription(guid: string) {
        this.actor.setDescription([guid])
        this.shadowArray.forEach(async (value) => {
            await value.npc.asyncReady()
            value.npc.setDescription([guid])
        })
    }

    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

        let shadowIndex = 0
        let circularIndex = 0

        let addValue = 1


        // 开始循环
        this.followInter = setInterval(() => {

            // 获取一个影子
            let shadow = this.getShadow()
            if (shadowIndex >= this.totalShadowCount) {
                addValue = -1
            } else if (shadowIndex <= 0) {
                addValue = 1
            }
            shadowIndex += addValue


            circularIndex++
            if (circularIndex >= this.totalShadowCount) {
                circularIndex = 0
            }

            // 默认动画ID
            let guid = "159213"
            // 默认动画时长
            let length = 2
            // 获取主角当前的动画
            const anim = this.actor.currentAnimation
            // 如果动画存在，且没有正在播放
            if (anim != null && anim.isPlaying) {
                // 获取动画ID
                guid = anim.assetId
                // 获取动画长度
                length = anim.length
            }

            let position = this.actor.worldTransform.position
            if (this.shadowPositionMode == SandevistanShadowPositionMode.Circular) {
                position = getCircularPosition(position.clone(), 300, 360 * (circularIndex / this.totalShadowCount))
            }

            // 在角色的位置展示影子
            shadow.show(duration, guid, progress / (length * 1000), position, this.actor.worldTransform.rotation)

            shadow.freshWeapon(this.currentWeaponGuid)

            switch (this.shadowColorMode) {
                case SandevistanShadowColorMode.Fixed:
                    shadow.setColor(LinearColor.colorHexToLinearColor(this.fixedColor))
                    break;
                case SandevistanShadowColorMode.Random:
                    shadow.setColor(LinearColor.colorHexToLinearColor(getRandomHexColor()))
                    break;
                case SandevistanShadowColorMode.Transition:
                    shadow.setColor(generateTransitionColor(this.startColor, this.endColor, this.totalShadowCount, shadowIndex))
                    break;
                default:
                    break;
            }


            // 更新进度
            progress += gap
            // 如果进度满了
            if (progress > length * 1000) {
                // 重置进度
                progress = 0
            }
        }, gap)
    }

    /**停止跟随 */
    public stopFollow() {
        if (this.followInter) {
            clearInterval(this.followInter)
        }
    }

    private totalShadowCount: number = 0
    /**获取一个影子 */
    private getShadow() {
        let shadow = this.shadowArray.pop()
        if (shadow == null) {
            shadow = new Shadow(this.actor)
            this.totalShadowCount++
            shadow.onHide.add(() => {
                this.shadowArray.push(shadow)
            })
        }
        return shadow
    }
}


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()
        await this.npc.asyncReady()
        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)
            }
        })
    }

}

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
}


/**
 * 获得一个过渡色
 * @param startColor 开始颜色
 * @param endColor 结束颜色
 * @param totalColors 颜色总数
 * @param targetColorIndex 第几个（取值范围：[0,颜色总数]）
 * @returns 
 */
function generateTransitionColor(startColorHex: string, endColorHex: string, totalColors: number, targetColorIndex: number) {

    const startColor = LinearColor.colorHexToLinearColor(startColorHex)
    const endColor = LinearColor.colorHexToLinearColor(endColorHex)

    const [startRed, startGreen, startBlue] = [startColor.r, startColor.g, startColor.b];
    const [endRed, endGreen, endBlue] = [endColor.r, endColor.g, endColor.b];

    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);
}

/**获得一个随机颜色值 */
function getRandomHexColor(): string {
    const randomValue = Math.floor(Math.random() * 16777215); // 0xFFFFFF
    return `#${randomValue.toString(16).padStart(6, '0')}`;
}

/**圆圈位置 */
function getCircularPosition(centerPos: Vector, radius: number, angle: number) {
    const x = centerPos.x + Math.cos(angle * (Math.PI / 180)) * radius
    const y = centerPos.y + Math.sin(angle * (Math.PI / 180)) * radius
    const z = centerPos.z
    return new Vector(x, y, z)
}