#include "XmlElement.h"

#include "XmlAttribute.h"

XmlElement::XmlElement(const std::string& tagName)
    : mTagName(tagName),
      mChildren()
{

}

XmlElement::~XmlElement()
{

}

XmlElementPtr XmlElement::Create(const std::string& tagName)
{
    return std::make_unique<XmlElement>(tagName);
}

void XmlElement::setTagName(const std::string& tagName)
{
    mTagName = tagName;
}

void XmlElement::addChild(XmlElementPtr child)
{
    mChildren.push_back(std::move(child));
}

void XmlElement::addAttribute(XmlAttributePtr attribute)
{
    mAttributes[attribute->getName()] = std::move(attribute);
}

void XmlElement::addAttribute(const std::string& name, const std::string& value)
{
    auto attr = std::make_unique<XmlAttribute>(name);
    attr->setValue(value);
    addAttribute(std::move(attr));
}

const std::string& XmlElement::getTagName() const
{
    return mTagName;
}

const std::string& XmlElement::getText() const
{
    return mText;
}

void XmlElement::setText(const std::string& text)
{
    mText = text;
}

XmlElement* XmlElement::getFirstChildWithTagName(const std::string& tag)
{
    for(auto& child : mChildren)
    {
        if (child->getTagName() == tag)
        {
            return child.get();
        }
    }

    return nullptr;
}

bool XmlElement::hasAttribute(const std::string& attribute) const
{
    return (bool)(getAttribute(attribute));
}

XmlAttribute* XmlElement::getAttribute(const std::string& attributeName) const
{
    if (auto iter = mAttributes.find(attributeName); iter != mAttributes.end())
    {
        return iter->second.get();
    }
    return nullptr;
}

const std::unordered_map<std::string, XmlAttributePtr>& XmlElement::getAttributes() const
{
    return mAttributes;
}

const std::vector<std::unique_ptr<XmlElement> >& XmlElement::getChildren() const
{
    return mChildren;
}

std::string XmlElement::toString(unsigned depth, bool keepInline) const
{
    const auto prefix = std::string(2*depth, ' ');

    std::string line_ending = keepInline ? "" : "\n";

    auto content = prefix + "<" + getTagName();
    for (const auto& [key, attribute] : getAttributes())
    {
        content += " " + attribute->getName() + "=\"" + attribute->getValue() + "\"";
    }

    const auto num_children = mChildren.size();
    if (num_children == 0 && getText().empty())
    {
        content += "/>" + line_ending;
        return content;
    }
    else
    {
        content += ">";
    }

    if (!getText().empty())
    {
        content += getText();
    }

    if (num_children>0)
    {
        content += line_ending;
    }

    for(const auto& child : mChildren)
    {
        content += child->toString(depth+1, keepInline);
    }
    if (num_children>0)
    {
        content += prefix;
    }

    content += "</" + getTagName() + ">" + line_ending;
    return content;
}
