package de.uka.ipd.consensus.impl;

import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;

import de.uka.ipd.consensus.foundation.ontology.OntologyManagerClassException;
import de.uka.ipd.consensus.foundation.ontology.OntologyManagerException;
import de.uka.ipd.consensus.foundation.query.QueryEngineException;
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.Association;
import de.uka.ipd.consensus.foundation.schema.AssociationRole;
import de.uka.ipd.consensus.foundation.schema.Rating;
import de.uka.ipd.consensus.foundation.schema.Topic;
import de.uka.ipd.consensus.foundation.schema.User;
import de.uka.ipd.consensus.foundation.schema.Version;

/**
 * This is the TM4J-based implementation of the Association interface.
 * @author Thomas Much
 * @version 2006-06-14
 */
public class AssociationImpl extends TM4JObjectWrapper implements Association {
	
	private User creator = null;


	protected AssociationImpl(OntologyManagerImpl omi, org.tm4j.topicmap.Association tmAssociation) {
		super(omi,tmAssociation);
	}


	protected org.tm4j.topicmap.Association getWrappedAssociation() {
		return (org.tm4j.topicmap.Association)this.getWrappedObject();
	}


	public String getId() {
		return this.getObjectId();
	}


	public Topic getType() {
		Object obj = TM4JObjectWrapper.wrap( this.getOntologyManager(), this.getWrappedAssociation().getType() );
		if ((obj != null) && (obj instanceof Topic)) {
			return (Topic)obj;
		}
		else {
			return null;
		}
	}


	public void setType(User setter, Topic type) throws OntologyManagerException,DynamicRightsManagerException {
		if ((type != null) && this.getOntologyManager().useClasses() && !type.isClassTopic()) {
			throw new OntologyManagerClassException("Type " + type + " is not a class");
		}

		DynamicRightsManager drm = this.getOntologyManager().getDynamicRightsManager();
		if (drm != null) {
			drm.checkChangeAssociation( setter, this );
		}

		Topic oldType = this.getType();
		if (((oldType == null) && (type != null))
				|| ((oldType != null) && !oldType.equals( type ))) {
			try {
				org.tm4j.topicmap.Topic tmTopic = (type != null) ? ((TopicImpl)type).getWrappedTopic() : null;

				this.getWrappedAssociation().setType( tmTopic );
				
				this.incVersion();
				this.getOntologyManager().fireConceptTypeChanged( this, setter, oldType );
			}
			catch (Exception e) {
				throw new OntologyManagerException( e );
			}
		}
	}


	public boolean isOfType(Topic type) {
		org.tm4j.topicmap.Topic tmTopic = null;
		if (type != null) {
			tmTopic = ((TopicImpl)type).getWrappedTopic();
		}
		return this.getWrappedAssociation().isOfType( tmTopic );
	}


	public Collection getAssociationRoles(User getter) {
		try {
			return this.getOntologyManager().getQueryEngine().getAssociationRoles( getter, this );
		}
		catch (QueryEngineException e) {
			this.getLoggingManager().error("Error getting association roles: " + e);
			return Collections.EMPTY_SET;
		}
	}


	public AssociationRole getAssociationRole(Topic type) {
		org.tm4j.topicmap.Topic tmTopic = null;
		if (type != null) {
			tmTopic = ((TopicImpl)type).getWrappedTopic();
		}
		Collection c = this.getOntologyManager().getMembersOfRole( this.getWrappedAssociation(), tmTopic );
		if (c != null) {
			Iterator it = c.iterator();
			if (it.hasNext()) {
				org.tm4j.topicmap.Member member = (org.tm4j.topicmap.Member)it.next();
				return TM4JObjectWrapper.wrap( this.getOntologyManager(), member );
			}
		}
		return null;
	}


	public AssociationRole addAssociationRole(User adder, Topic type) throws OntologyManagerException,DynamicRightsManagerException {
		if ((type != null) && this.getOntologyManager().useClasses() && !type.isClassTopic()) {
			throw new OntologyManagerClassException("Type " + type + " is not a class");
		}

		DynamicRightsManager drm = this.getOntologyManager().getDynamicRightsManager();
		if (drm != null) {
			drm.checkChangeAssociation( adder, this );
		}

		AssociationRole role = this.getAssociationRole( type );
		if (role == null) {
			org.tm4j.topicmap.Topic tmTopic = null;
			if (type != null) {
				tmTopic = ((TopicImpl)type).getWrappedTopic();
			}
			try {
				org.tm4j.topicmap.Member member = this.getWrappedAssociation().createMember( null, null, tmTopic, Collections.EMPTY_SET );
				
				this.getOntologyManager().createSourceLocator( member );

				role = TM4JObjectWrapper.wrap( this.getOntologyManager(), member );

				this.incVersion();
			}
			catch (Exception e) {
				throw new OntologyManagerException( e );
			}
		}
		return role;
	}


	public void removeAssociationRole(User remover, Topic type) throws OntologyManagerException,DynamicRightsManagerException {
		DynamicRightsManager drm = this.getOntologyManager().getDynamicRightsManager();
		if (drm != null) {
			drm.checkChangeAssociation( remover, this );
		}
		// TODO
		// this.incVersion();
	}


	public void removeTopic(User remover, Topic topic, Topic roleType) throws OntologyManagerException,DynamicRightsManagerException {
		AssociationRole role = this.getAssociationRole( roleType );
		if (role != null) {
			role.removeTopic( remover, topic );
			// version is incremented in role
		}
	}


	public void addTopic(User adder, Topic topic, Topic roleType) throws OntologyManagerException,DynamicRightsManagerException {
		AssociationRole role = this.addAssociationRole( adder, roleType );
		role.addTopic( adder, topic );
		// version is incremented in role
	}


	public boolean hasTopic(Topic topic, Topic roleType) {
		AssociationRole role = this.getAssociationRole( roleType );
		if (role != null) {
			return role.hasTopic( topic );
		}
		else {
			return false;
		}
	}


	public boolean hasTopic(User user, Topic topic) {
		if (topic != null) {
			Collection c = this.getAssociationRoles( user );
			Iterator it = c.iterator();
			while (it.hasNext()) {
				AssociationRole role = (AssociationRole)it.next();
				if (role.hasTopic( topic )) {
					return true;
				}
			}
		}
		return false;
	}


	public Collection getTopics(User getter, Topic roleType) {
		AssociationRole role = this.getAssociationRole( roleType );
		if (role != null) {
			return role.getTopics( getter );
		}
		else {
			return Collections.EMPTY_SET;			
		}
	}


	public String getName() {
		String name = null;
		
		org.tm4j.topicmap.Topic nameTopic = this.getOntologyManager().getFirstPlayer( this.getWrappedAssociation(), this.getOntologyManager().getNameTopicType() );

		if (nameTopic != null) {
			name = this.getOntologyManager().getTopicName( nameTopic );
		}

		if ((name == null) || (name.length() == 0)) {
			Topic t = getType();

			if (t != null) {
				name = t.getName();
			}
			
			if ((name == null) || (name.length() == 0)) {
				name = this.getId();
			}
		}

		return name;
	}


	public void setName(User setter, String name) throws OntologyManagerException,DynamicRightsManagerException {
		if (this.getName().equals( name )) {
			// if the type name or the id is displayed and the new name equals the displayed name, ignore this call
			return;
		}

		try {
			org.tm4j.topicmap.Topic nameTopic = this.getOntologyManager().getFirstPlayer( this.getWrappedAssociation(), this.getOntologyManager().getNameTopicType() );

			String oldName;
			
			if (nameTopic == null) {
				oldName = "";
			}
			else {
				oldName = this.getOntologyManager().getTopicName( nameTopic );
			}
			
			if (((nameTopic != null) && (!oldName.equals( name )))
					|| ((nameTopic == null) && (name != null) && (name.length() > 0))) {

				if (nameTopic == null) {
					nameTopic = this.createNameTopic();
				}
				
				this.getOntologyManager().setTopicName( nameTopic, name );
				
				this.incVersion();

				if (setter != null) {
					// called from OntologyManagerImpl with setter==null (for not to score twice on association creation)
					this.getOntologyManager().fireConceptNameChanged( this, setter, oldName );
				}
			}
		}
		catch (Exception e) {
			throw new OntologyManagerException( e );
		}
	}
	
	
	private org.tm4j.topicmap.Topic createNameTopic() throws Exception {
		org.tm4j.topicmap.Topic nameTopic = this.getOntologyManager().createNewTopic( null, null, this.getOntologyManager().getNameTopicType() );
		
		org.tm4j.topicmap.Member member = this.getWrappedAssociation().createMember( null, null, this.getOntologyManager().getNameTopicType(), Collections.EMPTY_SET );
		
		this.getOntologyManager().createSourceLocator( member );
		
		member.addPlayer( nameTopic );
		
		return nameTopic;
	}


	private org.tm4j.topicmap.Occurrence getOccurrence(org.tm4j.topicmap.Topic tmType, boolean create) throws Exception {
		org.tm4j.topicmap.Topic nameTopic = this.getOntologyManager().getFirstPlayer( this.getWrappedAssociation(), this.getOntologyManager().getNameTopicType() );
		if (nameTopic == null) {
			if (create) {
				nameTopic = this.createNameTopic();
			}
			else {
				return null;
			}
		}
		org.tm4j.topicmap.Occurrence occ = getOccurrence( nameTopic, tmType );
		if ((occ == null) && create) {
			occ = nameTopic.createOccurrence( null, null, tmType, "", EMPTY_SCOPE );
		}
		return occ;
	}


	protected Version incVersion() {
		VersionImpl ver = null;
		try {
			org.tm4j.topicmap.Occurrence occ = this.getOccurrence( this.getOntologyManager().getVersionTopicType(), true );
			long ms = -1L;
			String msstr = occ.getData();
			if ((msstr != null) && (msstr.length() > 0)) {
				try {
					ms = Long.parseLong( msstr );
				}
				catch (NumberFormatException e) { }
			}
			if (ms == -1) {
				ver = new VersionImpl( this );
			}
			else {
				ver = new VersionImpl( this, ms );
				ver.inc();
			}
			msstr = String.valueOf( ver.getDate().getTime() );
			occ.setData( msstr );
		}
		catch (Exception e) {
			this.getLoggingManager().error("Error incrementing association version: " + e);
		}
		return ver;
	}


	public Version getVersion() {
		try {
			org.tm4j.topicmap.Occurrence occ = this.getOccurrence( this.getOntologyManager().getVersionTopicType(), false );
			if (occ != null) {
				String msstr = occ.getData();
				if ((msstr != null) && (msstr.length() > 0)) {
					try {
						long ms = Long.parseLong( msstr );
						return new VersionImpl( this, ms );
					}
					catch (NumberFormatException e) { }
				}
			}
		}
		catch (Exception e) {
			this.getLoggingManager().error("Error getting association version: " + e);
		}
		return this.incVersion();
	}


	public String getComment() {
		try {
			org.tm4j.topicmap.Occurrence occ = this.getOccurrence( this.getOntologyManager().getCommentTopicType(), false );
			if (occ != null) {
				return occ.getData();
			}
		}
		catch (Exception e) {
			this.getLoggingManager().error("Error getting association comment: " + e);
		}
		return "";
	}


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

		try {
			org.tm4j.topicmap.Occurrence occ = this.getOccurrence( this.getOntologyManager().getCommentTopicType(), true );
			String oldComment = occ.getData();
			if (oldComment == null) {
				oldComment = "";
			}
			if (comment.equals(oldComment)) return;
			occ.setData( comment );
			this.incVersion();
			this.getOntologyManager().fireConceptCommentChanged( this, setter, oldComment );
		}
		catch (Exception e) {
			throw new OntologyManagerException( "Error setting association comment: " + e );
		}
	}


	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 getRating(User user) {
		if (this.getOntologyManager().getRatingManager() != null) {
			return this.getOntologyManager().getRatingManager().getRating( this, user );
		}
		else {
			return null;
		}
	}

	
	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 User getCreator() {
		if (this.creator == null) {
			this.creator = this.getOntologyManager().getCreator( this.getWrappedAssociation() );
		}
		return this.creator;
	}


	public int compareTo(Object other) {
		return this.getName().toLowerCase().compareTo( ((AssociationImpl)other).getName().toLowerCase() );
	}


	public String toString() {
		String s = this.getName();
		return "Association " + (s.length() > 0 ? ("\"" + s + "\""): "(unnamed)") + ", id=" + this.getId();
	}

}
