#include "xmpmeta/xml/search.h"

#include <stack>
#include <string>

#include "android-base/logging.h"
#include "xmpmeta/xml/utils.h"

using ::dynamic_depth::xmpmeta::xml::FromXmlChar;

namespace dynamic_depth {
namespace xmpmeta {
namespace xml {

xmlNodePtr DepthFirstSearch(const xmlDocPtr parent, const char* name) {
  return DepthFirstSearch(parent, "", name);
}

xmlNodePtr DepthFirstSearch(const xmlDocPtr parent, const char* prefix,
                            const char* name) {
  if (parent == nullptr || parent->children == nullptr) {
    LOG(ERROR) << "XML doc was null or has no XML nodes";
    return nullptr;
  }
  xmlNodePtr result;
  for (xmlNodePtr node = parent->children; node != nullptr; node = node->next) {
    result = DepthFirstSearch(node, prefix, name);
    if (result != nullptr) {
      return result;
    }
  }
  LOG(WARNING) << "No node matching " << prefix << ":" << name << " was found";
  return nullptr;
}

xmlNodePtr DepthFirstSearch(const xmlNodePtr parent, const char* name) {
  return DepthFirstSearch(parent, "", name);
}

xmlNodePtr DepthFirstSearch(const xmlNodePtr parent, const char* prefix,
                            const char* name) {
  if (parent == nullptr) {
    LOG(ERROR) << "XML node was null";
    return nullptr;
  }
  std::stack<xmlNodePtr> node_stack;
  node_stack.push(parent);
  while (!node_stack.empty()) {
    const xmlNodePtr current_node = node_stack.top();
    node_stack.pop();
    if (strcmp(FromXmlChar(current_node->name), name) == 0) {
      if (!prefix || strlen(prefix) == 0) {
        return current_node;
      }
      if (current_node->ns && current_node->ns->prefix &&
          strcmp(FromXmlChar(current_node->ns->prefix), prefix) == 0) {
        return current_node;
      }
    }
    std::stack<xmlNodePtr> stack_to_reverse;
    for (xmlNodePtr child = current_node->children; child != nullptr;
         child = child->next) {
      stack_to_reverse.push(child);
    }
    while (!stack_to_reverse.empty()) {
      node_stack.push(stack_to_reverse.top());
      stack_to_reverse.pop();
    }
  }
  return nullptr;
}

}  // namespace xml
}  // namespace xmpmeta
}  // namespace dynamic_depth