// SPDX-License-Identifier: Apache-2.0
// Copyright 2025 Sudoku Solver Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "SudokuSolver.h"
#include <iomanip>

/**
 * @brief Default constructor
 */
SudokuSolver::SudokuSolver() : mSolutionCount(0), mBacktrackCount(0) {
}

/**
 * @brief Destructor
 */
SudokuSolver::~SudokuSolver() {
}

/**
 * @brief Solves the Sudoku puzzle using backtracking algorithm
 * @param grid Reference to the 9x9 grid to solve
 * @return true if puzzle is solvable, false otherwise
 */
bool SudokuSolver::SolvePuzzle(std::vector<std::vector<int>>& grid) {
    ResetCounters();
    
    if (!IsValidGrid(grid)) {
        std::cerr << "Error: Invalid grid provided" << std::endl;
        return false;
    }
    
    bool result = SolveRecursive(grid);
    if (result) {
        mSolutionCount = 1;
    }
    
    return result;
}

/**
 * @brief Prints the Sudoku grid in a formatted manner
 * @param grid The grid to print
 */
void SudokuSolver::PrintGrid(const std::vector<std::vector<int>>& grid) const {
    std::cout << "\n+-------+-------+-------+" << std::endl;
    
    for (int row = 0; row < GRID_SIZE; row++) {
        std::cout << "| ";
        
        for (int col = 0; col < GRID_SIZE; col++) {
            if (grid[row][col] == EMPTY_CELL) {
                std::cout << ". ";
            } else {
                std::cout << grid[row][col] << " ";
            }
            
            if ((col + 1) % SUBGRID_SIZE == 0) {
                std::cout << "| ";
            }
        }
        
        std::cout << std::endl;
        
        if ((row + 1) % SUBGRID_SIZE == 0) {
            std::cout << "+-------+-------+-------+" << std::endl;
        }
    }
    std::cout << std::endl;
}

/**
 * @brief Validates if the grid is a proper 9x9 Sudoku grid
 * @param grid The grid to validate
 * @return true if valid, false otherwise
 */
bool SudokuSolver::IsValidGrid(const std::vector<std::vector<int>>& grid) const {
    if (grid.size() != GRID_SIZE) {
        return false;
    }
    
    for (int i = 0; i < GRID_SIZE; i++) {
        if (grid[i].size() != GRID_SIZE) {
            return false;
        }
        
        for (int j = 0; j < GRID_SIZE; j++) {
            int value = grid[i][j];
            if (value < 0 || value > 9) {
                return false;
            }
            
            // Check if current non-empty cell violates Sudoku rules
            if (value != EMPTY_CELL) {
                // Temporarily remove the value to check if it's valid in this position
                std::vector<std::vector<int>> temp_grid = grid;
                temp_grid[i][j] = EMPTY_CELL;
                
                if (!IsNumberSafe(temp_grid, i, j, value)) {
                    return false;
                }
            }
        }
    }
    
    return true;
}

/**
 * @brief Loads a puzzle from a 2D array into the grid
 * @param grid Reference to the grid to load into
 * @param puzzle The source puzzle array
 */
void SudokuSolver::LoadPuzzle(std::vector<std::vector<int>>& grid, const int puzzle[9][9]) {
    grid.resize(GRID_SIZE);
    for (int i = 0; i < GRID_SIZE; i++) {
        grid[i].resize(GRID_SIZE);
        for (int j = 0; j < GRID_SIZE; j++) {
            grid[i][j] = puzzle[i][j];
        }
    }
}

/**
 * @brief Finds the next empty cell in the grid
 * @param grid The grid to search
 * @param row Reference to store the row of empty cell
 * @param col Reference to store the column of empty cell
 * @return true if empty cell found, false if grid is complete
 */
bool SudokuSolver::FindEmptyCell(const std::vector<std::vector<int>>& grid, int& row, int& col) const {
    for (row = 0; row < GRID_SIZE; row++) {
        for (col = 0; col < GRID_SIZE; col++) {
            if (grid[row][col] == EMPTY_CELL) {
                return true;
            }
        }
    }
    return false;
}

/**
 * @brief Checks if it's safe to place a number at given position
 * @param grid The current grid state
 * @param row Row position
 * @param col Column position
 * @param num Number to check
 * @return true if safe to place, false otherwise
 */
bool SudokuSolver::IsNumberSafe(const std::vector<std::vector<int>>& grid, int row, int col, int num) const {
    return !IsUsedInRow(grid, row, num) &&
           !IsUsedInCol(grid, col, num) &&
           !IsUsedInSubgrid(grid, row - row % SUBGRID_SIZE, col - col % SUBGRID_SIZE, num);
}

/**
 * @brief Checks if number is already used in the given row
 * @param grid The current grid state
 * @param row Row to check
 * @param num Number to check for
 * @return true if number is used in row, false otherwise
 */
bool SudokuSolver::IsUsedInRow(const std::vector<std::vector<int>>& grid, int row, int num) const {
    for (int col = 0; col < GRID_SIZE; col++) {
        if (grid[row][col] == num) {
            return true;
        }
    }
    return false;
}

/**
 * @brief Checks if number is already used in the given column
 * @param grid The current grid state
 * @param col Column to check
 * @param num Number to check for
 * @return true if number is used in column, false otherwise
 */
bool SudokuSolver::IsUsedInCol(const std::vector<std::vector<int>>& grid, int col, int num) const {
    for (int row = 0; row < GRID_SIZE; row++) {
        if (grid[row][col] == num) {
            return true;
        }
    }
    return false;
}

/**
 * @brief Checks if number is already used in the 3x3 subgrid
 * @param grid The current grid state
 * @param startRow Starting row of the subgrid
 * @param startCol Starting column of the subgrid
 * @param num Number to check for
 * @return true if number is used in subgrid, false otherwise
 */
bool SudokuSolver::IsUsedInSubgrid(const std::vector<std::vector<int>>& grid, int startRow, int startCol, int num) const {
    for (int row = 0; row < SUBGRID_SIZE; row++) {
        for (int col = 0; col < SUBGRID_SIZE; col++) {
            if (grid[row + startRow][col + startCol] == num) {
                return true;
            }
        }
    }
    return false;
}

/**
 * @brief Recursive backtracking solver
 * @param grid Reference to the grid being solved
 * @return true if solution found, false otherwise
 */
bool SudokuSolver::SolveRecursive(std::vector<std::vector<int>>& grid) {
    int row, col;
    
    // If no empty cell found, puzzle is solved
    if (!FindEmptyCell(grid, row, col)) {
        return true;
    }
    
    // Try numbers 1-9
    for (int num = 1; num <= 9; num++) {
        if (IsNumberSafe(grid, row, col, num)) {
            // Place the number
            grid[row][col] = num;
            
            // Recursively solve the rest
            if (SolveRecursive(grid)) {
                return true;
            }
            
            // If placing num doesn't lead to solution, backtrack
            grid[row][col] = EMPTY_CELL;
            mBacktrackCount++;
        }
    }
    
    // No solution found
    return false;
}

/**
 * @brief Resets the solution and backtrack counters
 */
void SudokuSolver::ResetCounters() {
    mSolutionCount = 0;
    mBacktrackCount = 0;
}