#include "security/DefaultPermissionChecker.h"
#include "security/PermissionCache.h"
#include "context/UncheckedCallerController.h"
#include "context/ContextUtils.h"
#include "context/Context.h"
#include "util/Log.h"
#include "AggreGateException.h"
#include "SecurityException.h"
#include "IllegalStateException.h"
#include "Cres.h"

DefaultPermissionChecker::DefaultPermissionChecker()
{
    unchecked = UncheckedCallerControllerPtr(new UncheckedCallerController());

    PermissionTypePtr nullType = PermissionTypePtr(new PermissionType(0, NULL_PERMISSIONS_(), Cres::get()->getString("secNoPerms")));
    permissionTypes.push_back( nullType );
}

const AgString DefaultPermissionChecker::NULL_PERMISSIONS()
{
    return NULL_PERMISSIONS_();
}

void DefaultPermissionChecker::setPermissionTypes(std::vector<PermissionTypePtr> perms)
{
    this->permissionTypes = perms;
}

PermissionsPtr DefaultPermissionChecker::getNullPermissions()
{
    PermissionsPtr newPermiss = PermissionsPtr(new Permissions());
    return newPermiss;
}

bool DefaultPermissionChecker::has(
        CallerControllerPtr caller,
        PermissionsPtr requiredPermissions,
        Context* accessedContext
        ) {
    try
    {
        if (caller == NULL) {
            return requiredPermissions->size() == 0;
        }

        if (!caller->isPermissionCheckingEnabled()) {
            return true;
        }

        PermissionsPtr existingPermissions = caller->getPermissions();

        if (existingPermissions == NULL) {
            if(Log::AG.isDebugEnabled())
            {
                LOG_AG_DEBUG("Permission level of '" + caller->toString().toUtf8() + "' is 'null' and allow nothing, need " + requiredPermissions->toString().toUtf8());
            }
            return false;
        }

        if (requiredPermissions == NULL || requiredPermissions->size() == 0) {
            return true;
        }

        for (std::list<PermissionPtr>::iterator required = requiredPermissions->iteratorBegin();
             required!=requiredPermissions->iteratorEnd(); ++required) {
            AgString accessedPath = !(*required)->getContext().empty() ? (*required)->getContext()
                                                                       : (accessedContext != NULL ? accessedContext->getPath() : "");

            if (!accessedPath.empty()) {
                PermissionCachePtr cache = caller->getPermissionCache();

                AgString cachedLevel = cache != NULL ? cache->getLevel(accessedPath) : "";

                AgString effectiveLevel;
                if (!cachedLevel.empty()) {
                    effectiveLevel = cachedLevel;
                }else {
                    effectiveLevel = getLevel(existingPermissions, accessedPath, accessedContext != NULL ? accessedContext->getContextManager() : NULL);
                    if (cache != NULL && accessedContext != NULL && (accessedPath==accessedContext->getPath())) // Caching effective permissions for real contexts only
                    {
                        cache->cacheLevel(accessedPath, effectiveLevel);
                    }
                }

                if (!hasNecessaryLevel(effectiveLevel, (*required)->getLevel())) {
                    if(Log::AG.isDebugEnabled())
                    {
                        LOG_AG_DEBUG("Permissions '" + existingPermissions->toString().toUtf8() + "' doesn't allow '"
                                     + requiredPermissions->toString().toUtf8() + "' (because effective level '" + effectiveLevel.toUtf8() + "' in '" + accessedPath.toUtf8()
                                     + "' doesn't allow '" + (*required)->toString().toUtf8() + "')");
                    }

                    return false;
                }
            }else {
                if (existingPermissions->size() != 1) {
                    throw IllegalStateException("Required permissions doesn't include context specification, so existing permissions should include exactly one element");
                }

                //TODO:
                /*PermissionPtr existing;// = existingPermissions.iterator().next();

                if (!hasNecessaryLevel(existing->getLevel(), (*required)->getLevel())) {
                    AGDEBUG(DBG_ERROR, "DefaultPermissionChecker::has", "Permissions '" + existingPermissions->toString() + "' doesn't allow '"
                            + requiredPermissions->toString() + "' (because '" + existing->toString() + "' doesn't allow '" + (*required)->toString() + "')");
                    return false;
                }*/
            }
        }

        return true;
    }
    catch (AggreGateException ex)
    {
        LOG_AG_ERROR("Error checking permissions:" + ex.getMessage().toUtf8());
        return false;
    }
}

AgString DefaultPermissionChecker::getLevel(
        PermissionsPtr permissions,
        const AgString& context,
        ContextManager* cm
        ) {
    try
    {
        AgString level;

        if (permissions == NULL) {
            return NULL_PERMISSIONS_();
        }

        for (std::list<PermissionPtr>::iterator permission = permissions->iteratorBegin(); permission!=permissions->iteratorEnd(); ++permission) {
            if ((*permission)->getContext().empty()) {
                return (*permission)->getLevel().empty() ? (*permission)->getLevel() : NULL_PERMISSIONS();
            }

            std::list<AgString> allowedPaths = getAllowedPaths(*permission, cm);
            for (std::list<AgString>::iterator allowedPath=allowedPaths.begin(); allowedPath!=allowedPaths.end(); ++allowedPath) {
                if (ContextUtils::matchesToMask(*allowedPath, context, true, false)) {
                    return !(*permission)->getLevel().empty() ? (*permission)->getLevel() : NULL_PERMISSIONS();
                }
            }
        }


        return NULL_PERMISSIONS_();
    }
    catch (AggreGateException ex)
    {
        throw SecurityException("Error getting permission type of '" + permissions->toString() + "' in '" + context + "': ", ex.getMessage());
    }
}

bool DefaultPermissionChecker::canSee(PermissionsPtr permissions, const AgString& context, ContextManager* cm)
{
    try {
        if (permissions == NULL) {
            return false;
        }

        for (std::list<PermissionPtr>::iterator permission = permissions->iteratorBegin(); permission!=permissions->iteratorEnd(); ++permission) {
            if ((*permission)->getLevel() == NULL_PERMISSIONS()) {
                continue;
            }

            if ((*permission)->getContext().empty()) {
                return true;
            }

            std::list<AgString> allowedPaths = getAllowedPaths(*permission, cm);

            for (std::list<AgString>::iterator allowedPath=allowedPaths.begin(); allowedPath!=allowedPaths.end(); ++allowedPath) {
                if (ContextUtils::matchesToMask(*allowedPath, context, false, false)) {
                    return false;
                }

                if (ContextUtils::matchesToMask(*allowedPath, context, false, true)) {
                    return true;
                }
            }
        }

        return false;
    }catch (AggreGateException ex) {
        LOG_AG_DEBUG("Error checking permissions: " + ex.getMessage().toUtf8());
        return false;
    }
}

AgString DefaultPermissionChecker::canActivate(
        PermissionsPtr existingPermissions,
        PermissionsPtr requiredPermissions,
        ContextManager* cm
        ) {
    for (std::list<PermissionPtr>::iterator requiredPermission = requiredPermissions->iteratorBegin();
         requiredPermission!=requiredPermissions->iteratorEnd(); ++requiredPermission) {
        for (std::list<PermissionPtr>::iterator existingPermission = existingPermissions->iteratorBegin();
             existingPermission!=existingPermissions->iteratorEnd(); ++existingPermission) {
            if (contextMatches(*existingPermission, *requiredPermission, NULL)) {
                if (!hasNecessaryLevel((*existingPermission)->getLevel(), (*requiredPermission)->getLevel())) {
                    if (!hasNecessaryLevel(getLevel(existingPermissions, (*requiredPermission)->getContext(), cm),
                                           (*requiredPermission)->getLevel())) {
                        return "Cannot set permissions for '" + (*requiredPermission)->getContext() + "' to '"
                                + (*requiredPermission)->getLevel() + "' because your own permission level for '"
                                + (*existingPermission)->getContext() + "' is '" + (*existingPermission)->getLevel() + "'";
                    }
                }
            }
        }
    }

    return "";
}

bool DefaultPermissionChecker::isValid(const AgString& permissionLevel)
{
    try {
        for (unsigned int i = 0; i < permissionTypes.size(); i++) {
            if (permissionTypes[i]->getName() == permissionLevel)
            {
                return true;
            }
        }

        return false;
    }catch (AggreGateException ex) {
        return false;
    }
}

std::map<AgString, AgString> DefaultPermissionChecker::getPermissionLevels()
{
    std::map<AgString, AgString> pm;

    for (unsigned int i = 0; i < permissionTypes.size(); i++) {
        pm.insert( std::pair<AgString, AgString>(permissionTypes[i]->getName(), permissionTypes[i]->getDescription()) );
    }

    return pm;
}


bool DefaultPermissionChecker::hasNecessaryLevel(const AgString& existingLevel, const AgString& requiredLevel)
{
    int existingLevelPattern = findPattern(existingLevel);
    int requiredLevelPattern = findPattern(requiredLevel);

    if ((requiredLevelPattern & existingLevelPattern) != requiredLevelPattern) {
        return false;
    }

    return true;
}

int DefaultPermissionChecker::findPattern(const AgString &level) /* throws(SecurityException) */
{
    for (unsigned int i = 0; i < permissionTypes.size(); i++) {
        if (permissionTypes[i]->getName() == level) {
            return permissionTypes[i]->getPattern();
        }
    }

    throw SecurityException("Permission level '" + level + "' not found");
}

bool DefaultPermissionChecker::contextMatches(
        PermissionPtr existingPermission,
        PermissionPtr requiredPermission,
        Context* accessedContext
        ){
    if (existingPermission->getContext().empty()) {
        return true;
    }

    AgString accessedPath = (!requiredPermission->getContext().empty()) ? requiredPermission->getContext()
                                                                        : (accessedContext != NULL ? accessedContext->getPath() : "");

    if (accessedPath.empty()) {
        return false;
    }

    std::list<AgString> allowedPaths = getAllowedPaths(existingPermission, accessedContext != NULL ? accessedContext->getContextManager() : NULL);

    for (std::list<AgString>::iterator allowedPath = allowedPaths.begin(); allowedPath!=allowedPaths.end(); ++allowedPath) {
        if (ContextUtils::matchesToMask(*allowedPath, accessedPath, true, false)) {
            return true;
        }
    }

    return false;
}

std::list<AgString> DefaultPermissionChecker::getAllowedPaths(PermissionPtr permission, ContextManager* cm)
{
    if (cm != NULL && permission->getContext().endsWith(ContextUtils::CONTEXT_NAME_SEPARATOR() + ContextUtils::CONTEXT_GROUP_MASK())) {
        AgString truncated = permission->getContext().substr(0, permission->getContext().length() - 2);

        Context* groupContext = cm->get(truncated, unchecked);

        if (groupContext != NULL && groupContext->isMapped())
        {
            std::list<AgString> allowedPaths;
            std::list<Context*>  cgl = groupContext->getMappedChildren(unchecked);
            for (std::list<Context*>::iterator mappedChild = cgl.begin(); mappedChild!=cgl.end(); ++mappedChild) {
                allowedPaths.push_front((*mappedChild)->getPath());
            }
            return allowedPaths;
        }
    }

    std::list<AgString> paths;
    paths.push_back( permission->getContext() );

    return paths;
}
