Index: trunk/src/org/expeditee/encryption/items/surrogates/Label.java
===================================================================
--- trunk/src/org/expeditee/encryption/items/surrogates/Label.java	(revision 1430)
+++ trunk/src/org/expeditee/encryption/items/surrogates/Label.java	(revision 1431)
@@ -4,4 +4,5 @@
 import java.util.Arrays;
 import java.util.Base64;
+import java.util.Collection;
 import java.util.List;
 import java.util.function.Predicate;
@@ -10,4 +11,5 @@
 
 import org.expeditee.gui.ItemsList;
+import org.expeditee.gui.MessageBay;
 import org.expeditee.items.Item;
 import org.expeditee.items.Text;
@@ -15,188 +17,181 @@
 
 public class Label {
-	
-	private static int surrogateStep = -1;
-
-	public static LabelResult getLabel(String label) {
-		Text[] userLabels = KeyList.UserLabels.get();
-		Text labelText = null;
-		for (Text userLabel : userLabels) {
-			if (userLabel.getText().equals(label)) {
-				labelText = userLabel;
+	private static List<String> userLabels = new ArrayList<String>();
+	private static List<LabelInfo> labelsInfo = new ArrayList<LabelInfo>();
+	private static int surrogateStep = 0;
+	private static boolean isInSurrogateMode = false;
+	private static boolean isUnprivileged = false;
+	
+	public static LabelInfo getLabel(String labelName) {
+		// Maintain the local cache of user labels
+		ensureUserLabels();
+		
+		int indexOfLabel = userLabels.indexOf(labelName);
+		if (indexOfLabel == -1) {
+			//A label by the specified name does not exist.
+			return unableToFindLabelResult(labelName);
+		} else {
+			//A label by the specified name is in the local cache and can be used.
+			LabelInfo labelResult = labelsInfo.get(indexOfLabel);
+			return labelResult;
+		}
+	}
+	
+	public static List<String> progressSurrogateMode(ItemsList context) {
+		// Maintain the local cache of user labels
+		ensureUserLabels();
+		
+		List<String> ret;
+		
+		List<String> localLabels = getLabelsFromContext(context);
+		if (userLabels.isEmpty() || localLabels.isEmpty()) {
+			// If either the labels in the context are empty or the user
+			// has no labels then we are not able to enter surrogate mode.
+			return userLabels;
+		}
+		
+		ArrayList<String> intersection = new ArrayList<String>(userLabels);
+		intersection.retainAll(localLabels);
+		if (!isInSurrogateMode) {
+			isInSurrogateMode = true;
+			// Priming surrogate step to become zero in the loop below.
+			surrogateStep = -1; 
+		}
+		
+		// We are already in surrogate mode, progress it.
+		int index = surrogateStep + 1;
+		boolean found = false;
+		while (index < userLabels.size()) {
+			String surrogateMode = userLabels.get(index);
+			if (intersection.contains(surrogateMode)) {
+				found = true;
 				break;
-			}
-		}
-
-		if (labelText == null) {
-			return unableToFindLabelResult(label);
+			}			
+			index++;
+		}
+				
+		if (found) {
+			isUnprivileged = false;
+			surrogateStep = index;
+			String chosenSurrogateMode = userLabels.get(surrogateStep);
+			ret = Arrays.asList(chosenSurrogateMode);
+		} else {			
+			if (surrogateStep >= userLabels.size()) {
+				surrogateStep = 0;
+				isInSurrogateMode = false;
+				isUnprivileged = false;
+				ret = intersection;
+			} else {
+				ret = new ArrayList<String>();
+				surrogateStep = index;
+				isUnprivileged = true;
+			}
+		}
+		
+		return ret;
+	}
+	
+	public static void resetSurrogateMode() {
+		// Maintain the local cache of user labels
+		ensureUserLabels();
+		
+		surrogateStep = 0;
+		isInSurrogateMode = false;
+	}
+	
+	public static List<String> getAccessibleLabelsNames(ItemsList itemsList) {
+		// Maintain the local cache of user labels
+		ensureUserLabels();
+		
+		if (isInSurrogateMode) {
+			if (isUnprivileged) {
+				return new ArrayList<String>();
+			} else {
+				return Arrays.asList(userLabels.get(surrogateStep));
+			}
 		} else {
-			List<String> data = labelText.getData();
-			if (data == null || data.isEmpty()) { 
-				return unableToFindLabelResult(label);
-			}
+			return getLabelsFromContext(itemsList);
+		}
+	}
+	
+	private static void ensureUserLabels() {
+		List<String> userSettingsLabelNames = KeyList.getLabelsNameOnly();
+		boolean hasNoKeys = userLabels.isEmpty() && userSettingsLabelNames.isEmpty();
+		boolean hasNotChanged = userLabels.equals(userSettingsLabelNames) || hasNoKeys;
+		
+		if (hasNotChanged) { return; }
+		
+		// Deal with the fact that the position of our current surrogate mode 
+		// might have moved in the users list of labels.
+		if (userLabels.size() > surrogateStep) {
+			String surrogateMode = userLabels.get(surrogateStep);
+			int newIndexOfSurrogateMode = userSettingsLabelNames.indexOf(surrogateMode);
+			if (newIndexOfSurrogateMode >= 0) {
+				surrogateStep = newIndexOfSurrogateMode;
+			} else {
+				surrogateStep = 0;
+				isInSurrogateMode = false;
+				MessageBay.displayMessage("You have lost access to the encryption label " 
+						+ surrogateMode + ".  Dropping out of surrogate mode.");
+			}
+		}
+		
+		// (Re)Initialise the userLabels list and labelResultsCache.
+		userLabels.clear();
+		labelsInfo.clear();
+		Text[] userSettings = KeyList.UserLabels.get();
+		for (Text userSetting: userSettings) {
+			String labelName = userSetting.getName();
+			List<String> data = userSetting.getData();
+			if (data == null || data.isEmpty()) {
+				continue;
+			}
+			
+			// Extract the data and check if we have a full or partial key.
 			String dataEntry = data.get(0);
-			
 			if (dataEntry.contains("{")) {
-				return partialKeyResult(label, dataEntry);
-			} else {
-				return fullKeyResult(label, dataEntry);
-			}
-		}
-	}
-	
-	public static void progressSurrogateMode() {
-		surrogateStep += 1;
-	}
-
-	public static void resetSurrogateMode() {
-		surrogateStep = -1;
-	}
-
-	public static List<String> getAccessibleLabelsNames(ItemsList itemsList) {
-		// TODO make copy of itemsList in here and then adjust callers so they do not need to make copy.
-		// Alternatively, filter instead of removeIf
-		Text[] userLabels = KeyList.UserLabels.get();
-		Stream<String> accessibleUserWideLabels = Arrays.asList(userLabels).stream().map(label -> label.getText());
-		
-		Predicate<Item> isItemWithoutEncryptionLabel = item -> item.getEncryptionLabel() == null || item.getEncryptionLabel().isEmpty();
-		itemsList.removeIf(isItemWithoutEncryptionLabel);
-		Stream<String> accessibleFrameLocalLabels = itemsList.underlying().stream().map(item -> item.getEncryptionLabel()).distinct();
-		
-		List<String> accessibleLabelsUnion = accessibleUserWideLabels.collect(Collectors.toList());
-		accessibleLabelsUnion.retainAll(accessibleFrameLocalLabels.collect(Collectors.toList()));
-		
-		if (surrogateStep > -1 && surrogateStep < accessibleLabelsUnion.size()) {
-			String surrogateModeFilter = accessibleLabelsUnion.get(surrogateStep);
-			List<String> single = new ArrayList<String>();
-			single.add(surrogateModeFilter);
-			return single;
-		} else {
-			resetSurrogateMode();
-		}
-		
-		return accessibleLabelsUnion;
-	}
-
-	private static LabelResult partialKeyResult(String label, String dataEntry) {
-		dataEntry = dataEntry.substring(dataEntry.indexOf('}') + 1).trim();
-		LabelResult res = LabelResult.SuccessResolveLabelToPartialKey;
-		res.name = label;
-		return res;
-	}
-
-	private static LabelResult unableToFindLabelResult(String label) {
-		LabelResult res = LabelResult.ErrorUnableToFindLabel;
-		res.name = label;
-		return res;
-	}
-	
-	private static LabelResult fullKeyResult(String label, String dataEntry) {
-		try {
-			LabelResult res = LabelResult.SuccessResolveLabelToKey;
-			res.key = Base64.getDecoder().decode(dataEntry);
-			res.name = label;
-			return res;
-		} catch (IllegalArgumentException e) {
-			return LabelResult.ErrorUnableToFindLabel;
-		}
-	}
-
-	//private static Map<LabelResult, LabelAccess> labelCache = new LinkedHashMap<LabelResult, LabelAccess>();
-	//private static int surrogateStep = -1;
-	//private static final Predicate<? super LabelResult> isAccessible = a -> labelCache.get(a).allowed;
-	
-//	public static LabelResult resolveKey(String label) {
-//		Stream<LabelResult> stream = labelCache.keySet().stream();
-//		stream = stream.filter(res -> res.name.equals(label));
-//		if (stream.count() == 0) {
-//			return resolveKeyWorker(label);
-//		} else {
-//			LabelResult labelResult = stream.collect(Collectors.toList()).get(0);
-//			LabelAccess accessible = labelCache.get(labelResult);
-//			if (accessible.allowed) { return labelResult; }
-//			else { return LabelResult.ErrorForbiddenLabel; }
-//		}
-//	}	
-//	private static LabelResult resolveKeyWorker(String label) {
-//		Frame secretsFrame = getSecretsFrame();
-//		if (secretsFrame == null) { 
-//			return LabelResult.ErrorUnableToFindSecretsFrame;
-//		}
-//		
-//		String data = getDataFromLabel(secretsFrame, label);
-//		if (data == null) {
-//			return LabelResult.ErrorUnableToFindLabel;
-//		}
-//		
-//		if (data.contains("{")) {
-//			data = data.substring(data.indexOf('}') + 1).trim();
-//			LabelResult res = LabelResult.SuccessResolveLabelToPartialKey;
-//			res.key = Base64.getDecoder().decode(data);
-//			return res;
-//		} else {
-//			try {
-//				LabelResult res = LabelResult.SuccessResolveLabelToKey;
-//				res.key = Base64.getDecoder().decode(data);
-//				res.name = label;
-//				labelCache.put(res, new LabelAccess(true));
-//				return res;
-//			} catch (IllegalArgumentException e) {
-//				return LabelResult.ErrorUnableToFindLabel;
-//			}
-//		}
-//	}
-//	
-//	private static String getDataFromLabel(Frame frame, String label) {
-//		Collection<Text> labels = frame.getTextItems();
-//		labels.removeIf(lbl -> !lbl.getText().equals(label));
-//		labels.removeIf(lbl -> lbl.getData() == null || lbl.getData().size() == 0);
-//		if (labels.isEmpty()) { return null; }
-//		Text labelItem = labels.iterator().next();
-//		List<String> data = labelItem.getData();
-//		if (data.isEmpty()) { return null; }
-//		return data.get(0);
-//	}
-//	
-//	private static Frame getSecretsFrame() {
-//		String credentialsFrameName = UserSettings.ProfileName.get() + 
-//				AuthenticatorBrowser.CREDENTIALS_FRAME;
-//		Frame credentialsFrame = FrameIO.LoadFrame(credentialsFrameName);
-//		Collection<Text> textItems = credentialsFrame.getTextItems();
-//		textItems.removeIf(t -> !t.getText().equals("Secrets"));
-//		textItems.removeIf(t -> !t.hasLink());
-//		if (textItems.isEmpty()) {
-//			return null;
-//		}
-//		Text linkToSecretsFrame = textItems.iterator().next();
-//		if (!linkToSecretsFrame.hasLink()) {
-//			return null;
-//		}
-//		Frame secretsFrame = FrameIO.LoadFrame(linkToSecretsFrame.getParent().getFramesetName() + linkToSecretsFrame.getLink());
-//		return secretsFrame;
-//	}
-//	
-//	private static class LabelAccess {
-//		public final boolean allowedDefault;
-//		public boolean allowed;
-//		
-//		public LabelAccess(boolean value) { 
-//			allowedDefault = value;
-//			allowed = allowedDefault;
-//		}
-//	} 
-	
-	public enum LabelResult {
-		SuccessResolveLabelToKey, 
-		SuccessResolveLabelToPartialKey, 
-		ErrorUnableToFindSecretsFrame, 
-		ErrorUnableToFindLabel, 
-		ErrorForbiddenLabel;
+				userLabels.add(labelName);
+				LabelInfo labelInfo = new LabelInfo(LabelResult.SuccessResolveLabelToPartialKey, labelName, null);
+				labelsInfo.add(labelInfo);
+			} else {
+				userLabels.add(labelName);
+				byte[] key = Base64.getDecoder().decode(dataEntry);
+				LabelInfo labelInfo = new LabelInfo(LabelResult.SuccessResolveLabelToKey, labelName, key);
+				labelsInfo.add(labelInfo);
+			}
+		}
+	}
+	
+	private static List<String> getLabelsFromContext(ItemsList context) {
+		Predicate<Item> hasEncryptionLabel = item -> item.getEncryptionLabel() != null && !item.getEncryptionLabel().isEmpty();
+		Collection<Item> local = new ItemsList(context).underlying();
+		Stream<Item> itemsWithLabel = local.stream().filter(hasEncryptionLabel);
+		Stream<String> labels = itemsWithLabel.map(item -> item.getEncryptionLabel()).distinct();
+		return labels.collect(Collectors.toList());
+	}
+
+	private static LabelInfo unableToFindLabelResult(String label) {
+		LabelInfo info = new LabelInfo(LabelResult.ErrorUnableToFindLabel, label, null);
+		return info;
+	}
+	
+	public static class LabelInfo {
 		
 		public byte[] key;
 		public String name;
-		
-		@Override
+		public LabelResult type;
+		
+		public LabelInfo(LabelResult type, String name, byte[] key) {
+			this.type = type;
+			this.name = name;
+			this.key = key;
+		}
+		
+		public boolean is(LabelResult type) {
+			return this.type == type;
+		}
+		
 		public String toString() {
-			switch (this) {
+			switch(type) {
 			case SuccessResolveLabelToKey:
 				return "Resolved label to key: " + Base64.getEncoder().encodeToString(key);
@@ -215,3 +210,11 @@
 		}
 	}
-}
+	
+	public enum LabelResult {
+		SuccessResolveLabelToKey, 
+		SuccessResolveLabelToPartialKey, 
+		ErrorUnableToFindSecretsFrame, 
+		ErrorUnableToFindLabel, 
+		ErrorForbiddenLabel;
+	}
+} 
