const { ccclass, property } = cc._decorator;

@ccclass
class MapControl extends cc.Component {

    @property({
        type: cc.Node,
        tooltip: '目标节点，当mapContainer为null时，需要将该组件挂在目标节点，该节点默认为挂载节点'
    })
    public map: cc.Node = null;

    public camera  = null;

    @property({
        type: cc.Node,
        tooltip: '当该节点为空时，需要将改组件挂在map节点上，该节点默认为挂载节点的父节点'
    })
    public mapContainer: cc.Node = null;

    @property(cc.Label)
    public scaleTime: cc.Label = null;

    @property({
        tooltip: '图片缩放最大scale'
    })
    public maxScale: number = 1.3;

    @property({
        tooltip: '图片缩放最小scale'
    })
    public minScale: number = 0.5;

    @property({
        tooltip: '单点触摸容忍误差'
    })
    public moveOffset: number = 0;

    @property({
        tooltip: '滚轮缩放比率'
    })
    public increaseRate: number = 1000;

    public operLock: boolean = false; // 操作锁
    public isMoving: boolean = false; // 是否拖动地图flag
    public mapTouchList: any[] = []; // 触摸点容器
    public singleTouchCb: Function = null; // 单点回调函数

    public zoomRatio : number = 1

    protected start(): void {
        if (!this.map)
        this.map  = this.node
        //拥有同一个父级 zIndex才会生效
        this.map.getChildByName('block2').zIndex = 100
        this.camera = this.node.parent.parent.getChildByName('Main Camera')
        if (!this.mapContainer) this.mapContainer = this.node.parent;
        this.addEvent();
    }

    private addEvent(): void {
        this.map.on(cc.Node.EventType.TOUCH_MOVE, (event: any) => {
            console.log('TOUCH_MOVE')
            if (this.operLock) return; // 如果触摸操作暂时锁定则不响应
            let touches: any[] = event['getTouches'](); // 获取所有触摸点
            if (touches.length >= 2) {
                // 如果容器内触摸点数量超过1则为多点触摸，此处暂时不处理三点及以上的触摸点，可以根据需求来处理
                this.isMoving = true;
                this.dealTouchData(touches, this.camera);
            } else if (touches.length === 1) {
                // sigle touch
                let touch: any = touches[0];
                let startPos: cc.Vec2 = cc.v2(touch.getStartLocation());
                let nowPos: cc.Vec2 = cc.v2(touch.getLocation());
                // 有些设备单点过于灵敏，单点操作会触发TOUCH_MOVE回调，在这里作误差值判断
                if ((Math.abs(nowPos.x - startPos.x) <= this.moveOffset
                    || Math.abs(nowPos.y - startPos.y) <= this.moveOffset)
                    && !this.isMoving) {
                    return;
                }
                let dir: cc.Vec2 = cc.v2(touch.getDelta());
                this.isMoving = true;
                this.dealMove(dir, this.map, this.camera);

            }
        }, this);

        this.map.on(cc.Node.EventType.TOUCH_END, (event) => {
            if (this.operLock) return cc.log('operate is lock');
            // 需要自行管理touches队列, cocos 的多点触控并不可靠
            if (this.mapTouchList.length < 2) {
                if (!this.isMoving) {
                    let worldPos: cc.Vec2 = cc.v2(event['getLocation']());
                    let nodePos: cc.Vec2 = this.map.convertToNodeSpaceAR(worldPos);
                    this.dealSelect(nodePos);
                }
                this.isMoving = false; // 当容器中仅剩最后一个触摸点时讲移动flag还原
            };
            this.removeTouchFromContent(event, this.mapTouchList);
        }, this);

        this.map.on(cc.Node.EventType.TOUCH_CANCEL, (event) => {
            if (this.operLock) return;
            if (this.mapTouchList.length < 2) { // 当容器中仅剩最后一个触摸点时讲移动flag还原
                this.isMoving = false;
            };
            this.removeTouchFromContent(event, this.mapTouchList);
        }, this);

        this.map.on(cc.Node.EventType.MOUSE_WHEEL, (event) => {
            if (this.operLock) return;
            let location: any = event['getLocation']();
            let worldPos: cc.Vec2 = cc.v2(location.x, location.y);
            let scrollDelta: number = event['getScrollY']();
            let scale: number = (this.camera.scale + (scrollDelta / this.increaseRate));
            let target: cc.Node = this.camera;
            let pos: cc.Vec2 = target.convertToNodeSpaceAR(worldPos);
            this.smoothOperate(target, pos, scale);
        }, this);
    }

    public removeTouchFromContent(event: any, content: any[]): void {
        let eventToucheIDs: number[] = event['getTouches']().map(v => v.getID());
        for (let len = content.length, i = len - 1; i >= 0; --i) {
            if (eventToucheIDs.indexOf(content[i].id) > -1)
                content.splice(i, 1); // 删除触摸
        }
    }

    private smoothOperate(target: cc.Node, pos: cc.Vec2, scale: number): void {
        let scX: number = scale;
        // 当前缩放值与原来缩放值之差
        let disScale: number = scX - target.scaleX;
        // 当前点击的坐标与缩放值差像乘
        let gapPos: cc.Vec2 = pos.scale(cc.v2(disScale, disScale));
        // 当前node坐标位置减去点击 点击坐标和缩放值的值
        let mapPos: cc.Vec2 = target.getPosition().sub(cc.v2(gapPos.x, gapPos.y));
        // 放大缩小
        if (!this.isOutRangeScale(scale)) {
            scale = (scale * 10 | 0) / 10;
            console.log(scale)
            target.scale =  scale;
            target.getComponent(cc.Camera).zoomRatio = scale;
            this.dealScalePos(mapPos,target);
        }
    }

    private dealTouchData(touches: any[], target: cc.Node): void {
        let touch1: any = touches[0].touch;
        let touch2: any = touches[1].touch;
        let delta1: cc.Vec2 = cc.v2(touch1.getDelta());
        let delta2: cc.Vec2 = cc.v2(touch2.getDelta());
        let touchPoint1: cc.Vec2 = target.convertToNodeSpaceAR(cc.v2(touch1.getLocation()));
        let touchPoint2: cc.Vec2 = target.convertToNodeSpaceAR(cc.v2(touch2.getLocation()));
        let distance: cc.Vec2 = touchPoint1.sub(touchPoint2);
        let delta: cc.Vec2 = delta1.sub(delta2);
        let scale: number = 1;
        if (Math.abs(distance.x) > Math.abs(distance.y)) {
            scale = (distance.x + delta.x) / distance.x * target.scaleX;
        } else {
            scale = (distance.y + delta.y) / distance.y * target.scaleY;
        }
        let pos: cc.Vec2 = touchPoint2.add(cc.v2(distance.x / 2, distance.y / 2));
        this.smoothOperate(target, pos, scale);
    }

    private isOutRangeScale(scale: number): boolean {
        return (scale > this.maxScale || scale < this.minScale);
    }

    private dealScaleRange(scale: number): number {
        if (scale > this.maxScale) {
            return this.maxScale;
        } else if (scale < this.minScale) {
            return this.minScale;
        } else {
            return scale;
        }
    }

    private dealScalePos(pos: cc.Vec2, target: cc.Node): void {
        let worldPos: cc.Vec2 = this.map.convertToWorldSpaceAR(pos);
        let nodePos: cc.Vec2 = target.convertToNodeSpaceAR(worldPos);
        let edge: any = this.calculateEdge(this.map, nodePos);
        edge.lBorderDelta > 0 && (pos.x -= edge.lBorderDelta);
        edge.rBorderDelta > 0 && (pos.x += edge.rBorderDelta);
        edge.uBorderDelta > 0 && (pos.y += edge.uBorderDelta);
        edge.dBorderDelta > 0 && (pos.y -= edge.dBorderDelta);
        // if (0.8 === target.scale) pos = cc.Vec2.ZERO;
        this.camera.position = pos;
        // target.x = pos.x
        // target.y = pos.y
    }

    private dealMove(dir: cc.Vec2, map: cc.Node, carema: cc.Node): void {
        let worldPos: cc.Vec2 = map.convertToWorldSpaceAR(dir);
        let nodePos: cc.Vec2 = carema.convertToNodeSpaceAR(worldPos);

        let edge: any = this.calculateEdge(map, nodePos);

        console.log(carema.x)
        console.log(-dir.x)
        // nodePos.x += dir.x;
        // nodePos.y += dir.y;

        if (edge.lBorderDelta <= 0 && edge.rBorderDelta <= 0) {
            carema.x += (-dir.x);
        }
            
        if (edge.uBorderDelta <= 0 && edge.dBorderDelta <= 0) {
            carema.y += (-dir.y);
        }
    }

    public setSinglTouchCb(cb: Function): void {
        this.singleTouchCb = cb;
    }

    private dealSelect(nodePos: cc.Vec2): void {
        cc.log(`click map on cc.v2(${nodePos.x}, ${nodePos.y})`);
        // do sth
        if (this.singleTouchCb) this.singleTouchCb(nodePos);
    }

    // 计算map的四条边距离容器的距离
    public calculateEdge(target: cc.Node, nodePos: cc.Vec2): any {
        let realWidth: number = target.width * this.camera.scaleX;
        let realHeight: number = target.height * this.camera.scaleY;
        let lBorderDelta: number = (nodePos.x - realWidth / 2) + cc.winSize.width / 2;
        let rBorderDelta: number = cc.winSize.width / 2 - (realWidth / 2 + nodePos.x); // <= 0 safe
        let uBorderDelta: number = cc.winSize.height / 2 - (realHeight / 2 + nodePos.y);
        let dBorderDelta: number = (nodePos.y - realHeight / 2) + cc.winSize.height / 2;
        return { lBorderDelta, rBorderDelta, uBorderDelta, dBorderDelta };
    }
}

export = MapControl; 