/** 轴 */
export enum Axis {
    /** 正 X 轴 */
    positiveX,
    /** 正 Y 轴 */
    positiveY,
    /** 负 X 轴 */
    negativeX,
    /** 负 Y 轴 */
    negativeY
}

const { ccclass, property, executeInEditMode, playOnFocus } = cc._decorator;

/**
 * 围绕旋转组件
 * @author 薛鸿潇
 */
@ccclass
@executeInEditMode()
@playOnFocus()
export default class RotateAround extends cc.Component {

    @property({ type: cc.Node, tooltip: '围绕旋转的目标' })
    public target: cc.Node = null;

    @property({ tooltip: '顺时针旋转' })
    public clockwise: boolean = true;

    @property({ tooltip: '旋转一圈花费的时间' })
    public timePerRound: number = 10;

    @property({ tooltip: '是否始终面向目标节点' })
    public faceToTarget: boolean = false;

    @property({
        type: cc.Enum(Axis),
        tooltip: '面向目标节点的轴：\n- PositiveX：正 X 轴\n- PositiveY：正 Y 轴\n- NegativeX：负 X 轴\n- NegativeY：负 Y 轴',
        visible() { return this.faceToTarget }
    })
    public faceAxis: Axis = Axis.negativeY;

    @property({ tooltip: '自动开始旋转' })
    public auto_start: boolean = false;

    /** 编辑器预览 */
    private _edit_play: boolean = false;
    @property({ displayName: '编辑器预览' })
    get edit_play(): boolean {
        if (this._edit_play && !this.radius) this.run();
        this.isRotating = this._edit_play;
        return this._edit_play;
    }
    set edit_play(v: boolean) {
        this._edit_play = v;
    }

    /** 角度 */
    public angle: number = 0;
    /** 半径 */
    public radius: number = 0;
    /** 标志位，是否正在旋转 */
    private isRotating: boolean = false;

    protected start() {
        if (this.auto_start) this.run();
    }

    protected update(dt: number) {
        if (!this.isRotating || !this.target) return;
        // 将角度转换为弧度
        let radian = (Math.PI / 180) * this.angle;
        // 更新节点的位置
        this.node.x = this.target.x + this.radius * Math.cos(radian);
        this.node.y = this.target.y + this.radius * Math.sin(radian);
        // 更新节点的角度
        if (this.faceToTarget) {
            switch (this.faceAxis) {
                case Axis.positiveX:
                    this.node.angle = this.angle + 180;
                    break;
                case Axis.positiveY:
                    this.node.angle = this.angle + 90;
                    break;
                case Axis.negativeX:
                    this.node.angle = this.angle;
                    break;
                case Axis.negativeY:
                    this.node.angle = this.angle - 90;
                    break;
            }
        }
        // 计算下一帧的角度
        let anglePerFrame = dt * (360 / this.timePerRound);
        if (this.clockwise) this.angle -= anglePerFrame;
        else this.angle += anglePerFrame;
        // 重置角度，避免数值过大
        if (this.angle >= 360) this.angle %= 360;
        else if (this.angle <= -360) this.angle %= -360;
    }

    /**
     * 开始围绕目标节点旋转
     * @param target 目标节点
     * @param clockwise 是否顺时针旋转
     * @param timePerRound 旋转一圈的时间
     * @param faceToTarget 是否始终面向目标节点
     * @param faceAxis 面向目标节点的轴
     */
    public run(target?: cc.Node, clockwise?: boolean, timePerRound?: number, faceToTarget?: boolean, faceAxis?: Axis) {
        if (target) this.target = target;
        if (clockwise) this.clockwise = clockwise;
        if (timePerRound) this.timePerRound = timePerRound;
        if (faceToTarget) this.faceToTarget = faceToTarget;
        if (faceAxis) this.faceAxis = faceAxis;
        if (!this.target) return cc.log('No target!');
        if (this.isRotating) return;
        // 计算初始角度和半径
        this.angle = this.getAngle(this.target.getPosition(), this.node.getPosition());
        this.radius = this.getDistance(this.target.getPosition(), this.node.getPosition());
        // 开始
        this.isRotating = true;
    }

    /**
     * 停止旋转
     */
    public stop() {
        this.isRotating = false;
    }

    /**
     * 获取两点间的角度
     * @param p1 点1
     * @param p2 点2
     */
    public getAngle(p1: cc.Vec2, p2: cc.Vec2): number {
        return Math.atan((p2.y - p1.y) / (p2.x - p1.x));
    }

    /**
     * 获取两点间的距离
     * @param p1 点1
     * @param p2 点2
     */
    public getDistance(p1: cc.Vec2, p2: cc.Vec2): number {
        return Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2));
    }

}
