package com.plexobject.rbac.repository.bdb;

import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import com.plexobject.rbac.repository.PermissionRepository;
import com.plexobject.rbac.repository.PersistenceException;
import com.plexobject.rbac.repository.RoleRepository;
import com.plexobject.rbac.domain.Permission;
import com.plexobject.rbac.domain.Role;
import com.plexobject.rbac.metric.Metric;
import com.plexobject.rbac.metric.Timing;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.persist.EntityCursor;
import com.sleepycat.persist.EntityStore;
import com.sleepycat.persist.SecondaryIndex;

public class PermissionRepositoryImpl extends
        BaseRepositoryImpl<Permission, Integer> implements PermissionRepository {
    private RoleRepository roleRepository;
    private SecondaryIndex<String, Integer, Permission> rolenameIndex;

    public PermissionRepositoryImpl(final EntityStore entityStore) {
        super(entityStore);
        this.roleRepository = new RoleRepositoryImpl(entityStore);
        try {
            rolenameIndex = entityStore.getSecondaryIndex(primaryIndex,
                    String.class, "roleIds");
        } catch (DatabaseException e) {
            throw new PersistenceException(e);
        }
    }

    @Override
    public Set<Permission> getPermissionsForRoles(Collection<Role> roles) {
        final Timing timer = Metric.newTiming(getClass().getName()
                + ".getPermissionsForRole");
        Set<Permission> permissions = new HashSet<Permission>();
        try {
            for (Role role : roles) {
                loadPermissionsForRole(role, permissions);
            }
            return permissions;
        } catch (DatabaseException e) {
            throw new PersistenceException(e);
        } finally {
            timer.stop();
        }
    }

    private void loadPermissionsForRole(Role role,
            Collection<Permission> permissions) throws DatabaseException {
        EntityCursor<Permission> cursor = null;

        try {
            cursor = rolenameIndex.subIndex(role.getId()).entities();
            Iterator<Permission> it = cursor.iterator();
            while (it.hasNext()) {
                Permission next = it.next();
                permissions.add(next);
            }
            if (role.hasParentIds()) {
                for (String parentId : role.getParentIds()) {
                    Role parent = roleRepository.findById(parentId);
                    if (!role.equals(parent)) {
                        loadPermissionsForRole(parent, permissions);
                    }
                }
            }
        } finally {
            if (cursor != null) {
                try {
                    cursor.close();
                } catch (DatabaseException e) {
                    LOGGER.error("failed to close cursor", e);
                }
            }
        }
    }

}
