package de.ids_mannheim.korap.security.ac;

import de.ids_mannheim.korap.config.BeanConfiguration;
import de.ids_mannheim.korap.exceptions.KustvaktException;
import de.ids_mannheim.korap.interfaces.db.PolicyHandlerIface;
import de.ids_mannheim.korap.interfaces.db.ResourceOperationIface;
import de.ids_mannheim.korap.resources.KustvaktResource;
import de.ids_mannheim.korap.resources.Permissions;
import de.ids_mannheim.korap.resources.ResourceFactory;
import de.ids_mannheim.korap.security.PermissionsBuffer;
import de.ids_mannheim.korap.user.User;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;

/**
 * Created by hanl on 3/20/14.
 */
public class ResourceFinder {

    private static final Logger jlog = LoggerFactory
            .getLogger(ResourceFinder.class);
    private static PolicyHandlerIface policydao;

    private List<KustvaktResource.Container> containers;
    private User user;

    private ResourceFinder(User user) {
        this.containers = new ArrayList<>();
        this.user = user;
        checkProviders();
    }

    private static void checkProviders() {
        if (BeanConfiguration.hasContext() && policydao == null) {
            ResourceFinder.policydao = BeanConfiguration.getBeans()
                    .getPolicyDbProvider();
        }
        if (policydao == null)
            throw new RuntimeException("provider not set!");
    }

    @Deprecated
    public static void setProviders(PolicyHandlerIface policyHandler) {
        ResourceFinder.policydao = policyHandler;
    }

    public static <T extends KustvaktResource> Set<T> search(String path,
            boolean asParent, User user, Class<T> clazz,
            Permissions.PERMISSIONS... perms) throws KustvaktException {
        ResourceFinder cat = init(path, asParent, user, clazz, perms);
        return cat.getResources();
    }

    private static <T extends KustvaktResource> ResourceFinder init(String path,
            boolean asParent, User user, Class<T> clazz,
            Permissions.PERMISSIONS... perms) throws KustvaktException {
        ResourceFinder cat = new ResourceFinder(user);
        PermissionsBuffer buffer = new PermissionsBuffer();
        if (perms.length == 0)
            buffer.addPermissions(Permissions.PERMISSIONS.READ);
        buffer.addPermissions(perms);
        cat.retrievePolicies(path, buffer.getPbyte(), clazz, asParent);
        return cat;
    }

    //todo: needs to be much faster!
    public static <T extends KustvaktResource> ResourceFinder init(User user,
            Class<T> clazz) throws KustvaktException {
        return init(null, true, user, clazz, Permissions.PERMISSIONS.READ);
    }

    public static <T extends KustvaktResource> Set<T> search(String name,
            boolean asParent, User user, String type) throws KustvaktException {
        return (Set<T>) search(name, asParent, user,
                ResourceFactory.getResourceClass(type),
                Permissions.PERMISSIONS.READ);
    }

    // todo: should this be working?
    public static <T extends KustvaktResource> Set<T> search(User user,
            Class<T> clazz) throws KustvaktException {
        return search(null, true, user, clazz, Permissions.PERMISSIONS.READ);
    }

    private void retrievePolicies(String path, Byte b, Class type,
            boolean parent) throws KustvaktException {
        //fixme: throw exception to avoid susequent exceptions due to unknown origin
        if (user == null | type == null)
            return;
        if (parent)
            this.containers = policydao.getDescending(path, user, b, type);
        else
            this.containers = policydao.getAscending(path, user, b, type);
    }

    public <T extends KustvaktResource> Set<T> getResources() {
        return evaluateResources();
    }

    // todo: redo with less memory usage/faster
    private <T extends KustvaktResource> Set<T> evaluateResources() {
        Set<T> resources = new HashSet<>();
        if (this.containers != null) {
            for (KustvaktResource.Container c : this.containers) {
                ResourceOperationIface<T> iface = SecurityManager.getHandlers()
                        .get(c.getType());
                if (iface == null)
                    iface = SecurityManager.getHandlers()
                            .get(KustvaktResource.class);

                try {
                    T resource = (T) iface
                            .findbyId(c.getPersistentID(), this.user);
                    if (resource != null) {
                        PolicyEvaluator e = PolicyEvaluator
                                .setFlags(user, resource);
                        resource.setManaged(e.getFlag("managed", false));
                        resources.add(resource);
                    }
                }catch (KustvaktException e) {
                    // don't handle connection error or no handler registered!
                    jlog.error("Error while retrieving containers '{}' ",
                            this.containers);
                    return Collections.emptySet();
                }
            }
        }
        return resources;
    }

    public Set<String> getIds() {
        Set<String> resources = new HashSet<>();
        for (KustvaktResource.Container c : this.containers)
            resources.add(c.getPersistentID());
        return resources;
    }

}
