#include "utils.hpp"
#include <iostream>
#include <string>
#include <fstream>
#include <vector>
#include <algorithm>
#include <filesystem>
namespace dropshell {

void maketitle(const std::string& title) {
    std::cout << std::string(title.length() + 4, '-') << std::endl;
    std::cout << "| " << title << " |" << std::endl;
    std::cout << std::string(title.length() + 4, '-') << std::endl;
}

bool replace_line_in_file(const std::string& file_path, const std::string& search_string, const std::string& replacement_line) {
    std::ifstream input_file(file_path);
    std::vector<std::string> file_lines;
    std::string line;

    if (!input_file.is_open()) {
        std::cerr << "Error: Unable to open file: " << file_path << std::endl;
        return false;
    }

    while (std::getline(input_file, line)) {
        if (line.find(search_string) != std::string::npos)
            file_lines.push_back(replacement_line);
            else
                file_lines.push_back(line);
        }
    input_file.close();
        
    std::ofstream output_file(file_path);
    if (!output_file.is_open())
    {
        std::cerr << "Error: Unable to open file: " << file_path << std::endl;
        return false;
    }
    for (const auto& modified_line : file_lines)
        output_file << modified_line << "\n";
    output_file.close();
    return true;
}

std::string trim(std::string str) {
    // Trim leading whitespace
    str.erase(str.begin(), std::find_if(str.begin(), str.end(), [](unsigned char ch) {
        return !std::isspace(ch);
    }));

    // Trim trailing whitespace
    str.erase(std::find_if(str.rbegin(), str.rend(), [](unsigned char ch) {
        return !std::isspace(ch);
    }).base(), str.end());

    return str;
}

std::string dequote(std::string str)
{
    if (str.length() < 2)
        return str;
    if (str.front() == '"' && str.back() == '"') {
        return str.substr(1, str.length() - 2);
    }
    return str;
}

std::string quote(std::string str)
{
    return "\""+str+"\"";
}

std::string multi2string(std::vector<std::string> values)
{
    std::string result;
    for (const auto& value : values) {
        // remove any " contained in the string value, if present
        result += dequote(trim(value)) + ",";
    }
    if (!result.empty()) 
        result.pop_back(); // Remove the last comma
    
    return result;
}

std::vector<std::string> string2multi(std::string values)
{
    std::vector<std::string> result;

    values = dequote(trim(values));

    // Return values separated by commas, but ignore commas within quotes
    bool inside_quotes = false;
    std::string current_item;
    
    for (char c : values) {
        if (c == '"') {
            inside_quotes = !inside_quotes;
        } else if (c == ',' && !inside_quotes) {
            if (!current_item.empty()) {
                std::string final = dequote(trim(current_item));
                if (!final.empty()) 
                    result.push_back(final);
                current_item.clear();
            }
        } else {
            current_item += c;
        }
    }
    
    // Add the last item if not empty
    if (!current_item.empty()) {
        std::string final = dequote(trim(current_item));
        if (!final.empty()) 
            result.push_back(final);
    }
    
    return result;
}

int str2int(const std::string &str)
{
    try {
        return std::stoi(str);
    } catch (const std::exception& e) {
        std::cerr << "Error: Invalid integer string: [" << str << "]" << std::endl;
        return 0;
    }
}


void recursive_copy(const std::string & source, const std::string & destination) {
    try {
        if (std::filesystem::is_directory(source)) {
            if (!std::filesystem::exists(destination)) {
                std::filesystem::create_directory(destination);
            }
            for (const auto& entry : std::filesystem::directory_iterator(source)) {
                recursive_copy(entry.path(), destination / entry.path().filename());
            }
        } else if (std::filesystem::is_regular_file(source)) {
            std::filesystem::copy(source, destination, std::filesystem::copy_options::overwrite_existing);
        }
    } catch (const std::filesystem::filesystem_error& ex) {
        std::cerr << "Error copying " << source << " to " << destination << ": " << ex.what() << std::endl;
    }
}

void ensure_directories_exist(std::vector<std::string> directories)
{
    for (const auto& directory : directories) {
        if (!std::filesystem::exists(directory)) {
            std::filesystem::create_directories(directory);
        }
    }
}


//https://www.geeksforgeeks.org/kmp-algorithm-for-pattern-searching/
void constructLps(const std::string &pat, std::vector<int> &lps) {

    // len stores the length of longest prefix which
    // is also a suffix for the previous index
    int len = 0;

    // lps[0] is always 0
    lps[0] = 0;

    int i = 1;
    while (i < pat.length()) {

        // If characters match, increment the size of lps
        if (pat[i] == pat[len]) {
            len++;
            lps[i] = len;
            i++;
        }

        // If there is a mismatch
        else {
            if (len != 0) {

                // Update len to the previous lps value
                // to avoid reduntant comparisons
                len = lps[len - 1];
            }
            else {

                // If no matching prefix found, set lps[i] to 0
                lps[i] = 0;
                i++;
            }
        }
    }
}

std::vector<int> search(const std::string &pat, const std::string &txt) {
    int n = txt.length();
    int m = pat.length();

    std::vector<int> lps(m);
    std::vector<int> res;

    constructLps(pat, lps);

    // Pointers i and j, for traversing
    // the text and pattern
    int i = 0;
    int j = 0;

    while (i < n) {

        // If characters match, move both pointers forward
        if (txt[i] == pat[j]) {
            i++;
            j++;

            // If the entire pattern is matched
            // store the start index in result
            if (j == m) {
                res.push_back(i - j);

                // Use LPS of previous index to
                // skip unnecessary comparisons
                j = lps[j - 1];
            }
        }

        // If there is a mismatch
        else {

            // Use lps value of previous index
            // to avoid redundant comparisons
            if (j != 0)
                j = lps[j - 1];
            else
                i++;
        }
    }
    return res;
}

int count_substring(const std::string &substring, const std::string &text) {
    std::vector<int> positions = search(substring, text);
    return positions.size();
}

std::vector<std::string> split(const std::string& str, const std::string& delimiter) {
    std::vector<std::string> tokens;
    size_t start = 0;
    size_t end = 0;
    
    while ((end = str.find(delimiter, start)) != std::string::npos) {
        tokens.push_back(str.substr(start, end - start));
        start = end + delimiter.length();
    }
    
    // Add the last token
    tokens.push_back(str.substr(start));
    
    return tokens;
}

} // namespace dropshell 