const { ccclass, property } = cc._decorator;

const d = 40;

@ccclass
export default class Test extends cc.Component {

    private dirX: -1 | 0 | 1;
    private dirY: -1 | 0 | 1;
    private container: cc.Node;
    private map: Array<Array<cc.Node>>;
    private hero: Hero;
    private v0: number = 4;

    protected start(): void {
        const _container = this.node.getChildByName("container");
        this.container = this.node.getChildByName("container").getChildByName("container");
        this.map = [];
        for (let y: number = 0; y < 10; y++) {
            this.map[y] = [];
            for (let x: number = 0; x < 10; x++) {
                this.map[y][x] = null;
            }
        }
        for (const node of this.container.children) {
            const x = Math.round(node.x / d);
            const y = Math.round(-node.y / d);
            this.map[y][x] = node;
            node.x = x * d;
            node.y = y * -d;
        }
        this.hero = new Hero(_container.getChildByName("hero"));
        _container.removeChild(this.hero.node, false);
        this.container.addChild(this.hero.node);
        this.adjustDepths();

        this.dirX = 0;
        this.dirY = 0;

        cc.systemEvent.on(cc.SystemEvent.EventType.KEY_DOWN, (evt: { keyCode: number }) => {
            switch (evt.keyCode) {
                case cc.macro.KEY.a:
                case cc.macro.KEY.left:
                    this.dirX = -1;
                    break;
                case cc.macro.KEY.w:
                case cc.macro.KEY.up:
                    this.dirY = 1;
                    break;
                case cc.macro.KEY.d:
                case cc.macro.KEY.right:
                    this.dirX = 1;
                    break;
                case cc.macro.KEY.s:
                case cc.macro.KEY.down:
                    this.dirY = -1;
                    break;
            }
        });
        cc.systemEvent.on(cc.SystemEvent.EventType.KEY_UP, (evt: { keyCode: number }) => {
            switch (evt.keyCode) {
                case cc.macro.KEY.a:
                case cc.macro.KEY.left:
                    if (this.dirX == -1) this.dirX = 0;
                    break;
                case cc.macro.KEY.w:
                case cc.macro.KEY.up:
                    if (this.dirY == 1) this.dirY = 0;
                    break;
                case cc.macro.KEY.d:
                case cc.macro.KEY.right:
                    if (this.dirX == 1) this.dirX = 0;
                    break;
                case cc.macro.KEY.s:
                case cc.macro.KEY.down:
                    if (this.dirY == -1) this.dirY = 0;
                    break;
            }
        });
    }

    private adjustDepths(): void {
        const keys: Array<string> = new Array();
        const nodes: { [key: string]: cc.Node } = {};
        let i: number = -1;
        for (const node of this.container.children) {
            i++;
            const key = (10000 - Math.round(node.y)).toString() + (10000 + Math.round(node.x)).toString() + (1000 + i).toString();
            keys.push(key);
            nodes[key] = node;
        }
        keys.sort();
        i = -1;
        for (const key of keys) {
            i++;
            const node = nodes[key];
            delete nodes[key];
            if (node.getSiblingIndex() == i) continue;
            node.setSiblingIndex(i);
        }
        keys.length = 0;
    }

    protected update(): void {
        if (this.dirX < 0) {
            this.hero.change(this.hero.left);
            this.hero.next();
            this.hero.node.x += this.dirX * this.v0;
            if (this.map[Math.round(-this.hero.node.y / d)][Math.round((this.hero.node.x - d / 2) / d)]) {
                this.hero.node.x = Math.round(this.hero.node.x / d) * d;
            }
        } else if (this.dirX > 0) {
            this.hero.change(this.hero.right);
            this.hero.next();
            this.hero.node.x += this.dirX * this.v0;
            if (this.map[Math.round(-this.hero.node.y / d)][Math.round((this.hero.node.x + d / 2) / d)]) {
                this.hero.node.x = Math.round(this.hero.node.x / d) * d;
            }
        } else if (this.dirY < 0) {
            this.hero.change(this.hero.down);
            this.hero.next();
            this.hero.node.y += this.dirY * this.v0;
            if (this.map[Math.round((-this.hero.node.y + d / 2) / d)][Math.round(this.hero.node.x / d)]) {
                this.hero.node.y = -Math.round(-this.hero.node.y / d) * d;
            }
        } else if (this.dirY > 0) {
            this.hero.change(this.hero.up);
            this.hero.next();
            this.hero.node.y += this.dirY * this.v0;
            if (this.map[Math.round((-this.hero.node.y - d / 2) / d)][Math.round(this.hero.node.x / d)]) {
                this.hero.node.y = -Math.round(-this.hero.node.y / d) * d;
            }
        }

        this.adjustDepths();
    }

}

class Hero {

    public node: cc.Node;

    public left: Array<cc.Node>;
    public up: Array<cc.Node>;
    public right: Array<cc.Node>;
    public down: Array<cc.Node>;

    private seq: Array<cc.Node>;
    private curr: cc.Node;
    private index: number;
    private delayTime: number;

    public constructor(node: cc.Node) {
        this.node = node;
        this.left = this.node.getChildByName("left").children;
        this.up = this.node.getChildByName("up").children;
        this.right = this.node.getChildByName("right").children;
        this.down = this.node.getChildByName("down").children;
        for (const child of this.node.children) {
            for (const subChild of child.children) {
                subChild.active = false;
            }
        }
        this.curr = this.down[0];
        this.change(this.down);
    }

    public change(seq: Array<cc.Node>): void {
        if (this.seq == seq) return;
        this.seq = seq;
        this.index = -1;
        this.delayTime = 0;
        this.next();
    }

    public next(): void {
        if (--this.delayTime < 0) {
            this.delayTime = 5;
            this.curr.active = false;
            if (++this.index >= this.seq.length) {
                this.index = 0;
            }
            this.curr = this.seq[this.index];
            this.curr.active = true;
        }
    }

}
