package com.kaviju.accesscontrol.service;

import com.kaviju.accesscontrol.annotation.*;
import com.kaviju.accesscontrol.model.*;
import com.webobjects.appserver.WOComponent;
import com.webobjects.eocontrol.EOEditingContext;
import com.webobjects.foundation.*;

import er.extensions.eof.ERXEC;

public class ComponentAccessService {
	static ComponentAccessService sharedInstance;
	private NSSet<String> _allRolesCodes;
	
	static public ComponentAccessService getInstance() {
		if (sharedInstance == null) {
			sharedInstance = new ComponentAccessService();
		}
		return sharedInstance;
	}
	
	static public void setTestInstance(ComponentAccessService instance) {
		sharedInstance = instance;
	}
	
	private ComponentAccessService() {
		
	}
	
	public boolean isComponentAccessibleForUser(Class<? extends WOComponent> componentClass, KAUser user) {
		NSSet<String> roleCodesThatAllowAccess = readAllowedRoleCodesInClass(componentClass);
		
		return user.hasAtLeastOneOfTheseRoles(roleCodesThatAllowAccess);
	}
	
	public NSSet<String> readAllowedRoleCodesInClass(Class<? extends WOComponent> componentClass) {
		boolean isAllowedForAll = verifyAllowedForAllAnnotationInClass(componentClass);
		NSSet<String> allowedRoleCodesAnnotation = readAllowedForRolesAnnotationInClass(componentClass);
		NSSet<String> deniedRoleCodesAnnotation = readDeniedForRolesAnnotationInClass(componentClass);
		
		if (isAllowedForAll) {
			if (allowedRoleCodesAnnotation != null || deniedRoleCodesAnnotation != null) {
				throw new RuntimeException("The class "+componentClass.getName()+" specify an AllowedForAll, an AllowedForRole and a DeniedForRole annotation and only one is allowed.");
			}
			return getAllPossibleRoleCodes();
		}
		
		if (allowedRoleCodesAnnotation != null && deniedRoleCodesAnnotation != null) {
			throw new RuntimeException("The class "+componentClass.getName()+" specify both an AllowedForRole and a DeniedForRole annotation and only one is allowed.");
		}

		if (allowedRoleCodesAnnotation != null) {
			return allowedRoleCodesAnnotation;
		}
		
		if (deniedRoleCodesAnnotation != null) {
			return getAllPossibleRoleCodes().setBySubtractingSet(deniedRoleCodesAnnotation);
		}
		return new NSSet<String>(); // Nothing specified, no access allowed.
	}

	public boolean verifyAllowedForAllAnnotationInClass(Class<? extends WOComponent> componentClass) {
		AllowedForAll annotation = componentClass.getAnnotation(AllowedForAll.class);
		return annotation != null;
	}

	public NSSet<String> readAllowedForRolesAnnotationInClass(Class<? extends WOComponent> componentClass) {
		AllowedForRole annotation = componentClass.getAnnotation(AllowedForRole.class);
		if (annotation != null) {
			return new NSSet<String>(annotation.value());
		}
		return null;
	}
	public NSSet<String> readDeniedForRolesAnnotationInClass(Class<? extends WOComponent> componentClass) {
		DeniedForRole annotation = componentClass.getAnnotation(DeniedForRole.class);
		if (annotation != null) {
			return new NSSet<String>(annotation.value());
		}
		return null;
	}
	
	
	protected NSSet<String> getAllPossibleRoleCodes() {
		if (_allRolesCodes == null) {
			EOEditingContext ec = ERXEC.newEditingContext();
			ec.lock();
			try {
				NSArray<KARole> roles = KARole.fetchAllKARoles(ec);
				_allRolesCodes = new NSSet<String>(KARole.CODE.arrayValueInObject(roles));
			}
			finally {
				ec.unlock();
			}
		}
		return _allRolesCodes;
	}
	
	protected void setAllRolesCodesForTest(NSSet<String> allRolesCodes) {
		_allRolesCodes = allRolesCodes;
	}
	
}

@@ -79,17 +79,192 @@
 otation.
-*
+AllowedForAll;%0Aimport com.kaviju.accesscontrol.annotation.AllowedForRole;%0Aimport com.kaviju.accesscontrol.annotation.DeniedForRole;%0Aimport com.kaviju.accesscontrol.model.KARole
 ;%0Aimport
@@ -295,17 +295,22 @@
 l.model.
-*
+KAUser
 ;%0Aimport
@@ -435,9 +435,102 @@
 ion.
-*
+NSArray;%0Aimport com.webobjects.foundation.NSSet;%0Aimport com.webobjects.foundation._NSUtilities
 ;%0A%0Ai
@@ -1002,24 +1002,381 @@
 ) %7B%0A%09%09%0A%09%7D%0A%09%0A
+%09public boolean isComponentAccessibleForUser(String componentName, KAUser user) %7B%0A%09%09@SuppressWarnings(%22unchecked%22)%0A%09%09Class%3CWOComponent%3E componentClass = _NSUtilities.classWithName(componentName);%0A%09%09NSSet%3CString%3E roleCodesThatAllowAccess = readAllowedRoleCodesInClass(componentClass);%0A%09%09%0A%09%09return user.hasAtLeastOneOfTheseRoles(roleCodesThatAllowAccess);%0A%09%7D%0A
 %09public bool

Add method to check access with component name instead of class.