package tahrir.io.net.broadcasts;
import com.google.common.base.Optional;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.common.eventbus.EventBus;
import com.google.gson.reflect.TypeToken;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import tahrir.TrConstants;
import tahrir.tools.TrUtils;
import tahrir.ui.IdentityModifiedEvent;
import java.io.*;
import java.lang.reflect.Type;
import java.security.interfaces.RSAPublicKey;
import java.util.*;
/**
* Author : Ravisvi <ravitejasvi@gmail.com>
* Date : 26/6/13
*/
public class IdentityStore {
private static Logger logger = LoggerFactory.getLogger(IdentityStore.class);
private File identityStoreFile = new File(TrConstants.IDENTITY_STORE_TEST_FILE_PATH);
//contains circles in String (label) i.e FOLLOWING, etc. And the id info in UserIdentity.
private HashMap<String, Set<UserIdentity>> usersInLabels;
public HashMap<UserIdentity, Set<String>> labelsOfUser = Maps.newHashMap();
private TreeMap<String, Set<UserIdentity>> usersWithNickname = Maps.newTreeMap();
public EventBus eventBus;
public IdentityStore(File identityStoreFile){
this.identityStoreFile=identityStoreFile;
if(identityStoreFile.exists()){
try {
FileReader identityStoreFileReader = new FileReader(identityStoreFile);
logger.info("Trying to load identity store.");
Type idStoreType = new TypeToken<Map<String, Set<UserIdentity>>>() {}.getType();
usersInLabels = TrUtils.gson.fromJson(identityStoreFileReader, idStoreType);
if (usersInLabels == null) {
logger.info("Failed to load any idStore. Creating new Identity Store.");
usersInLabels = Maps.newHashMap();
}
else {
updateOtherMaps(usersInLabels);
logger.info("Identity Store successfully loaded from file.");
}
}
catch (IOException e) {
logger.info("The identity store file doesn't exist.");
}
}
else{
usersInLabels = Maps.newHashMap();
logger.info("The identity store file doesn't exist.");
}
}
public void setEventBus(EventBus eventBus){
this.eventBus = eventBus;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
IdentityStore that = (IdentityStore) o;
if (labelsOfUser != null ? !labelsOfUser.equals(that.labelsOfUser) : that.labelsOfUser != null) return false;
if (usersInLabels != null ? !usersInLabels.equals(that.usersInLabels) : that.usersInLabels != null)
return false;
return true;
}
@Override
public int hashCode() {
int result = usersInLabels != null ? usersInLabels.hashCode() : 0;
result = 31 * result + (labelsOfUser != null ? labelsOfUser.hashCode() : 0);
return result;
}
private void updateOtherMaps(HashMap<String, Set<UserIdentity>> usersInLabel){
for (Map.Entry<String, Set<UserIdentity>> pairs : usersInLabel.entrySet()){
for(UserIdentity userIdentity: pairs.getValue()){
updateLabelsOfUsers(userIdentity, pairs.getKey());
addIdentityToUsersWithNickname(userIdentity);
}
}
}
public void addIdentity(UserIdentity identity){
//As label is not specified, this contact is not added to the file, but added temporarily to the map
if(!(labelsOfUser.containsKey(identity))){
Set<String> labels=Collections.emptySet();
labelsOfUser.put(identity, labels);
logger.debug("New identity created and label added.");
addIdentityToUsersWithNickname(identity);
}
else{
logger.debug("Identity already exists");
}
}
public void removeIdentity(UserIdentity identity){
if(labelsOfUser.containsKey(identity)){
removeIdentityFromNick(identity);
for(String label: labelsOfUser.get(identity)){
removeIdentityFromLabel(identity, label);
}
labelsOfUser.remove(identity);
logger.debug("Identity removed.");
eventBus.post(new IdentityModifiedEvent(identity, IdentityModifiedEvent.IdentityModificationType.DELETE));
updateIdentityInFile();
}
else{
//adds to the already existing identity if the label isn't present.
logger.debug("Identity doesn't exist.");
}
}
public void addIdentityWithLabel(String label, UserIdentity identity){
//checks whether the identity exists, if not, adds the identity first and then adds label.
if(!(labelsOfUser.containsKey(identity))){
Set<String> labels=Sets.newHashSet();
labels.add(label);
labelsOfUser.put(identity, labels);
logger.debug("New identity created and label added.");
addIdentityToUsersWithNickname(identity);
updateUsersInLabel(identity, label);
this.eventBus.post(new IdentityModifiedEvent(identity, IdentityModifiedEvent.IdentityModificationType.ADD));
updateIdentityInFile();
}
else{
//adds to the already existing identity if the label isn't present.
if(((labelsOfUser.get(identity)).contains(label))){
logger.debug("Identity already contains the label.");
}
else{
labelsOfUser.get(identity).add(label);
logger.debug("Added label to the existing identity.");
updateUsersInLabel(identity, label);
}
}
}
private void updateLabelsOfUsers(UserIdentity identity, String label){
if (!(labelsOfUser.containsKey(identity))){
Set<String> labelSet = Sets.newHashSet();
labelSet.add(label);
labelsOfUser.put(identity, labelSet);
}
else if(!(labelsOfUser.get(identity).contains(label))){
labelsOfUser.get(identity).add(label);
logger.debug("Label was not present on the existing identity, added it.");
}
else{
logger.debug("Identity already conains the label.");
}
}
private void updateUsersInLabel(UserIdentity identity, String label){
if (!(usersInLabels.containsKey(label))){
Set<UserIdentity> identitySet = Sets.newHashSet();
identitySet.add(identity);
usersInLabels.put(label, identitySet);
}
else if(usersInLabels.get(label).contains(identity)){
logger.debug("Label already contains identity.");
}
else{
usersInLabels.get(label).add(identity);
logger.debug("Added identity to label.");
}
}
public boolean addIdentityToUsersWithNickname(UserIdentity identity){
if(usersWithNickname.containsKey(identity.getNick())){
//usersWithNickname.get(identity.getNick()).add(identity);
//logger.debug("Nick was already present, added identity to it.");
logger.debug("Nick already present choose another.");
return false;
}
else{
Set<UserIdentity> identitySet=new HashSet();
identitySet.add(identity);
usersWithNickname.put(identity.getNick(), identitySet);
logger.debug("Nick created and identity added.");
return true;
}
}
public void removeLabelFromIdentity(String label, UserIdentity identity){
if(labelsOfUser.containsKey(identity)){
if(labelsOfUser.get(identity).contains(label)){
labelsOfUser.get(identity).remove(label);
logger.debug("Label removed from identity.");
removeIdentityFromLabel(identity, label);
eventBus.post(new IdentityModifiedEvent(identity, IdentityModifiedEvent.IdentityModificationType.DELETE));
updateIdentityInFile();
}
else{
logger.debug("The identity doesn't contain the label.");
}
}
else{
logger.debug("The identity doesn't exist");
}
}
private void removeIdentityFromLabel(UserIdentity identity, String label){
if(usersInLabels.containsKey(label)){
if(usersInLabels.get(label).contains(identity)){
usersInLabels.get(label).remove(identity);
logger.debug("Removed identity from the label.");
removeIdentityFromNick(identity);
}
else{
logger.debug("Identity not present in the label.");
}
}
else{
logger.debug("Label doesn't exist");
}
}
public boolean hasIdentityInLabel(UserIdentity identity){
//currently checks if user is FOLLOWING the identity.
if(labelsOfUser.containsKey(identity)){
if(labelsOfUser.get(identity).contains(TrConstants.FOLLOWING)){
return true;
}
}
return false;
}
public boolean hasIdentityInIdStore(UserIdentity identity){
if(usersWithNickname.containsKey(identity.getNick())){
if (usersWithNickname.get(identity.getNick()).contains(identity)){
return true;
}
else return false;
}
else return false;
}
public Set<UserIdentity> getIdentitiesWithLabel(String label){
if(usersInLabels.containsKey(label)){
logger.debug("Label was present, returning userIdentities.");
return usersInLabels.get(label);
}
else{
return Collections.emptySet();
}
}
public void removeIdentityFromNick(UserIdentity identity) {
if(usersWithNickname.containsKey(identity.getNick())){
logger.debug("Nickname exists, removing identity from it.");
usersWithNickname.get(identity.getNick()).remove(identity);
eventBus.post(new IdentityModifiedEvent(identity, IdentityModifiedEvent.IdentityModificationType.DELETE));
}
else{
logger.debug("Nickname isn't present so identity is also not present.");
}
}
public Set<String> getLabelsForIdentity(UserIdentity identity){
if(labelsOfUser.containsKey(identity)){
logger.debug("Identity was present, returning corresponding labels.");
return labelsOfUser.get(identity);
}
else{
return Collections.emptySet();
}
}
public SortedSet<UserIdentity> getUserIdentitiesStartingWith(String nick){
int indexOfLastChar=nick.length()-1;
String upperBoundNick= nick.substring(0, indexOfLastChar);
upperBoundNick += (char)(nick.charAt(indexOfLastChar)+1);
final SortedMap<String, Set<UserIdentity>> sortedMap = usersWithNickname.subMap(nick, upperBoundNick);
SortedSet<UserIdentity> sortedUserIdentities = Sets.newTreeSet(new NickNameComparator());
for (Set<UserIdentity> userIdentities : sortedMap.values()) {
sortedUserIdentities.addAll(userIdentities);
}
return sortedUserIdentities;
}
private void updateIdentityInFile() {
logger.info("Adding identities to file");
try {
//TODO: Try to append idStore into the file rather than rewriting the whole thing.
FileWriter identityStoreWriter = new FileWriter(identityStoreFile);
identityStoreWriter.write(TrUtils.gson.toJson(usersInLabels));
identityStoreWriter.close();
} catch (final IOException ioException) {
logger.error("Problem writing identities to file: the identities weren't saved.");
ioException.printStackTrace();
}
}
public Set<UserIdentity> getIdentitiesWithNick(String nick){
if(usersWithNickname.containsKey(nick)){
return usersWithNickname.get(nick);
}
else{
return Collections.emptySet();
}
}
public Optional<UserIdentity> getIdentityWithNick(String nick){
/**Current version can't handle more than one identity with same nick.
* This get's the first identity with that nick.
*/
if(usersWithNickname.containsKey(nick)){
return Optional.of(usersWithNickname.get(nick).iterator().next());
}
else{
return Optional.absent();
}
}
public Optional<UserIdentity> getIdentityWithPubKey(RSAPublicKey authorKey) {
for( Map.Entry<String, Set<UserIdentity>> entry: usersWithNickname.entrySet()){
for(UserIdentity identity: entry.getValue()){
if (identity.getPubKey().equals(authorKey)){
return Optional.of(identity);
}
}
}
return Optional.absent();
}
private static class NickNameComparator implements Comparator<UserIdentity> {
@Override
public int compare(final UserIdentity o1, final UserIdentity o2) {
int nickComparison = o1.getNick().compareTo(o2.getNick());
if (nickComparison != 0) {
return nickComparison;
} else {
return o1.getPubKey().toString().compareTo(o2.getPubKey().toString());
}
}
}
}