#include "context/ContextUtils.h"
#include "context/Contexts.h"
#include "context/Context.h"
#include "context/ContextManager.h"
#include <boost/regex.hpp>
#include "util/StringUtils.h"

AgString ContextUtils::createName(std::vector<AgString> &parts)
{
    AgString result;

    for (unsigned int i=0; i < parts.size(); i++)
    {
        if(i > 0) {
            result += CONTEXT_NAME_SEPARATOR();
        }
        result += parts[i];
    }

    return result;
}

bool ContextUtils::isRelative(const AgString & name)
{
   return name.startsWith(CONTEXT_NAME_SEPARATOR());
}

bool ContextUtils::isValidContextName(const AgString & s)
{
    static const boost::regex rregex(CONTEXT_NAME_PATTERN().toUtf8());
    return boost::regex_match(s.toUtf8(), rregex);
}

bool ContextUtils::invalidChar(char c)
{
    return !((c >= 65 && c < 80 ) || (c >= 97 && c < 122));
}

AgString ContextUtils::getTypeForClass(const std::string& className)
{
    std::string res = className;

    res.erase(remove_if(res.begin(), res.end(), invalidChar), res.end());
    return std::string(res);
}

bool ContextUtils::isValidContextType(const AgString & s)
{
    static const boost::regex rregex(CONTEXT_TYPE_PATTERN().toUtf8());
    return CONTEXT_TYPE_ANY() == s ||  boost::regex_match(s.toUtf8(), rregex);
}

AgString ContextUtils::getBaseGroup(const AgString & group)
{
    if (group.empty())
    {
        return "";
    }

    size_t index = group.find(ENTITY_GROUP_SEPARATOR()[0]);
    return index == utf16string::npos ? group : (AgString)group.substr(0, index);
}

std::list<AgString> ContextUtils::expandMaskToPaths(const AgString & mask, ContextManager* contextManager)
{

    return expandMaskToPaths(mask, contextManager, CallerControllerPtr(), false);
}

std::list<AgString> ContextUtils::expandMaskToPaths(const AgString & mask, ContextManager* contextManager, CallerControllerPtr caller)
{

    return expandMaskToPaths(mask, contextManager, caller, false);
}

std::list<AgString>  ContextUtils::expandMaskToPaths(const AgString & mask, ContextManager* contextManager, CallerControllerPtr caller, bool useVisibleChildren)
{
    std::list<AgString>  result;
    std::vector<AgString> parts = StringUtils::split(mask, CONTEXT_NAME_SEPARATOR()[0]);
    for (unsigned int i = 0; i < parts.size(); i++) {
        if (parts[i] == CONTEXT_GROUP_MASK()) {
            AgString head;
            for (unsigned int j = 0; j < i; j++) {
                if(j > 0) {
                    head.append(CONTEXT_NAME_SEPARATOR());
                }
                head.append(parts[j]);
            }

            AgString tail;
            for (unsigned int j = i + 1; j < parts.size(); j++) {
                tail.append(CONTEXT_NAME_SEPARATOR());
                tail.append(parts[j]);
            }
            std::list<AgString> res = expandMaskPart(head, tail, contextManager, caller, useVisibleChildren);
            result.merge(res);
            return result;
        }
    }

    if (contextManager->get(mask, caller) != 0)
    {
        result.push_back(mask);
    }

    return result;
}

std::list<Context*>  ContextUtils::expandMaskToContexts(const AgString & mask, ContextManager* contextManager)
{

    return expandMaskToContexts(mask, contextManager, CallerControllerPtr(), false);
}

std::list<Context*>  ContextUtils::expandMaskToContexts(const AgString & mask, ContextManager* contextManager, CallerControllerPtr caller)
{

    return expandMaskToContexts(mask, contextManager, caller, false);
}

std::list<Context*>  ContextUtils::expandMaskToContexts(const AgString & mask, ContextManager* contextManager,
                                                          CallerControllerPtr caller, bool useVisibleChildren)
{
    std::list<Context*> res;

    std::list<AgString> paths = expandMaskToPaths(mask, contextManager, caller, useVisibleChildren);

    for (std::list<AgString>::iterator path = paths.begin(); path != paths.end(); ++path)
    {
        Context* con = contextManager->get(*path, caller);
        if (con != NULL)
        {
            res.push_back(con);
        }
    }

    return res;
}
bool ContextUtils::matchesToMask(const AgString & mask, const AgString & name)
{
    return matchesToMask(mask, name, false, false);
}

bool ContextUtils::matchesToMask(const AgString & mask, const AgString & context, bool contextMayExtendMask, bool maskMayExtendContext)
{
    if (!isMask(mask)) {
        if (contextMayExtendMask && maskMayExtendContext) {
            int length = std::max(mask.length(), context.length());
            return (mask.substr (0, length) == (context.substr (0, length)));
        }else {
            bool equals = (mask==context);

            if (maskMayExtendContext) {
                return (equals || ((mask.length() > context.length()) && (mask.find_first_of(context) == 0)
                                  && (mask.at(context.length()) == CONTEXT_NAME_SEPARATOR()[0])));
            }else if (contextMayExtendMask) {
                return (equals || ((context.length() > mask.length()) && (context.find_first_of(mask) == 0)
                                  && context.at(mask.length()) == CONTEXT_NAME_SEPARATOR()[0]));
            }else{
                return equals;
            }
        }
    }

    std::vector<AgString> maskParts = StringUtils::split(mask, CONTEXT_NAME_SEPARATOR()[0]);
    std::vector<AgString> nameParts = StringUtils::split(context, CONTEXT_NAME_SEPARATOR()[0]);

    if ((maskParts.size() > nameParts.size()) && !maskMayExtendContext) {
        return false;
    }

    if ((maskParts.size() < nameParts.size()) && !contextMayExtendMask) {
        return false;
    }

    for (unsigned int i = 0; i < std::min(maskParts.size(), nameParts.size()); i++){
        if ((maskParts[i] ==CONTEXT_GROUP_MASK()) && (nameParts[i] != CONTEXT_GROUP_MASK())) {
            continue;
        }else {
            if (maskParts[i] != nameParts[i]) {
                return false;
            }
        }
    }

    return true;
}

AgString ContextUtils::devicesContextPath(AgString owner)
{
    std::vector<AgString> parts;
    parts.push_back(userContextPath(owner));
    parts.push_back(Contexts::CTX_DEVICES);
    return createName(parts);
}

bool ContextUtils::isMask(const AgString & name)
{
    return name.find(CONTEXT_GROUP_MASK()[0]) != AgString::npos;
}

AgString ContextUtils::pluginIdToContextName(const AgString & pluginId)
{
    AgString res = pluginId;
    res.replaceStr(".","_");
    res.replaceStr("-", "");
    return res;
}

AgString ContextUtils::pluginUserConfigContextPath(const AgString & username, const AgString & pluginId)
{
    std::vector<AgString> param;
    param.push_back( userContextPath(username) );
    param.push_back( Contexts::CTX_PLUGINS_CONFIG );
    param.push_back( pluginIdToContextName(pluginId) );

    return createName(param);
}

AgString ContextUtils::pluginGlobalConfigContextPath(const AgString & pluginId)
{
    std::vector<AgString> param;
    param.push_back( Contexts::CTX_PLUGINS_CONFIG );
    param.push_back( pluginIdToContextName(pluginId) );

    return createName(param);
}


AgString ContextUtils::userContextPath(const AgString & username)
{
    std::vector<AgString> param;
    param.push_back( Contexts::CTX_USERS );
    param.push_back( username );

    return createName( param );
}

std::list<AgString>  ContextUtils::expandMaskPart(const AgString & head, const AgString & tail, ContextManager* contextManager, CallerControllerPtr caller, bool useVisibleChildren)
{
    std::list<AgString>  result;
    Context* con = contextManager->get(head, caller);
    if (con == NULL)
    {
        return result;
    }

    if (con->isMapped())
    {
        std::list<Context*> mappedChildren = con->getMappedChildren(caller);
        for (std::list<Context*>::iterator it = mappedChildren.begin(); it != mappedChildren.end(); ++it)
        {
            result.push_back((*it)->getPath());
        }
    }
    else {
        std::list<Context*> children = useVisibleChildren ? con->getVisibleChildren(caller) : con->getChildren(caller);
        for (std::list<Context*>::iterator it = children.begin(); it != children.end(); ++it)
        {
            if(useVisibleChildren)
            {
                Context* realChild = con->getChild((*it)->getName());
                if (realChild == 0 || (realChild->getPath() != (*it)->getPath()))
                {
                    std::list<AgString> res = expandMaskToPaths(AgString((*it)->getPath()) + tail,
                                                                   contextManager, caller, useVisibleChildren);
                    result.merge(res);
                    continue;
                }
            }

            std::list<AgString> res2 = expandMaskToPaths(AgString(head) + CONTEXT_NAME_SEPARATOR() + (*it)->getName() + tail,
                                                        contextManager, caller, useVisibleChildren);
            result.merge( res2 );
        }
    }

    return result;
}
