package org.slc.sli.api.security.context.resolver;

import org.slc.sli.api.config.EntityNames;
import org.slc.sli.api.config.ResourceNames;
import org.slc.sli.api.security.context.AssociativeContextHelper;
import org.slc.sli.domain.Entity;
import org.slc.sli.domain.EntityRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * TeacherEdOrgContextResolver
 * Determine security authorization.
 * Finds the education organization that an educator user has context to access.
 */
@Component
public class TeacherEdOrgContextResolver implements EntityContextResolver {

    @Autowired
    private EntityRepository repository;
    @Autowired
    private AssociativeContextHelper helper;

    @Override
    public List<String> findAccessible(Entity principal) {

        // find schools the teacher can reach
        List<String> schoolIds = helper.findAccessible(principal, Arrays.asList(ResourceNames.TEACHER_SCHOOL_ASSOCIATIONS));

        // find edOrgs directly referenced by schools
        Set<String> edOrgIds = new HashSet<String>();
        for (String schoolId : schoolIds) {
            Entity school = this.repository.find(EntityNames.SCHOOL, schoolId);
            edOrgIds.add(school.getBody().get("parentEducationAgencyReference").toString());
        }

        // from those edOrgs, build the closure under the "parent" relationship 
        Set<String> retVal = new HashSet<String>();
        retVal.addAll(edOrgIds);
        while (true) { //TODO this could result in an infinite loop if circular references exist
            // in this loop, "edOrgIds" contains ids added in the previous round
            Set<String> toAdd = new HashSet<String>(); // this contains ids to be added in this round
            for (String edOrgId : edOrgIds) {
                Entity edOrg = this.repository.find(EntityNames.EDUCATION_ORGANIZATION, edOrgId);
                Object newId = edOrg.getBody().get("parentEducationAgencyReference");
                if (newId != null && !retVal.contains(newId.toString())) {
                    toAdd.add(newId.toString());
                }
            }
            if (toAdd.isEmpty()) {
                break; // no new ids to add; closure is reached.  
            }
            retVal.addAll(toAdd);
            edOrgIds = toAdd;
        }

        return new ArrayList<String>(retVal);
    }

    public void setRepository(EntityRepository repository) {
        this.repository = repository;
    }

    @Override
    public boolean canResolve(String fromEntityType, String toEntityType) {
        return EntityNames.TEACHER.equals(fromEntityType) && EntityNames.EDUCATION_ORGANIZATION.equals(toEntityType);
    }
}

@@ -950,17 +950,16 @@
 ipal) %7B%0A
-%0A
         
@@ -1176,34 +1176,35 @@
 schools%0A        
-Se
+Lis
 t%3CString%3E edOrgI
@@ -1199,17 +1199,17 @@
 ring%3E ed
-O
+o
 rgIds = 
@@ -1208,30 +1208,32 @@
 rgIds = new 
-HashSe
+ArrayLis
 t%3CString%3E();
@@ -1367,25 +1367,25 @@
           ed
-O
+o
 rgIds.add(sc
@@ -1473,445 +1473,147 @@
     
-// from those edOrgs, build the closure under the %22parent%22 relationship %0A        Set%3CString%3E retVal = new HashSet%3CString%3E();%0A        retVal.addAll(edOrgIds);%0A        while (true) %7B //TODO this could result in an infinite loop if circular references exist%0A            // in this loop, %22edOrgIds%22 contains ids added in the previous round%0A            Set%3CString%3E toAdd = new HashSet%3CString%3E(); // this contains ids to be added in this round
+Set%3CString%3E finalEdorgIds = new HashSet%3CString%3E(edorgIds);%0A        boolean added = true;%0A        while (added) %7B%0A            added = false;
 %0A   
@@ -1625,192 +1625,100 @@
     
-for (String 
 ed
-O
+o
 rgId
- : edOrgIds) %7B%0A                Entity edOrg = this.repository.find(EntityNames.EDUCATION_ORGANIZATION, edOrgId);%0A                Object newId = edOrg.getBody().get(%22
+s = helper.findEntitiesContainingReference(EntityNames.EDUCATION_ORGANIZATION, %22body.
 pare
@@ -1744,19 +1744,30 @@
 ference%22
+, edorgIds
 );%0A
+%0A
         
@@ -1774,493 +1774,179 @@
     
-    if (newId != null && !retVal.contains(newId.toString())) %7B%0A                    toAdd.add(newId.toString());%0A                %7D%0A            %7D%0A            if (toAdd.isEmpty()) %7B%0A                break; // no new ids to add; closure is reached.  %0A            %7D%0A            retVal.addAll(toAdd);%0A            edOrgIds = toAdd;%0A        %7D%0A%0A        return new ArrayList%3CString%3E(retVal);%0A    %7D%0A%0A    public void setRepository(EntityRepository repository) %7B%0A        this.repository = repository
+for (String crntEdorgId : edorgIds) %7B%0A                added %7C= finalEdorgIds.add(crntEdorgId);%0A            %7D%0A        %7D%0A%0A        return new ArrayList%3CString%3E(finalEdorgIds)
 ;%0A  

Resolve TODO - possible infinite loop caused by circular reference.