package de.uka.ipd.consensus.impl;

import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;

import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionBindingListener;

import de.uka.ipd.consensus.foundation.ontology.OntologyManagerException;
import de.uka.ipd.consensus.foundation.rating.RatingManager;
import de.uka.ipd.consensus.foundation.rating.RatingManagerException;
import de.uka.ipd.consensus.foundation.rights.DynamicRightsManager;
import de.uka.ipd.consensus.foundation.rights.DynamicRightsManagerException;
import de.uka.ipd.consensus.foundation.schema.Rating;
import de.uka.ipd.consensus.foundation.schema.User;
import de.uka.ipd.consensus.foundation.user.UserManager;
import de.uka.ipd.consensus.foundation.user.UserManagerException;
import de.uka.ipd.consensus.foundation.user.UserRole;

/**
 * 
 * TODO
 * @author Thomas Much
 * @version 2006-06-14
 */
public class UserImpl extends TM4JObjectWrapper implements User, HttpSessionBindingListener {

	private static final String USERID_PREFIX = OntologyManagerImpl.INTERNAL_TYPE_PREFIX + "UserId_";
	
	private static Map loggedon = new HashMap();

	private String username;
	private String firstname;
	private String lastname;
	private String email;
	private String comment;
	private int passwordHash;
	private double score;

	private transient Date loginTime = null;

	private transient UserManagerImpl manager;

	private Set roles = new HashSet();



	public static UserImpl getInstance(OntologyManagerImpl omi, org.tm4j.topicmap.Topic userTopic) {
		String userid = userTopic.getID();
		UserImpl ui = getFromCaches( userid );
		if (ui == null) {
			UserManagerImpl umi = (UserManagerImpl)omi.getUserManager();
			ui = new UserImpl( umi, omi, extractUsername(userid), userTopic );
			umi.loadUserData( ui );
			putCache( userid, ui );
		}
		return ui;
	}


	public static UserImpl getInstance(UserManagerImpl umi, String username, int passwordHash, String firstname, String lastname, String email, double initScore, String comment, String roles) throws Exception {
		String userid = createId( username );
		UserImpl user = getFromCaches( userid );
		if (user == null) {
			OntologyManagerImpl omi = (OntologyManagerImpl)umi.getOntologyManager();

			org.tm4j.topicmap.Topic userTopic = omi.getTopicMap().getTopicByID( userid );

			if (userTopic == null) {
				userTopic = omi.createNewTopic( userid, null, omi.getUserTopicType() );
			}

			user = new UserImpl( umi, omi, username, passwordHash, firstname, lastname, email, initScore, userTopic, comment, roles );

			putCache( userid, user );
		}
		else {
			user.init( passwordHash,firstname,lastname,email,initScore,comment,roles );
		}
		return user;
	}
	
	
	private static UserImpl getFromCaches(String userid) {
		UserImpl user = (UserImpl)loggedon.get(userid);
		if (user == null) {
			return (UserImpl)getCache( userid );
		}
		else {
			return user;
		}
	}

	
	
	private UserImpl(UserManagerImpl umi, OntologyManagerImpl omi, String username, org.tm4j.topicmap.Topic userTopic) {
		this(umi,omi,username,0,"","","",0,userTopic,"",null);
	}


	private UserImpl(UserManagerImpl umi, OntologyManagerImpl omi, String username, int passwordHash, String firstname, String lastname, String email, double initScore, org.tm4j.topicmap.Topic userTopic, String comment, String roles) {
		super( omi, userTopic );

		this.manager = umi;

		this.username = username;
		this.passwordHash = passwordHash;
		this.firstname = firstname;
		this.lastname = lastname;
		this.email = email;
		this.score = initScore;
		this.comment = comment;

		this.initRoles( roles );
		umi.assignSystemRoles( this );
	}


	protected void init(int passwordHash, String firstname, String lastname, String email, double initScore, String comment, String roles) {
		this.passwordHash = passwordHash;
		this.firstname = firstname;
		this.lastname = lastname;
		this.email = email;
		this.score = initScore;
		this.comment = comment;

		this.initRoles( roles );
	}
	
	
	protected void change(int passwordHash, String firstname, String lastname, String email, String comment) {
		this.passwordHash = passwordHash;
		this.firstname = firstname;
		this.lastname = lastname;
		this.email = email;
		this.comment = comment;
	}


	protected org.tm4j.topicmap.Topic getWrappedTopic() {
		return (org.tm4j.topicmap.Topic)this.getWrappedObject();
	}


	public UserManager getUserManager() {
		return manager;
	}


	public String getUsername() {
		return username;
	}


	protected static String extractUsername(String userid) {
		if (userid.startsWith( USERID_PREFIX ) && (userid.length() > USERID_PREFIX.length())) {
			userid = userid.substring( USERID_PREFIX.length() );
		}
		return userid;
	}


	private static String createId(String username) {
		return USERID_PREFIX + username;		
	}


	public String getId() {
		return createId( this.username );
	}


	public String getFirstname() {
		return firstname;
	}


	public String getLastname() {
		return lastname;
	}

	
	public String getEmail() {
		return email;
	}


	public int getPasswordHash() {
		return passwordHash;
	}


	public Date getLoginTime() {
		return loginTime;
	}	


	public Collection getRatings() {
		if (this.getOntologyManager().getRatingManager() != null) {
			return this.getOntologyManager().getRatingManager().getRatings( this );
		}
		else {
			return Collections.EMPTY_SET;
		}
	}


	public double getAverageRatingValue() {
		if (this.getOntologyManager().getRatingManager() != null) {
			return this.getOntologyManager().getRatingManager().getAverageRatingValue( this );
		}
		else {
			return Rating.NEUTRAL;
		}
	}


	public int getRatingsCount() {
		if (this.getOntologyManager().getRatingManager() != null) {
			return this.getOntologyManager().getRatingManager().getRatingsCount( this );
		}
		else {
			return 0;
		}
	}


	public Rating addRating(User rater, double value) throws RatingManagerException,DynamicRightsManagerException {
		Rating r = this.getRating( rater );
		if (r != null) {
			r.setValue( value );
		}
		else {
			RatingManager rm = this.getOntologyManager().getRatingManager();
			if (rm != null) {
				r = rm.createRating( rater, value );
				rm.addRating( this, r );
			}
		}
		return r;
	}

	
	public Rating getRating(User user) {
		if (this.getOntologyManager().getRatingManager() != null) {
			return this.getOntologyManager().getRatingManager().getRating( this, user );
		}
		else {
			return null;
		}
	}


	public double getAverageOwnRatingsValue() {
		if (this.getOntologyManager().getRatingManager() != null) {
			return this.getOntologyManager().getRatingManager().getUsersAverageOwnRatingsValue( this );
		}
		else {
			return Rating.NEUTRAL;
		}
	}


	public Collection getOwnRatings() {
		if (this.getOntologyManager().getRatingManager() != null) {
			return this.getOntologyManager().getRatingManager().getUsersOwnRatings( this );
		}
		else {
			return Collections.EMPTY_SET;
		}
	}


	public int getOwnRatingsCount() {
		if (this.getOntologyManager().getRatingManager() != null) {
			return this.getOntologyManager().getRatingManager().getUsersOwnRatingsCount( this );
		}
		else {
			return 0;
		}
	}


	public String getComment() {
		return this.comment;
	}


	public void setComment(User setter, String comment) throws OntologyManagerException,DynamicRightsManagerException {
		DynamicRightsManager drm = this.getOntologyManager().getDynamicRightsManager();
		if (drm != null) {
			drm.checkChangeUserComment( setter, this );
		}

		if (comment == null) {
			comment ="";
		}

		if (!comment.equals( this.comment )) {
			this.comment = comment;
			manager.saveUserComment( this );
		}
	}


	public String toString() {
		return "User \"" + username + "\" (" + lastname + ", " + firstname + ", " + email + ( loginTime != null ? (", login " + loginTime) : "") + ")";
	}


	public int compareTo(Object obj) {
		return username.toLowerCase().compareTo( ((UserImpl)obj).username.toLowerCase() );
	}


	public void setScore(double score) {
		this.score = score;
		manager.saveUserScore( this );
	}


	public double getScore() {
		return this.score;
	}


	private void initRoles(String rolestr) {
		if (rolestr != null) {
			StringTokenizer st = new StringTokenizer( rolestr, "," );
			while (st.hasMoreTokens()) {
				String s = st.nextToken().trim();
				if (s.length() > 0) {
					UserRole role = manager.findRole( s );
					if (role != null) {
						this.roles.add( role );
					}
				}
			}
		}
	}


	public void addRole(UserRole role) {
		if ((role != null) && (!this.isInRole( role ))) {
			roles.add( role );
			manager.saveUserRoles( this );
		}
	}


	public Collection getRoles() {
		return Collections.unmodifiableSet( roles );
	}


	public boolean isInRole(UserRole role) {
		if (role == null) return false;
		return roles.contains( role );
	}


	public void removeRole(UserRole role) {
		if (this.isInRole( role )) {
			roles.remove( role );
			manager.saveUserRoles( this );
		}
	}


	public boolean isAdmin() {
		return isInRole( manager.findRole(UserManagerImpl.ROLE_ADMIN) ) || this.isSuperAdmin();
	}


	public boolean isSuperAdmin() {
		return isInRole( manager.findRole(UserManagerImpl.ROLE_SUPERADMIN) );
	}


	public boolean isLoggedOn() {
		return (loginTime != null);
	}


	protected void setLoggedOn(boolean loggedOn) {
		if (loggedOn) {
			this.loginTime = new Date();
			loggedon.put( this.getId(), this );
		}
		else {
			this.loginTime = null;
			loggedon.remove( this.getId() );
		}
	}


	public void valueBound(HttpSessionBindingEvent event) { }


	public void valueUnbound(HttpSessionBindingEvent event) {
		try {
			if (this.isLoggedOn()) {
				manager.logout( this );
			}
		}
		catch (UserManagerException e) {
			manager.getLoggingManager().error( e );
		}
	}

}
