#include "simple_parallel_walker.h"
#include "tree.h"
#include <algorithm>
#include <cstring>

namespace cutysoup {

SimpleParallelWalker::SimpleParallelWalker(const Config& config) 
    : config_(config) {
}

std::vector<GumboNode*> SimpleParallelWalker::collect_all_nodes(GumboNode* root) const {
    std::vector<GumboNode*> nodes;
    
    if (!root) return nodes;
    
    // Pre-allocate reasonable capacity to avoid frequent reallocations
    nodes.reserve(1000);
    
    std::function<void(GumboNode*)> collect = [&](GumboNode* node) {
        if (!node) return;
        
        nodes.push_back(node);
        
        if (node->type == GUMBO_NODE_ELEMENT) {
            const GumboVector* children = &node->v.element.children;
            for (unsigned int i = 0; i < children->length; ++i) {
                collect(static_cast<GumboNode*>(children->data[i]));
            }
        }
    };
    
    collect(root);
    return nodes;
}

std::vector<std::vector<GumboNode*>> SimpleParallelWalker::distribute_nodes(
    const std::vector<GumboNode*>& all_nodes,
    size_t thread_count
) const {
    std::vector<std::vector<GumboNode*>> chunks(thread_count);
    
    if (all_nodes.empty() || thread_count == 0) {
        return chunks;
    }
    
    // Distribute nodes evenly across threads
    size_t chunk_size = all_nodes.size() / thread_count;
    size_t remainder = all_nodes.size() % thread_count;
    
    size_t start_idx = 0;
    for (size_t i = 0; i < thread_count; ++i) {
        size_t current_chunk_size = chunk_size + (i < remainder ? 1 : 0);
        
        if (current_chunk_size > 0 && start_idx < all_nodes.size()) {
            size_t end_idx = std::min(start_idx + current_chunk_size, all_nodes.size());
            chunks[i].assign(all_nodes.begin() + start_idx, all_nodes.begin() + end_idx);
            start_idx = end_idx;
        }
    }
    
    return chunks;
}

std::shared_ptr<Node> SimpleParallelWalker::create_node(GumboNode* gumbo_node, const Tree* tree) const {
    if (!gumbo_node || !tree) return nullptr;
    return tree->get_or_create_node(gumbo_node);
}

// Specialized searches with optimized predicates

std::vector<std::shared_ptr<Node>> SimpleParallelWalker::find_by_tag_parallel(
    GumboNode* root,
    const Tree* tree,
    const std::string& tag_name,
    const Config& config
) {
    // Convert tag name to Gumbo tag enum for faster comparison
    GumboTag target_tag = gumbo_tag_enum(tag_name.c_str());
    
    auto predicate = [target_tag, &tag_name](GumboNode* node) -> bool {
        if (!node || node->type != GUMBO_NODE_ELEMENT) {
            return false;
        }
        
        // Fast comparison using enum if possible
        if (target_tag != GUMBO_TAG_UNKNOWN) {
            return node->v.element.tag == target_tag;
        }
        
        // Fallback to string comparison for custom tags
        const char* node_name = gumbo_normalized_tagname(node->v.element.tag);
        return node_name && tag_name == node_name;
    };
    
    return parallel_find_all(root, tree, predicate, config);
}

std::vector<std::shared_ptr<Node>> SimpleParallelWalker::find_by_class_parallel(
    GumboNode* root,
    const Tree* tree,
    const std::string& class_name,
    const Config& config
) {
    auto predicate = [&class_name](GumboNode* node) -> bool {
        if (!node || node->type != GUMBO_NODE_ELEMENT) {
            return false;
        }
        
        // Look for class attribute
        const GumboVector* attributes = &node->v.element.attributes;
        for (unsigned int i = 0; i < attributes->length; ++i) {
            GumboAttribute* attr = static_cast<GumboAttribute*>(attributes->data[i]);
            if (attr && strcmp(attr->name, "class") == 0) {
                // Check if class_name is in the space-separated class list
                std::string class_attr = attr->value;
                
                // Simple but efficient class matching
                size_t pos = 0;
                while (pos < class_attr.length()) {
                    // Skip whitespace
                    pos = class_attr.find_first_not_of(" \t\r\n", pos);
                    if (pos == std::string::npos) break;
                    
                    // Find end of current class
                    size_t end = class_attr.find_first_of(" \t\r\n", pos);
                    if (end == std::string::npos) end = class_attr.length();
                    
                    // Check if this class matches
                    if (class_attr.substr(pos, end - pos) == class_name) {
                        return true;
                    }
                    
                    pos = end;
                }
                break;  // Found class attribute, no need to check others
            }
        }
        
        return false;
    };
    
    return parallel_find_all(root, tree, predicate, config);
}

std::shared_ptr<Node> SimpleParallelWalker::find_by_id_parallel(
    GumboNode* root,
    const Tree* tree,
    const std::string& id,
    const Config& config
) {
    auto predicate = [&id](GumboNode* node) -> bool {
        if (!node || node->type != GUMBO_NODE_ELEMENT) {
            return false;
        }
        
        // Look for id attribute
        const GumboVector* attributes = &node->v.element.attributes;
        for (unsigned int i = 0; i < attributes->length; ++i) {
            GumboAttribute* attr = static_cast<GumboAttribute*>(attributes->data[i]);
            if (attr && strcmp(attr->name, "id") == 0) {
                return id == attr->value;
            }
        }
        
        return false;
    };
    
    return parallel_find(root, tree, predicate, config);
}

} // namespace cutysoup
