const { ccclass, property } = cc._decorator;


/**
 * 方向类型
 */
export enum DirectionType {
    FOUR,
    EIGHT,
    ALL,
}

/**
 * 速度类型
 */
export enum SpeedType {
    STOP,
    NORMAL,
    FAST,
}

/**
 * 摇杆类型
 */
export enum JoystickType {
    FIXED,
    FOLLOW,
}

/**
 * 摇杆类
 */
@ccclass
export default class Joystick_mag extends cc.Component {
    @property({
        type: cc.Node,
        displayName: "Dot",
        tooltip: "摇杆操纵点",
    })
    dot = null;

    @property({
        type: cc.Node,
        displayName: "Ring",
        tooltip: "摇杆背景节点",
    })
    ring = null;

    @property({
        type: cc.Enum(JoystickType),
        displayName: "Touch Type",
        tooltip: "触摸类型",
    })
    joystickType = JoystickType.FIXED;

    @property({
        type: cc.Enum(DirectionType),
        displayName: "Direction Type",
        tooltip: "方向类型",
    })
    directionType = DirectionType.ALL;

    @property({
        type: cc.Node,
        tooltip: "摇杆所在位置",
    })
    _stickPos = null;

    @property({
        type: cc.Node,
        tooltip: "触摸位置",
    })
    _touchLocation = null;

    @property({
        tooltip: "半径",
    })
    _radius = 0;

    /**
     * 事件回调
     */
   private callback:(speedType:SpeedType,moveVec?:cc.Vec2)=>void = undefined;

    onLoad() {
        this._radius = this.ring.width / 2;
        this._initTouchEvent();
        // hide joystick when follow
        if (this.joystickType === JoystickType.FOLLOW) {
            this.node.opacity = 0;
        }

        cc.systemEvent.on(cc.SystemEvent.EventType.KEY_DOWN, this.onKeyPressed, this);
        cc.systemEvent.on(cc.SystemEvent.EventType.KEY_UP, this.onKeyReleased, this);
    }


    /**
     * 改变摇杆类型
     * @param type
     */
    _onSetJoystickType(type: JoystickType) {
        this.joystickType = type;
        this.node.opacity = type === JoystickType.FIXED ? 255 : 0;
    }

    /**
     * 初始化触摸事件
     */
    _initTouchEvent() {
        // set the size of joystick node to control scale
        this.node.on(cc.Node.EventType.TOUCH_START, this._touchStartEvent, this);
        this.node.on(cc.Node.EventType.TOUCH_MOVE, this._touchMoveEvent, this);
        this.node.on(cc.Node.EventType.TOUCH_END, this._touchEndEvent, this);
        this.node.on(cc.Node.EventType.TOUCH_CANCEL, this._touchEndEvent, this);
    }

    /**
     * 触摸开始回调函数
     * @param event
     */
    _touchStartEvent(event: cc.Event.EventTouch) {

        const touchPos = this.node.convertToNodeSpaceAR(event.getLocation());

        if (this.joystickType === JoystickType.FIXED) {
            this._stickPos = this.ring.getPosition();

            // 触摸点与圆圈中心的距离
            const distance = touchPos.sub(this.ring.getPosition()).mag();

            // 手指在圆圈内触摸,控杆跟随触摸点
            this._radius > distance && this.dot.setPosition(touchPos);
        } else if (this.joystickType === JoystickType.FOLLOW) {
            // 记录摇杆位置,给 touch move 使用
            this._stickPos = touchPos;
            this.node.opacity = 255;
            this._touchLocation = event.getLocation();

            // 更改摇杆的位置
            this.ring.setPosition(touchPos);
            this.dot.setPosition(touchPos);
        }
    }

    /**
     * 触摸移动回调函数
     * @param event
     */
    _touchMoveEvent(event: cc.Event.EventTouch) {
        // 如果 touch start 位置和 touch move 相同,禁止移动
        if (
            this.joystickType === JoystickType.FOLLOW &&
            this._touchLocation === event.getLocation()
        ) {
            return false;
        }

        // 以圆圈为锚点获取触摸坐标
        const touchPos = this.ring.convertToNodeSpaceAR(event.getLocation());
        const distance = touchPos.mag();

        // 由于摇杆的 postion 是以父节点为锚点,所以定位要加上 touch start 时的位置
        const posX = this._stickPos.x + touchPos.x;
        const posY = this._stickPos.y + touchPos.y;

        // 归一化
        const p = cc.v2(posX, posY).sub(this.ring.getPosition()).normalize();

        let speedType;

        if (this._radius > distance) {
            this.dot.setPosition(cc.v2(posX, posY));

            speedType = SpeedType.NORMAL;
        } else {
            // 控杆永远保持在圈内,并在圈内跟随触摸更新角度
            const x = this._stickPos.x + p.x * this._radius;
            const y = this._stickPos.y + p.y * this._radius;
            this.dot.setPosition(cc.v2(x, y));

            speedType = SpeedType.FAST;
        }
        if(this.callback != undefined){
            if(p.x == 0 && p.y == 0){
                this.callback(SpeedType.STOP)
            }else{
                this.callback(speedType,p)
            }
            
        }
    }

    /**
     * 触摸结束回调函数
     * @param event
     */
    _touchEndEvent(event: cc.Event.EventTouch) {
        this.dot.setPosition(this.ring.getPosition());
        if (this.joystickType === JoystickType.FOLLOW) {
            this.node.opacity = 0;
        }
        if(this.callback != undefined){
            this.callback(SpeedType.STOP)
        }
    }

    public stop(){
        this._touchEndEvent(null)
    }
    
    public setListen(callback:(speedType:SpeedType,moveVec?:cc.Vec2)=>void){
        this.callback = callback;
    }

    private keyDir = cc.v2(0,0)

    private onKeyPressed(event) {
        let keyCode = event.keyCode;
        switch(keyCode) {
            case cc.macro.KEY.w:
            case cc.macro.KEY.up:
                this.keyDir.y = 1;
                break;
            case cc.macro.KEY.s:
            case cc.macro.KEY.down:
                this.keyDir.y = -1;
                break;
            case cc.macro.KEY.a:
            case cc.macro.KEY.left:
                this.keyDir.x = -1;
                break;
            case cc.macro.KEY.d:
            case cc.macro.KEY.right:
                this.keyDir.x = 1;
                break;
        }
        if(this.callback){
            let dmg = this.keyDir.normalize()
            if(dmg.x == 0 && dmg.y == 0){
                this.callback(SpeedType.STOP)
            }else{
                this.callback(SpeedType.FAST,dmg)
            }
            
        }
    }

    private onKeyReleased(event) {
        let keyCode = event.keyCode;
        switch(keyCode) {
            case cc.macro.KEY.w:
            case cc.macro.KEY.up:
                this.keyDir.y = 0;
                break;
            case cc.macro.KEY.s:
            case cc.macro.KEY.down:
                this.keyDir.y = 0;
                break;
            case cc.macro.KEY.a:
            case cc.macro.KEY.left:
                this.keyDir.x = 0;
                break;
            case cc.macro.KEY.d:
            case cc.macro.KEY.right:
                this.keyDir.x = 0;
                break;
        }
       
        if(this.callback){
            let dmg = this.keyDir.normalize()
            if(dmg.x == 0 && dmg.y == 0){
                this.callback(SpeedType.STOP)
            }else{
                this.callback(SpeedType.FAST,dmg)
            }
            
        }
    }

}