const { ccclass, property } = cc._decorator;

const h = 300;

@ccclass
export default class Test extends cc.Component {

    private sw2: number = 640 / 2;
    private sh2: number = 1200 / 2;
    private r: number = 76;
    private left: number = -this.sw2 + this.r;
    private right: number = this.sw2 - this.r;
    private back: number = -this.sh2 + this.r;
    private front: number = this.sh2 - this.r;
    private v0: number = 10;
    private dirX: -1 | 0 | 1;
    private dirY: -1 | 0 | 1;
    private sqrt2 = Math.sqrt(2);

    private bottom: cc.Node;
    private bottomMat: cc.Material;
    private cameraPosAndH: Array<number>;

    private container: cc.Node;
    private hero: Role;
    private monsters: Array<Role>;
    private camera: { x: number, y: number, z: number };

    protected start(): void {

        const _container = this.node.getChildByName("container");

        this.bottom = _container.getChildByName("bottom");
        const texWHScreenWH: Array<number> = [this.bottom.width, this.bottom.height];
        texWHScreenWH[2] = this.bottom.width = cc.winSize.width;
        texWHScreenWH[3] = this.bottom.height = cc.winSize.height;
        this.bottomMat = this.bottom.getComponent(cc.Sprite).getMaterial(0);
        this.bottomMat.setProperty("texWHScreenWH", texWHScreenWH);
        console.log("texWHScreenWH=" + texWHScreenWH);
        this.cameraPosAndH = [0, 0, 0, h];

        this.container = _container.getChildByName("container");
        this.monsters = [];
        for (const child of this.container.children) {
            switch (child.name) {
                case "hero":
                    this.monsters.push(this.hero = new Role(child));
                    break;
                case "monster":
                    this.monsters.push(new Role(child));
                    break;
            }
        }
        this.camera = { x: 0, y: 0, z: 0 };
        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;
            }
        });

        this.render();
    }

    protected update(): void {
        if (this.dirX == 0 || this.dirY == 0) {
            this.hero.x += this.dirX * this.v0;
            this.hero.y += this.dirY * this.v0;
        } else {
            this.hero.x += this.dirX * this.v0 / this.sqrt2;
            this.hero.y += this.dirY * this.v0 / this.sqrt2;
        }
        if (this.dirX < 0) {
            if (this.hero.x <= this.left) {
                this.hero.x = this.left;
            }
        } else {
            if (this.hero.x >= this.right) {
                this.hero.x = this.right;
            }
        }
        if (this.dirY < 0) {
            if (this.hero.y <= this.back) {
                this.hero.y = this.back;
            }
        } else {
            if (this.hero.y >= this.front) {
                this.hero.y = this.front;
            }
        }

        this.render();
    }

    private render(): void {
        this.cameraPosAndH[0] = this.camera.x = this.hero.x;
        this.cameraPosAndH[1] = this.camera.y = this.hero.y - 300;
        this.cameraPosAndH[2] = this.camera.z = this.hero.z + 200;

        this.bottomMat.setProperty("cameraPosAndH", this.cameraPosAndH);
        //console.log("cameraPosAndH=" + this.cameraPosAndH);

        const keys: Array<string> = new Array();
        const nodes: { [key: string]: cc.Node } = {};
        let i: number = -1;
        for (const monster of this.monsters) {
            const dy = monster.y - this.camera.y;
            if (dy > 10) {
                i++;
                const k = h / dy;
                monster.node.x = (monster.x - this.camera.x) * k;
                monster.node.y = (monster.z - this.camera.z) * k;
                monster.node.scale = k;
                monster.node.active = true;
                const key = (10000 - Math.round(monster.node.y)).toString() + (1000 + i).toString();
                keys.push(key);
                nodes[key] = monster.node;
            } else {
                monster.node.active = false;
            }
        }
        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;
    }

}

class Role {

    public node: cc.Node;

    public x: number;//朝右
    public y: number;//朝前
    public z: number;//朝上

    public constructor(node: cc.Node) {
        this.node = node;
        this.x = node.x;
        this.y = node.y;
        this.z = 0;
    }

}
