// SPDX-License-Identifier: Apache-2.0
// Copyright 2025 Letter Combinations Solution
//
// 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 "LetterCombinations.h"
#include <iostream>
#include <cassert>

// Constructor
LetterCombinations::LetterCombinations() {
    // Initialize digit to letters mapping
    mDigitMap[2] = "abc";
    mDigitMap[3] = "def";
    mDigitMap[4] = "ghi";
    mDigitMap[5] = "jkl";
    mDigitMap[6] = "mno";
    mDigitMap[7] = "pqrs";
    mDigitMap[8] = "tuv";
    mDigitMap[9] = "wxyz";
}

// Main public method to get letter combinations
std::vector<std::string> LetterCombinations::GetCombinations(const std::string& digits) {
    std::vector<std::string> result;
    
    // Handle empty input
    if (digits.empty()) {
        return result;
    }
    
    // Validate input contains only valid digits
    if (!IsValidInput(digits)) {
        return result;
    }
    
    std::string current_combination = "";
    GenerateCombinationsRecursive(digits, 0, current_combination, result);
    
    return result;
}

// Recursive helper method to generate combinations
void LetterCombinations::GenerateCombinationsRecursive(const std::string& digits, 
                                                      int digit_index,
                                                      std::string& current_combination,
                                                      std::vector<std::string>& result) {
    // Base case: processed all digits
    if (digit_index == static_cast<int>(digits.length())) {
        result.push_back(current_combination);
        return;
    }
    
    // Get current digit and its corresponding letters
    int current_digit = digits[digit_index] - '0';
    const std::string& letters = mDigitMap[current_digit];
    
    // Try each letter for current digit
    for (char letter : letters) {
        current_combination.push_back(letter);
        GenerateCombinationsRecursive(digits, digit_index + 1, current_combination, result);
        current_combination.pop_back(); // backtrack
    }
}

// Iterative solution using queue-based approach
std::vector<std::string> LetterCombinations::GetCombinationsIterative(const std::string& digits) {
    std::vector<std::string> result;
    
    // Handle empty input
    if (digits.empty()) {
        return result;
    }
    
    // Validate input
    if (!IsValidInput(digits)) {
        return result;
    }
    
    // Initialize with empty string
    result.push_back("");
    
    // Process each digit
    for (char digit_char : digits) {
        int digit = digit_char - '0';
        const std::string& letters = mDigitMap[digit];
        
        std::vector<std::string> temp_result;
        
        // For each existing combination, append each possible letter
        for (const std::string& combination : result) {
            for (char letter : letters) {
                temp_result.push_back(combination + letter);
            }
        }
        
        result = temp_result;
    }
    
    return result;
}

// Validate input contains only digits 2-9
bool LetterCombinations::IsValidInput(const std::string& digits) const {
    for (char digit_char : digits) {
        int digit = digit_char - '0';
        if (digit < 2 || digit > 9) {
            return false;
        }
    }
    return true;
}

// Get the mapping for a specific digit
std::string LetterCombinations::GetLettersForDigit(int digit) const {
    auto it = mDigitMap.find(digit);
    if (it != mDigitMap.end()) {
        return it->second;
    }
    return "";
}

// Print all combinations (utility method)
void LetterCombinations::PrintCombinations(const std::vector<std::string>& combinations) const {
    std::cout << "Letter combinations: [";
    for (size_t i = 0; i < combinations.size(); ++i) {
        std::cout << "\"" << combinations[i] << "\"";
        if (i < combinations.size() - 1) {
            std::cout << ", ";
        }
    }
    std::cout << "]" << std::endl;
}

// Demo function to show usage
void LetterCombinations::RunDemo() {
    std::cout << "=== Letter Combinations of Phone Number Demo ===" << std::endl;
    
    // Test cases
    std::vector<std::string> test_cases = {"23", "2", "", "234", "7"};
    
    for (const std::string& test_digits : test_cases) {
        std::cout << "\nInput: \"" << test_digits << "\"" << std::endl;
        
        // Test recursive solution
        auto recursive_result = GetCombinations(test_digits);
        std::cout << "Recursive solution: ";
        PrintCombinations(recursive_result);
        
        // Test iterative solution
        auto iterative_result = GetCombinationsIterative(test_digits);
        std::cout << "Iterative solution: ";
        PrintCombinations(iterative_result);
        
        // Verify both solutions produce same results
        assert(recursive_result == iterative_result);
        std::cout << "✓ Both solutions match!" << std::endl;
    }
}

