/*
 *   ORXONOX - the hottest 3D action shooter ever to exist
 *                    > www.orxonox.net <
 *
 *
 *   License notice:
 *
 *   This program is free software; you can redistribute it and/or
 *   modify it under the terms of the GNU General Public License
 *   as published by the Free Software Foundation; either version 2
 *   of the License, or (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 *
 *   Author:
 *      Fabian 'x3n' Landau
 *   Co-authors:
 *      ...
 *
 */

#include "Namespace.h"

#include <set>

#include "NamespaceNode.h"
#include "CoreIncludes.h"
#include "XMLPort.h"
#include "util/SubString.h"

namespace orxonox
{
    RegisterClass(Namespace);

    Namespace::Namespace(Context* context) : BaseObject(context), Context(context),
      bAutogeneratedFileRootNamespace_(false),
      bRoot_(false),
      operator_("or")
    {
        RegisterObject(Namespace);

        this->setNamespace(WeakPtr<Namespace>(this)); // store a weak-pointer to itself (a strong-pointer would create a recursive dependency)
    }

    Namespace::~Namespace()
    {
        if (this->bRoot_)
            for (NamespaceNode* node : this->representingNamespaces_)
                delete node;
    }

    void Namespace::XMLPort(Element& xmlelement, XMLPort::Mode mode)
    {
        SUPER(Namespace, XMLPort, xmlelement, mode);

        std::string name = this->getName();
        size_t pos = 0;
        while ((pos = name.find(',')) != std::string::npos)
            name.replace(pos, 1, " ");
        while ((pos = name.find(';')) != std::string::npos)
            name.replace(pos, 1, " ");
        while ((pos = name.find('\n')) != std::string::npos)
            name.replace(pos, 1, " ");
        while ((pos = name.find('\t')) != std::string::npos)
            name.replace(pos, 1, " ");
        SubString tokens(name, " ", "", false, '\\', true, '"', true, '\0', '\0', true, '\0');
        if (this->bRoot_)
        {
            this->representingNamespaces_.insert(new NamespaceNode(this->getName()));
        }
        else
        {
            for (unsigned int i = 0; i < tokens.size(); i++)
            {
                for (NamespaceNode* node : this->getNamespace()->representingNamespaces_)
                {
                    std::set<NamespaceNode*> temp = node->getNodeRelative(tokens[i]);
                    this->representingNamespaces_.insert(temp.begin(), temp.end());
                }
            }
        }

        XMLPortParam(Namespace, "operator", setOperator, getOperator, xmlelement, mode);
        XMLPortParam(Namespace, "bAutogenerated", setAutogenerated, isAutogenerated, xmlelement, mode);

        if (this->bAutogeneratedFileRootNamespace_)
        {
            for (NamespaceNode* node : this->representingNamespaces_)
            {
                node->setRoot(true);
                node->setHidden(true);
            }
        }

        XMLPortObjectExtended(Namespace, BaseObject, "", loadObjects, saveObjects, xmlelement, mode, true, false);
    }

    void Namespace::loadObjects(BaseObject* object)
    {
    }

    const BaseObject* Namespace::saveObjects(unsigned int index) const
    {
        return nullptr; // todo
    }

    bool Namespace::includes(const Namespace* ns) const
    {
        for (NamespaceNode* node1 : this->representingNamespaces_)
        {
            for (NamespaceNode* node2 : ns->representingNamespaces_)
            {
                if (node1->includes(node2))
                {
                    if (this->operator_ == "or")
                        return true;

                    if (this->operator_ == "not")
                        return false;
                }
                else
                {
                    if (this->operator_ == "and")
                        return false;
                }
            }
        }

        if (this->operator_ == "or")
            return false;
        else if (this->operator_ == "and")
            return true;
        else if (this->operator_ == "not")
            return true;

        return false;
    }

    std::string Namespace::toString() const
    {
        std::string output;

        int i = 0;
        for (NamespaceNode* node : this->representingNamespaces_)
        {
            if (i > 0)
                output += " / ";

            output += node->toString();
            i++;
        }

        return output;
    }

    std::string Namespace::toString(const std::string& indentation) const
    {
        std::string output;

        int i = 0;
        for (NamespaceNode* node : this->representingNamespaces_)
        {
            if (i > 0)
                output += '\n';

            output += node->toString(indentation);
            i++;
        }

        return output;
    }
}
