package starter;

import io.zzax.jadeite.debug.DebugStringConvertible;
import io.zzax.jadeite.lang.Obj;
import io.zzax.jadeite.test.Checker;

public class Main {
    public static void main(String[] args) {
        new Main().test();
    }

    public void delete(int[][] matrix, int x, int y) {
        matrix[y][x] = 0;
    }

    public void printIcon(int[][] matrix) {
        // TODO
    }

    public void fill(int[][] matrix) {
        // TODO
    }

    public void rollToRight(int[][] matrix) {
        // TODO
    }

    public void rollToTop(int[][] matrix) {
        // TODO
    }

    public void pushToRight(int[][] matrix) {
        // TODO
    }

    public void pushToTop(int[][] matrix) {
        // TODO
    }

    public void flipHorizontally(int[][] matrix) {
        // TODO
    }

    public void flipVertically(int[][] matrix) {
        // TODO
    }

    public void rotateRight(int[][] matrix) {
        // TODO
    }

    public void match(int[][] matrix) {
        // TODO
    }

    public void test() {

        Checker.task("delete", task -> {
            int[][] matrix = {
                {2, 1, 2, 3},
                {1, 2, 2, 3},
                {1, 1, 1, 3},
                {1, 2, 3, 1}
            };
            task.when("matrix before call", Matrix.of(matrix));
            delete(matrix, 2, 1);
            int[][] expected = {
                {2, 1, 2, 3},
                {1, 2, 0, 3},
                {1, 1, 1, 3},
                {1, 2, 3, 1}
            };
            task.check("matrix after call", Matrix.of(matrix), Matrix.of(expected));
        });


        Checker.task("printIcon", task -> {
            int[][] matrix = {
                {0, 1, 2, 3},
                {1, 0, 0, 3},
                {1, 1, 1, 3},
                {1, 2, 3, 0}
            };
            task.when("matrix data", Matrix.of(matrix));
            task.startCollectOutput();
            printIcon(matrix);

            task.expectPrintln(". $ @ #");
            task.expectPrintln("$ . . #");
            task.expectPrintln("$ $ $ #");
            task.expectPrintln("$ @ # .");
            task.softCheckOutput();
        });

        Checker.task("fill", task -> {
            int[][] matrix = {
                {0, 1, 2, 3},
                {1, 0, 0, 3},
                {1, 1, 1, 3},
                {1, 2, 3, 0}
            };
            task.when("matrix before call", Matrix.of(matrix));
            fill(matrix);
            boolean check = false;
            for (int[] row : matrix) {
                for (int value : row) {
                    if (value == 0) {
                        check = true;
                        break;
                    }
                }
            }
            task.check("exist zero after call", check, false);
        });

        Checker.task("rollToRight", task -> {
            int[][] matrix = {
                {0, 1, 2, 3},
                {1, 0, 0, 3},
                {1, 1, 1, 3},
                {1, 2, 3, 0}
            };
            task.when("matrix before call", Matrix.of(matrix));
            rollToRight(matrix);
            int[][] expected = {
                {3, 0, 1, 2},
                {3, 1, 0, 0},
                {3, 1, 1, 1},
                {0, 1, 2, 3}
            };
            task.check("matrix after call", Matrix.of(matrix), Matrix.of(expected));
        });

        Checker.task("rollToTop", task -> {
            int[][] matrix = {
                {0, 1, 2, 3},
                {1, 0, 0, 3},
                {1, 1, 1, 3},
                {1, 2, 3, 0}
            };
            task.when("matrix before call", Matrix.of(matrix));
            rollToTop(matrix);
            int[][] expected = {
                {1, 0, 0, 3},
                {1, 1, 1, 3},
                {1, 2, 3, 0},
                {0, 1, 2, 3},
            };
            task.check("matrix after call", Matrix.of(matrix), Matrix.of(expected));
        });

        Checker.task("pushToRight", task -> {
            int[][] matrix = {
                {0, 1, 2, 3},
                {1, 0, 0, 3},
                {1, 1, 1, 3},
                {1, 2, 3, 0}
            };
            task.when("matrix before call", Matrix.of(matrix));
            pushToRight(matrix);
            int[][] expected = {
                {0, 1, 2, 3},
                {0, 0, 1, 3},
                {1, 1, 1, 3},
                {0, 1, 2, 3}
            };
            task.check("matrix after call", Matrix.of(matrix), Matrix.of(expected));
        });

        Checker.task("pushToTop", task -> {
            int[][] matrix = {
                {0, 1, 2, 3},
                {1, 0, 0, 3},
                {1, 1, 1, 3},
                {1, 2, 3, 0}
            };
            task.when("matrix before call", Matrix.of(matrix));
            pushToTop(matrix);
            int[][] expected = {
                {1, 1, 2, 3},
                {1, 1, 1, 3},
                {1, 2, 3, 3},
                {0, 0, 0, 0}
            };
            task.check("matrix after call", Matrix.of(matrix), Matrix.of(expected));
        });

        Checker.task("flipHorizontally", task -> {
            int[][] matrix = {
                {0, 1, 2, 3},
                {1, 0, 0, 3},
                {1, 1, 1, 3},
                {1, 2, 3, 0}
            };
            task.when("matrix before call", Matrix.of(matrix));
            flipHorizontally(matrix);
            int[][] expected = {
                {3, 2, 1, 0},
                {3, 0, 0, 1},
                {3, 1, 1, 1},
                {0, 3, 2, 1}
            };
            task.check("matrix after call", Matrix.of(matrix), Matrix.of(expected));
        });

        Checker.task("flipVertically", task -> {
            int[][] matrix = {
                {0, 1, 2, 3},
                {1, 0, 0, 3},
                {1, 1, 1, 3},
                {1, 2, 3, 0}
            };
            task.when("matrix before call", Matrix.of(matrix));
            flipVertically(matrix);
            int[][] expected = {
                {1, 2, 3, 0},
                {1, 1, 1, 3},
                {1, 0, 0, 3},
                {0, 1, 2, 3},
            };
            task.check("matrix after call", Matrix.of(matrix), Matrix.of(expected));
        });

        Checker.task("rotateRight", task -> {
            int[][] matrix = {
                {0, 1, 2, 3},
                {1, 0, 0, 3},
                {1, 1, 1, 3},
                {1, 2, 3, 0}
            };
            task.when("matrix before call", Matrix.of(matrix));
            rotateRight(matrix);
            int[][] expected = {
                {1, 1, 1, 0},
                {2, 1, 0, 1},
                {3, 1, 0, 2},
                {0, 3, 3, 3},
            };
            task.check("matrix after call", Matrix.of(matrix), Matrix.of(expected));
        });

        Checker.task("match 1", task -> {
            int[][] matrix = {
                {0, 1, 2, 3},
                {3, 0, 0, 2},
                {1, 1, 1, 3},
                {2, 2, 3, 0}
            };
            task.when("matrix before call", Matrix.of(matrix));
            match(matrix);
            int[][] expected = {
                {0, 1, 2, 3},
                {3, 0, 0, 2},
                {0, 0, 0, 3},
                {2, 2, 3, 0}
            };
            task.check("matrix after call", Matrix.of(matrix), Matrix.of(expected));
        });

        Checker.task("match 2", task -> {
            int[][] matrix = {
                {0, 1, 2, 3},
                {1, 0, 0, 3},
                {1, 1, 1, 3},
                {1, 2, 3, 0}
            };
            task.when("matrix before call", Matrix.of(matrix));
            match(matrix);
            int[][] expected = {
                {0, 1, 2, 0},
                {0, 0, 0, 0},
                {0, 0, 0, 0},
                {0, 2, 3, 0}
            };
            task.check("matrix after call", Matrix.of(matrix), Matrix.of(expected));
        });
    }

    static class Matrix implements DebugStringConvertible {
        int[][] matrix;

        public static Matrix of(int[][] matrix) {
            Matrix object = new Matrix();
            object.matrix = matrix;
            return object;
        }

        public String toDebugString() {
            StringBuilder builder = new StringBuilder();
            for (int i = 0; i < matrix.length; i++) {
                builder.append("\n");
                for (int j = 0; j < matrix[i].length; j++) {
                    builder.append(matrix[i][j]);
                    builder.append(" ");
                }
            }
            return builder.toString();
        }

        @Override
        public boolean equals(Object o) {
            return Obj.equals(this.matrix, ((Matrix)o).matrix);
        }
    }
}
