package owltools.gaf.rules;

import java.util.HashSet;
import java.util.Set;

import org.semanticweb.owlapi.model.OWLNamedObject;
import org.semanticweb.owlapi.model.OWLObject;

import owltools.gaf.GeneAnnotation;
import owltools.graph.OWLGraphEdge;
import owltools.graph.OWLGraphWrapper;
import owltools.graph.OWLQuantifiedProperty;

/**
 * Checks if an annotation is valid according to taxon constraints.
 * 
 * @author cjm
 */
public class AnnotationTaxonRule extends AbstractAnnotationRule {
	
	private final OWLGraphWrapper graph;
	private final OWLObject rNever;
	private final OWLObject rOnly;
	
	/**
	 * @param graph The {@link OWLGraphWrapper} with merged-in taxon constraints
	 * @param neverId
	 * @param onlyId 
	 */
	public AnnotationTaxonRule(OWLGraphWrapper graph, String neverId, String onlyId) {
		
		if(graph == null){
			throw new IllegalArgumentException("OWLGraphWrapper may not be null");
		}
		this.graph = graph;
		rNever = graph.getOWLObjectPropertyByIdentifier(neverId);
		rOnly = graph.getOWLObjectPropertyByIdentifier(onlyId);
	}

	@Override
	public Set<AnnotationRuleViolation> getRuleViolations(GeneAnnotation a) {
		return _getRuleViolations(a.getCls(), a.getBioentityObject().getNcbiTaxonId(),a);
	}

	public boolean check(String annotationCls, String taxonCls) {
		return getRuleViolations(annotationCls, taxonCls).size() == 0;
	
	}

	public Set<AnnotationRuleViolation> getRuleViolations(String annotationCls, String taxonCls) {
		return _getRuleViolations(annotationCls, taxonCls,null);
	}
	
	private Set<AnnotationRuleViolation> _getRuleViolations(String annotationCls, String taxonCls, GeneAnnotation a) {
		Set<AnnotationRuleViolation> violations = new HashSet<AnnotationRuleViolation>();
	
		if(taxonCls == null){
			AnnotationRuleViolation v = new AnnotationRuleViolation(getRuleId(), "Taxon id is null", a);
			violations.add(v);
			return violations;
		}
		
		OWLObject cls = graph.getOWLObjectByIdentifier(annotationCls);
		OWLObject tax = graph.getOWLObjectByIdentifier(taxonCls);
		
		
		Set<OWLGraphEdge> edges = graph.getOutgoingEdgesClosure(cls);

		for (OWLGraphEdge ge : edges) {
			OWLObject tgt = ge.getTarget();
			if (!(tgt instanceof OWLNamedObject))
				continue;
			OWLObject p = graph.getOWLClass(tgt);
			if (p == null) {
				continue;
			}
			OWLQuantifiedProperty qp = ge.getLastQuantifiedProperty();
			if (qp.isQuantified() && qp.getProperty().equals(rOnly)) {
				// ONLY
				if (!graph.getAncestorsReflexive(tax).contains(p)) {
					StringBuilder sb = new StringBuilder();
					sb.append("The term ");
					renderEntity(sb, cls, graph);
					sb.append("requires the taxon ");
					renderEntity(sb, p, graph);
					sb.append(", but ");
					renderEntity(sb, tax, graph);
					sb.append(" does not match this constraint");
					// System.out.println("   "+sb);
					
					AnnotationRuleViolation v = new AnnotationRuleViolation(getRuleId(), sb.toString(), a);
					violations.add(v);
				}
			}
			else if (qp.isQuantified() && qp.getProperty().equals(rNever)) {
				// NEVER
				if (graph.getAncestorsReflexive(tax).contains(p)) {
					StringBuilder sb = new StringBuilder();
					sb.append("The term ");
					renderEntity(sb, cls, graph);
					sb.append(" may not be used with ");
					renderEntity(sb, tax, graph);
					sb.append(" reason: excluded ancestor ");
					renderEntity(sb, p, graph);
					AnnotationRuleViolation v = new AnnotationRuleViolation(getRuleId(), sb.toString(), a);
					violations.add(v);
				}
			}
		}
		return violations;	
	}
	
	private void renderEntity(StringBuilder sb, OWLObject o, OWLGraphWrapper graph) {
		sb.append(graph.getIdentifier(o));
		String label = graph.getLabel(o);
		if (label != null) {
			sb.append(" '");
			sb.append(label);
			sb.append('\'');
		}
	}
}


@@ -22,16 +22,46 @@
 rules;%0A%0A
+import java.util.Collections;%0A
 import j
@@ -237,16 +237,81 @@
 tation;%0A
+import owltools.gaf.rules.AnnotationRuleViolation.ViolationType;%0A
 import o
@@ -1737,92 +1737,9 @@
 %7B%0A%09%09
-Set%3CAnnotationRuleViolation%3E violations = new HashSet%3CAnnotationRuleViolation%3E();%0A%09
 %0A
+
 %09%09if
@@ -1861,47 +1861,39 @@
 %0A%09%09%09
-violations.add(v);%0A%09%09%09return violations
+return Collections.singleton(v)
 ;%0A%09%09
@@ -2025,18 +2025,540 @@
 ls);%0A%09%09%0A
-
 %09%09
+if (cls == null) %7B%0A%09%09%09AnnotationRuleViolation v = new AnnotationRuleViolation(getRuleId(), %22Could not retrieve a class for annotationCls: %22+annotationCls, a, ViolationType.Warning);%0A%09%09%09return Collections.singleton(v);%0A%09%09%7D%0A%09%09if (tax == null) %7B%0A%09%09%09AnnotationRuleViolation v = new AnnotationRuleViolation(getRuleId(), %22Could not retrieve a class for taxonCls: %22+taxonCls, a, ViolationType.Warning);%0A%09%09%09return Collections.singleton(v);%0A%09%09%7D%0A%09%09%0A%09%09Set%3CAnnotationRuleViolation%3E violations = new HashSet%3CAnnotationRuleViolation%3E();
 %0A%09%09Set%3CO

improve handling of unknown classes in taxon check