<template>
    <div style="position: relative;">
        <canvas :id="id" width="320" height="620" tabindex="0"/>
        <div style="position: absolute;top: 0;left: 350px;">
            <p>{{score}}</p>
            <el-button @click="reset">reset</el-button>
        </div>
    </div>
</template>

<script lang="ts">
    import { Component, Prop, Vue } from "vue-property-decorator";
    import { v1 as uuid } from "uuid";
    import {SCanvasView, SColor, SPainter, STextAlign} from "@persagy-web/draw/lib";

    /**
     * 俄罗斯方块视图
     *
     * @author 郝建龙
     * */
    class TestView extends SCanvasView {
        /** 背景表示数组 */
        map: number[][] = [];
        /** 方块类型索引 */
        boxType: number = Number(Math.floor(Math.random() * 7));  // 0-6
        /** 方块变形索引 */
        dir: number = Number(Math.floor(Math.random() * 4));  // 0-3
        /** 所有方块集合 */
        box: number[][][] = [
            [
                [0, 0, 0, 0],
                [0, 1, 1, 0],
                [0, 1, 1, 0],
                [0, 0, 0, 0],
            ],
            [
                [0, 0, 0, 0],
                [0, 1, 1, 0],
                [0, 1, 1, 0],
                [0, 0, 0, 0],
            ],
            [
                [0, 0, 0, 0],
                [0, 1, 1, 0],
                [0, 1, 1, 0],
                [0, 0, 0, 0],
            ],
            [
                [0, 0, 0, 0],
                [0, 1, 1, 0],
                [0, 1, 1, 0],
                [0, 0, 0, 0],
            ],
            [
                [0, 1, 0, 0],
                [0, 1, 0, 0],
                [0, 1, 0, 0],
                [0, 1, 0, 0],
            ],
            [
                [0, 0, 0, 0],
                [1, 1, 1, 1],
                [0, 0, 0, 0],
                [0, 0, 0, 0],
            ],
            [
                [0, 0, 1, 0],
                [0, 0, 1, 0],
                [0, 0, 1, 0],
                [0, 0, 1, 0],
            ],
            [
                [0, 0, 0, 0],
                [0, 0, 0, 0],
                [1, 1, 1, 1],
                [0, 0, 0, 0],
            ],
            [
                [0, 0, 0, 0],
                [0, 1, 1, 0],
                [1, 1, 0, 0],
                [0, 0, 0, 0],
            ],
            [
                [0, 1, 0, 0],
                [0, 1, 1, 0],
                [0, 0, 1, 0],
                [0, 0, 0, 0],
            ],
            [
                [0, 0, 0, 0],
                [0, 0, 1, 1],
                [0, 1, 1, 0],
                [0, 0, 0, 0],
            ],
            [
                [0, 0, 0, 0],
                [0, 1, 0, 0],
                [0, 1, 1, 0],
                [0, 0, 1, 0],
            ],
            [
                [0, 0, 0, 0],
                [0, 1, 1, 0],
                [0, 0, 1, 1],
                [0, 0, 0, 0],
            ],
            [
                [0, 0, 0, 0],
                [0, 0, 1, 0],
                [0, 1, 1, 0],
                [0, 1, 0, 0],
            ],
            [
                [0, 0, 0, 0],
                [1, 1, 0, 0],
                [0, 1, 1, 0],
                [0, 0, 0, 0],
            ],
            [
                [0, 0, 1, 0],
                [0, 1, 1, 0],
                [0, 1, 0, 0],
                [0, 0, 0, 0],
            ],
            [
                [0, 0, 0, 0],
                [0, 1, 1, 0],
                [0, 0, 1, 0],
                [0, 0, 1, 0],
            ],
            [
                [0, 0, 0, 0],
                [0, 0, 1, 0],
                [1, 1, 1, 0],
                [0, 0, 0, 0],
            ],
            [
                [0, 1, 0, 0],
                [0, 1, 0, 0],
                [0, 1, 1, 0],
                [0, 0, 0, 0],
            ],
            [
                [0, 0, 0, 0],
                [0, 1, 1, 1],
                [0, 1, 0, 0],
                [0, 0, 0, 0],
            ],
            [
                [0, 0, 0, 0],
                [0, 1, 1, 0],
                [0, 1, 0, 0],
                [0, 1, 0, 0],
            ],
            [
                [0, 0, 0, 0],
                [1, 1, 1, 0],
                [0, 0, 1, 0],
                [0, 0, 0, 0],
            ],
            [
                [0, 0, 1, 0],
                [0, 0, 1, 0],
                [0, 1, 1, 0],
                [0, 0, 0, 0],
            ],
            [
                [0, 0, 0, 0],
                [0, 1, 0, 0],
                [0, 1, 1, 1],
                [0, 0, 0, 0],
            ],
            [
                [0, 0, 0, 0],
                [0, 0, 1, 0],
                [0, 1, 1, 1],
                [0, 0, 0, 0],
            ],
            [
                [0, 0, 0, 0],
                [0, 1, 0, 0],
                [0, 1, 1, 0],
                [0, 1, 0, 0],
            ],
            [
                [0, 0, 0, 0],
                [1, 1, 1, 0],
                [0, 1, 0, 0],
                [0, 0, 0, 0],
            ],
            [
                [0, 0, 1, 0],
                [0, 1, 1, 0],
                [0, 0, 1, 0],
                [0, 0, 0, 0],
            ]
        ];
        /** 方块初始位置x坐标 */
        x = 5;
        /** 方块初始位置y坐标 */
        y = 0;
        /** 记录上次刷新时间 */
        t = Date.now();
        /** 是否游戏结束 */
        gameOver: boolean = false;
        /** 方块颜色 */
        boxColor: SColor = new SColor();

        /**
         * 构造函数
         *
         * @param id canvas DOM id
         */
        constructor(id: string) {
            super(id);
            this.initMap();
            this.initColor();
        }

        /**
         * 键盘按下事件
         *
         * @param event 事件对象
         */
        onKeyDown(event: KeyboardEvent): void {
            if (this.gameOver) {
                return;
            }

            if (event.code == "KeyW") {
                if (!this.checkKnocked(this.x, this.y, (this.dir + 1) % 4)) {
                    this.dir = (this.dir + 1) % 4;
                }
            } else if (event.code == "KeyA") {
                if (!this.checkKnocked(this.x - 1, this.y, this.dir)) {
                    this.x--;
                }
            } else if (event.code == "KeyD") {
                if (!this.checkKnocked(this.x + 1, this.y, this.dir)) {
                    this.x++;
                }
            } else if (event.code == "KeyS") {
                if (!this.checkKnocked(this.x, this.y + 1, this.dir)) {
                    this.y++;
                } else {
                    this.fullMap();
                }
            }

            this.update();
        }

        /**
         * 键盘按下事件
         *
         * @param painter 绘制对象
         */
        onDraw(painter: SPainter): void {
            if (this.gameOver) {
                painter.brush.color = SColor.Blue;
                painter.font.size = 20;
                painter.font.textAlign = STextAlign.Center;
                painter.drawText("GAME OVER", 180, 30);
                return;
            }

            painter.clearRect(0, 0, this.width, this.height);

            painter.pen.color = new SColor("#0bc0ff");
            painter.brush.color = new SColor("#2accc7");
            this.drawMap(painter);

            painter.pen.color = this.boxColor;
            painter.brush.color = this.boxColor;
            this.drawBox(painter);

            if (Date.now() - this.t > 500) {
                if (!this.checkKnocked(this.x, this.y + 1, this.dir)) {
                    this.y++;
                } else {
                    this.fullMap();
                }
                this.t = Date.now();
            }
            this.update();
        }

        /**
         * 初始化背景
         */
        initMap(): void {
            this.gameOver = false;
            this.map = [];
            for (let row = 0; row < 22; row++) {
                const m1: number[] = [];
                for (let col = 0; col < 14; col++) {
                    if (row > 19 || col < 2 || col > 11) {
                        m1.push(-1);
                    } else {
                        m1.push(0);
                    }
                }
                this.map.push(m1);
            }
        }

        /**
         * 初始化方块形状
         */
        initBox(): void {
            this.x = 5;
            this.y = 0;
            this.boxType = Number(Math.floor(Math.random() * 7));
            this.dir = Number(Math.floor(Math.random() * 4));
            this.initColor();
        }

        /**
         * 初始化方块颜色
         */
        initColor(): void {
            const a = Number(Math.floor(Math.random() * 128));
            const b = Number(Math.floor(Math.random() * 128));
            const c = Number(Math.floor(Math.random() * 128));
            this.boxColor = new SColor(a, b, c);
        }

        /**
         * 绘制背景
         *
         * @param painter 绘制对象
         */
        private drawMap(painter: SPainter): void {
            for (let row = 0; row < 22; row++) {
                for (let col = 0; col < 14; col++) {
                    this.drawShape(painter, row, col, this.map[row][col]);
                    // if (n == 0) {
                    //     painter.drawRect(col * 30 + 11 - 60, row * 30 + 11, 28, 28);
                    // }
                }
            }
        }
        
        /**
         * 绘制实体图形
         * 
         * @param painter   绘制对象
         * @param row       行
         * @param col       列  
         * @param type      图类型
         */
        private drawShape(painter: SPainter, row: number, col: number, type: number): void {
            const x = col * 30 + 11 - 60;
            const y = row * 30 + 11;
            switch (type) {
                case 0:
                    break;
                case -1:
                    painter.drawRoundRect(x, y, 28, 28, 6);
                    break;
                default:
                    painter.drawRoundRect(x, y, 28, 28, 6);
                    break;
            }
        }

        /**
         * 绘制方块
         *
         * @param painter 绘制对象
         */
        private drawBox(painter: SPainter): void {
            for (let row = 0; row < 4; row++) {
                for (let col = 0; col < 4; col++) {
                    this.drawShape(painter, row + this.y, col + this.x, this.box[this.boxType * 4 + this.dir][row][col]);
                }
            }
        }

        /**
         * 是否碰撞
         *
         * @param x     x坐标
         * @param y     y坐标
         * @param dir   方块变形索引
         * @return 是否碰撞
         */
        private checkKnocked(x: number, y: number, dir: number): boolean {
            for (let row = 0; row < 4; row++) {
                for (let col = 0; col < 4; col++) {
                    if (this.box[this.boxType * 4 + dir][row][col] != 0 && this.map[y + row][x + col] != 0) {
                        return true;
                    }
                }
            }
            return false;
        }

        /**
         * 将方块合到背景中
         */
        private fullMap(): void {
            for (let row = 0; row < 4; row++) {
                for (let col = 0; col < 4; col++) {
                    if (this.box[this.boxType * 4 + this.dir][row][col] == 1) {
                        this.map[this.y + row][this.x + col] = 1;
                    }
                }
            }

            this.initBox();

            if (this.checkKnocked(this.x, this.y, this.dir)) {
                this.gameOver = true;
            }

            this.remove();
        }

        /**
         * 消除满行
         */
        private remove(): void {
            let removeCount = 1;
            for (let row = 0; row < 20; row++) {
                let flag = true;
                for (let col = 0; col < 14; col++) {
                    if (this.map[row][col] == 0) {
                        flag = false;
                        break;
                    }
                }

                if (flag) {
                    this.map.splice(row, 1);
                    this.map.unshift(this.randomRow(10, 0));
                    removeCount++;
                }
            }

            if (removeCount > 1) {
                this.$emit("score", removeCount);
                this.map.shift();
                this.map.splice(19, 0, this.randomRow());
            }
        }

        /**
         * 随机数组生成
         *
         * @return 数组
         */
        private randomRow(len: number = 10, num: number = 2): number[] {
            let array = new Array(len + 4).fill(2);
            for (let i = 2; i < array.length - 2; i++) {
                array[i] = Math.floor(num * Math.random());
            }

            return array;
        }
    }

    @Component
    export default class ElsfkCanvas extends Vue {
        id: string = uuid();
        scoreList: number[] = [100, 300, 600, 1000];
        score: number = 0;
        view: TestView | undefined = undefined;
        mounted(): void {
            this.view = new TestView(this.id);
            this.view.connect("score", this, this.changeScore);
            document.getElementById(this.id)!!.focus();
        }

        changeScore(v: SCanvasView, count: number[]) {
            this.score+= this.scoreList[count[0] - 2];
        }

        reset(){
            this.view!!.initMap();
            this.view!!.initBox();
            this.score = 0;
            this.view!!.update();
            document.getElementById(this.id)!!.focus();
        }
    }
</script>
