#include "Index.h"
#include <sstream>
#include <format>

std::set<Identifier> LeafNode::evaluate(const Index& index) const {
    return index.search(term_);
}

std::set<Identifier> AndOperationNode::evaluate(const Index& index) const {
    auto left_set = left_->evaluate(index);
    auto right_set = right_->evaluate(index);
    std::set<Identifier> result_set;
    for (const auto& id : left_set) {
        if (right_set.contains(id)) {
            result_set.insert(id);
        }
    }
    return result_set;
}

std::set<Identifier> OrOperationNode::evaluate(const Index& index) const {
    auto left_set = left_->evaluate(index);
    auto right_set = right_->evaluate(index);
    left_set.merge(right_set);
    return left_set;
}

std::set<Identifier> NotOperationNode::evaluate(const Index& index) const {
    auto child_set = child_->evaluate(index);
    std::set<Identifier> result_set;
    for (Identifier id = 0; id < index.next_identifier_; ++id) {
        if (!child_set.contains(id)) {
            result_set.insert(id);
        }
    }
    return result_set;
}

void Query::check_for_missing_operands(OperationType operation_type) {
    if (
        (operation_type == OperationType::BINARY && parsing_stack_.size() < 2)
        || (operation_type == OperationType::UNARY && parsing_stack_.size() < 1)
    ) {
        throw std::string(MISSING_OPERANDS_EXCEPTION);
    }
}

void Query::process_term(std::string term) {
    LeafNode node{std::move(term)};
    std::unique_ptr<Node> node_ptr = std::make_unique<LeafNode>(std::move(node));
    parsing_stack_.push(std::move(node_ptr));
}

void Query::process_operation(const std::string& operation_token) {
    std::string_view token_view(operation_token);
    auto operation_name = token_view.substr(1);

    if (operation_name == NOT_OPERATION_NAME) {
        process_unary_operation<NotOperationNode>();
    } else if (operation_name == OR_OPERATION_NAME) {
        process_binary_operation<OrOperationNode>();
    } else if (operation_name == AND_OPERATION_NAME) {
        process_binary_operation<AndOperationNode>();
    } else {
        throw std::format(UNKNOWN_OPERATION_EXCEPTION, operation_token);
    }
}

Query::Query(const std::string& condition) {
    std::string token;
    std::istringstream condition_stream(condition);
    while (std::getline(condition_stream, token, WORD_SEPARATOR)) {
        if (token.empty()) continue;
        if (token[0] == SPECIAL_CHAR) { // operator
            process_operation(token);
        } else { // term
            process_term(std::move(token));
        }
    }
    if (parsing_stack_.empty()) {
        throw std::string(EMPTY_CONDITION_EXCEPTION);
    } else if (parsing_stack_.size() > 1) {
        throw std::string(INVALID_CONDITION_EXCEPTION);
    }

    root_ = std::move(parsing_stack_.top());
    parsing_stack_.pop();
}

std::set<Identifier> Query::evaluate(const Index& index) const {
    return root_->evaluate(index);
}