#include <fstream>
#include <filesystem>
#include <string>
#include <iostream>
#include <cstring>

/*

   THIS FILE IS AUTO-GENERATED BY DEHYDRATE.
   DO NOT EDIT THIS FILE.

*/


#include "_original.hpp"
namespace recreate_original {


// Tiny dependency-free FNV-1a 64-bit hash
static uint64_t fnv1a_64(const void* data, size_t len) {
    const uint8_t* p = static_cast<const uint8_t*>(data);
    uint64_t h = 0xcbf29ce484222325ULL;
    for (size_t i = 0; i < len; ++i)
        h = (h ^ p[i]) * 0x100000001b3ULL;
    return h;
}


// Base64 decoding function - no dependencies
static void base64_decode(const char* encoded_data, size_t encoded_len, unsigned char* output, size_t* output_len) {
    const char* base64_chars = 
        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    size_t out_pos = 0;
    int val = 0, valb = -8;
    
    for (size_t i = 0; i < encoded_len; i++) {
        char c = encoded_data[i];
        if (c == '=') break;
        
        // Find position in base64_chars
        const char* pos = strchr(base64_chars, c);
        if (pos == nullptr) continue; // Skip invalid characters
        
        val = (val << 6) + static_cast<int>(pos - base64_chars);
        valb += 6;
        if (valb >= 0) {
            output[out_pos++] = static_cast<unsigned char>((val >> valb) & 0xFF);
            valb -= 8;
        }
    }
    
    *output_len = out_pos;
}

// Utility function to recreate a file with proper permissions
static bool _recreate_file_(const std::filesystem::path& outpath, uint64_t file_hash, std::filesystem::perms file_perms, const unsigned char* filedata, size_t filedata_len) {
    namespace fs = std::filesystem;
    bool needs_write = false;
    
    // Check if file exists and has correct hash
    if (fs::exists(outpath)) {
        // Check content hash
        std::ifstream in(outpath, std::ios::binary);
        std::ostringstream oss;
        oss << in.rdbuf();
        std::string data = oss.str();
        uint64_t existing_hash = fnv1a_64(data.data(), data.size());
        needs_write = existing_hash != file_hash;
    } else {
        needs_write = true; // File doesn't exist, need to create it
    }

    bool needs_permission_update = true;
    if (!needs_write) { // we always update permissions if the file is written or changed. Othewise we check.
        fs::perms current_perms = fs::status(outpath).permissions();
        needs_permission_update = current_perms != file_perms;
    }

    if (needs_write) {
        bool existed = fs::exists(outpath);

        fs::create_directories(outpath.parent_path());
        std::ofstream out(outpath, std::ios::binary);
        out.write(reinterpret_cast<const char*>(filedata), filedata_len);
        out.close();
        // Set the file permissions
        fs::permissions(outpath, file_perms);
        
        if (!existed) {
            std::cout << "[dehydrate] " << outpath.filename() << ": created\n";
        } else {
            std::cout << "[dehydrate] " << outpath.filename() << ": updated (hash changed)\n";
        }
        return true;
    } 
    
    if (needs_permission_update) {
        // Update only permissions
        fs::permissions(outpath, file_perms);
        std::cout << "[dehydrate] " << outpath.filename() << ": updated (permissions changed)\n";
        return true;
    }
    
    return false;
}

bool recreate_tree(std::string destination_folder) {
    namespace fs = std::filesystem;
    bool any_written = false;
    {
        // File: small.txt
        fs::path outpath = fs::path(destination_folder) / "small.txt";
        static const char filedata_base64[] = "eA==";

        // Decode Base64 data
        size_t decoded_size = (strlen(filedata_base64) * 3) / 4;
        unsigned char* decoded_data = new unsigned char[decoded_size];
        size_t actual_size;
        base64_decode(filedata_base64, strlen(filedata_base64), decoded_data, &actual_size);

        bool file_written = _recreate_file_(outpath, 12638214688346347271ULL, std::filesystem::perms(384), decoded_data, actual_size);
        delete[] decoded_data;
        any_written = any_written || file_written;
    }
    {
        // File: test2.bin
        fs::path outpath = fs::path(destination_folder) / "test2.bin";
        static const char filedata_base64[] = "AP9CEzferb7v";

        // Decode Base64 data
        size_t decoded_size = (strlen(filedata_base64) * 3) / 4;
        unsigned char* decoded_data = new unsigned char[decoded_size];
        size_t actual_size;
        base64_decode(filedata_base64, strlen(filedata_base64), decoded_data, &actual_size);

        bool file_written = _recreate_file_(outpath, 10042072622899139650ULL, std::filesystem::perms(448), decoded_data, actual_size);
        delete[] decoded_data;
        any_written = any_written || file_written;
    }
    {
        // File: test3.sh
        fs::path outpath = fs::path(destination_folder) / "test3.sh";
        static const char filedata_base64[] = "IyEvYmluL2Jhc2gKZWNobyAnSGVsbG8gZnJvbSB0ZXN0IHNjcmlwdCcKZXhpdCAw";

        // Decode Base64 data
        size_t decoded_size = (strlen(filedata_base64) * 3) / 4;
        unsigned char* decoded_data = new unsigned char[decoded_size];
        size_t actual_size;
        base64_decode(filedata_base64, strlen(filedata_base64), decoded_data, &actual_size);

        bool file_written = _recreate_file_(outpath, 14335927320996074478ULL, std::filesystem::perms(488), decoded_data, actual_size);
        delete[] decoded_data;
        any_written = any_written || file_written;
    }
    {
        // File: test1.txt
        fs::path outpath = fs::path(destination_folder) / "test1.txt";
        static const char filedata_base64[] = "VGhpcyBpcyBhIHNpbXBsZSB0ZXh0IGZpbGUuCkl0IGhhcyBtdWx0aXBsZSBsaW5lcy4KTGluZSAz"\
            "Lg==";

        // Decode Base64 data
        size_t decoded_size = (strlen(filedata_base64) * 3) / 4;
        unsigned char* decoded_data = new unsigned char[decoded_size];
        size_t actual_size;
        base64_decode(filedata_base64, strlen(filedata_base64), decoded_data, &actual_size);

        bool file_written = _recreate_file_(outpath, 11900461415522640014ULL, std::filesystem::perms(384), decoded_data, actual_size);
        delete[] decoded_data;
        any_written = any_written || file_written;
    }
    {
        // File: subdir/nested.txt
        fs::path outpath = fs::path(destination_folder) / "subdir/nested.txt";
        static const char filedata_base64[] = "VGhpcyBmaWxlIGlzIGluIGEgc3ViZGlyZWN0b3J5Lg==";

        // Decode Base64 data
        size_t decoded_size = (strlen(filedata_base64) * 3) / 4;
        unsigned char* decoded_data = new unsigned char[decoded_size];
        size_t actual_size;
        base64_decode(filedata_base64, strlen(filedata_base64), decoded_data, &actual_size);

        bool file_written = _recreate_file_(outpath, 14153000318456068100ULL, std::filesystem::perms(256), decoded_data, actual_size);
        delete[] decoded_data;
        any_written = any_written || file_written;
    }
    return any_written;
}
}
