#!python3
"""
# A Backtracking program in Python to solve Sudoku problem
"""
import sys


def is_valid(grid):
    print("INPUT GRID IS:")
    print_grid(grid)
    print()
    print()
    if len(grid) != 9:
        return False
    if len(grid[0]) != 9:
        return False
    return True


def read_grid_from_file(filename):
    with open(filename, 'r') as f:
        rows = f.readlines()
        grid = [[int(i) for i in row.split(',')] for row in rows]
        return grid


def print_grid(arr):
    """
    # A Utility Function to print the Grid
    """
    # print("SOLUTION IS:")
    for i in range(9):
        print(' '.join(str(x) for x in arr[i]))


def find_empty_location(arr, l):
    """
    # Function to Find the entry in the Grid that is still not used
    # Searches the grid to find an entry that is still unassigned. If
    # found, the reference parameters row, col will be set the location
    # that is unassigned, and true is returned. If no unassigned entries
    # remain, false is returned.
    # 'l' is a list variable that has been passed from the solve_sudoku function
    # to keep track of incrementation of Rows and Columns
    """
    for row in range(9):
        for col in range(9):
            if(arr[row][col] == 0):
                l[0] = row
                l[1] = col
                return True
    return False


def used_in_row(arr, row, num):
    """
    # Returns a boolean which indicates whether any assigned entry
    # in the specified row matches the given number.
    """
    for i in range(9):
        if(arr[row][i] == num):
            return True
    return False


def used_in_col(arr, col, num):
    """
    # Returns a boolean which indicates whether any assigned entry
    # in the specified column matches the given number.
    """
    for i in range(9):
        if(arr[i][col] == num):
            return True
    return False


def used_in_box(arr, row, col, num):
    """
    # Returns a boolean which indicates whether any assigned entry
    # within the specified 3x3 box matches the given number
    """
    for i in range(3):
        for j in range(3):
            if(arr[i+row][j+col] == num):
                return True
    return False


def check_location_is_safe(arr, row, col, num):
    """
    # Checks whether it will be legal to assign num to the given row,col
    # Returns a boolean which indicates whether it will be legal to assign
    # num to the given row,col location.
    """
    # Check if 'num' is not already placed in current row,
    # current column and current 3x3 box
    return not used_in_row(arr, row, num) and not used_in_col(arr, col, num) and not used_in_box(arr, row - row % 3, col - col % 3, num)


def solve_sudoku(arr):
    """
   # Takes a partially filled-in grid and attempts to assign values to
   # all unassigned locations in such a way to meet the requirements
   # for Sudoku solution (non-duplication across rows, columns, and boxes)
    """

    # 'l' is a list variable that keeps the record of row and col in find_empty_location Function
    l = [0, 0]

    # If there is no unassigned location, we are done
    if(not find_empty_location(arr, l)):
        return True

    # Assigning list values to row and col that we got from the above Function
    row = l[0]
    col = l[1]

    # consider digits 1 to 9
    for num in range(1, 10):

        # if looks promising
        if(check_location_is_safe(arr, row, col, num)):

            # make tentative assignment
            arr[row][col] = num

            # return, if success, ya!
            if(solve_sudoku(arr)):
                return True

            # failure, unmake & try again
            arr[row][col] = 0

    # this triggers backtracking
    return False


# Driver main function to test above functions
if __name__ == "__main__":
    from argparse import ArgumentParser
    from datetime import datetime
    year = datetime.now().date().year
    epilog = f"""
    Author: Tralah M Brian\n
    Github: <https://github.com/TralahM>\n
    Twitter: <https://twitter.com/TralahM>\n
    Copyright: (c) {year}.\n
    """
    parser = ArgumentParser(epilog=epilog)
    parser.add_argument('-f', action='store', dest='filegrid', default=None,
                        help="(CSV comma separated values) file containing sudoku puzzle to solve empty cells should be 0, if not specified an example puzzle is solved")
    args = parser.parse_args()
    # creating a 2D array for the grid
    grid = [[0 for x in range(9)]for y in range(9)]

    # assigning values to the grid
    grid = [[3, 0, 6, 5, 0, 8, 4, 0, 0],
            [5, 2, 0, 0, 0, 0, 0, 0, 0],
            [0, 8, 7, 0, 0, 0, 0, 3, 1],
            [0, 0, 3, 0, 1, 0, 0, 8, 0],
            [9, 0, 0, 8, 6, 3, 0, 0, 5],
            [0, 5, 0, 0, 9, 0, 6, 0, 0],
            [1, 3, 0, 0, 0, 0, 2, 5, 0],
            [0, 0, 0, 0, 0, 0, 0, 7, 4],
            [0, 0, 5, 2, 0, 6, 3, 0, 0]]
    if args.filegrid is not None:
        grid = read_grid_from_file(args.filegrid)
    #  Check  if  grid  is  valid
    if not is_valid(grid):
        print("Please specify a valid grid")
        sys.exit()
    # if success print the grid
    if(solve_sudoku(grid)):
        print("SOLUTION IS:")
        print_grid(grid)
    else:
        print("No solution exists")
