package ru.yandex.webmaster3.storage.admin.security;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author avhaliullin
 */
public class PermissionsSet extends AbstractSet<Permission> {
    private final EnumMap<AccessActionEnum, EnumMap<AccessObjectEnum, Permission>> permissions = new EnumMap<>(AccessActionEnum.class);
    private final int size;

    public PermissionsSet(Iterable<Permission> perms) {
        int sizeAcc = 0;
        for (Permission perm : perms) {
            EnumMap<AccessObjectEnum, Permission> objects = permissions.get(perm.getAction());

            if (objects == null) {
                objects = new EnumMap<>(AccessObjectEnum.class);
                permissions.put(perm.getAction(), objects);
            }
            if (objects.put(perm.getAccessObject(), perm) == null) {
                sizeAcc++;
            }
        }
        size = sizeAcc;
    }

    public PermissionsSet with(PermissionsSet other) {
        List<Permission> all = new ArrayList<>(this);
        all.addAll(other);
        return new PermissionsSet(all);
    }

    @Override
    public boolean contains(Object o) {
        if (!(o instanceof Permission)) {
            return false;
        }
        Permission permission = (Permission) o;
        EnumMap<AccessObjectEnum, Permission> objects = permissions.get(permission.getAction());
        return objects != null && objects.containsKey(permission.getAccessObject());
    }

    @Override
    public boolean isEmpty() {
        return permissions.isEmpty();
    }

    @Override
    public Iterator<Permission> iterator() {
        return permissions.keySet().stream().flatMap(action ->
                        permissions.get(action).values().stream()
        ).iterator();
    }

    @Override
    public int size() {
        return size;
    }

    public static PermissionsSet allActionsFor(AccessObjectEnum... accessObjects) {
        List<Permission> permissions = new ArrayList<>(AccessActionEnum.values().length * accessObjects.length);
        for (AccessObjectEnum accessObject : accessObjects) {
            for (AccessActionEnum action : AccessActionEnum.values()) {
                permissions.add(new Permission(action, accessObject));
            }
        }
        return new PermissionsSet(permissions);
    }

    public static PermissionsSet everythingExcept(AccessObjectEnum exclude) {
        return new PermissionsSet(Arrays.stream(AccessObjectEnum.values())
                .filter(p -> exclude != p)
                .flatMap(object -> Arrays.stream(AccessActionEnum.values()).map(
                        action -> new Permission(action, object)
                ))
                .collect(Collectors.toList())
        );
    }

    public static PermissionsSet readFor(AccessObjectEnum... accessObjectEnum) {
        List<Permission> permissions = new ArrayList<>(accessObjectEnum.length);
        for (AccessObjectEnum object : accessObjectEnum) {
            permissions.add(new Permission(AccessActionEnum.READ, object));
        }
        return new PermissionsSet(permissions);
    }
}
