package it.unibz.inf.ontop.spec.ontology.impl;

import com.google.common.base.Objects;
import it.unibz.inf.ontop.model.term.IRIConstant;
import it.unibz.inf.ontop.model.term.ObjectConstant;
import it.unibz.inf.ontop.model.term.RDFConstant;
import it.unibz.inf.ontop.model.vocabulary.RDF;
import it.unibz.inf.ontop.spec.ontology.RDFFact;

import javax.annotation.Nullable;
import java.util.Optional;

public class RDFFactImpl implements RDFFact {

    private final ObjectConstant subject;
    private final IRIConstant property;
    private final RDFConstant object;

    @Nullable
    private final ObjectConstant graph;
    private final ObjectConstant classOrProperty;

    /**
     * Quad
     */
    private RDFFactImpl(ObjectConstant subject, IRIConstant property, RDFConstant object, ObjectConstant graph) {
        this.subject = subject;
        this.property = property;
        this.object = object;
        this.graph = graph;
        this.classOrProperty = extractClassOrProperty(property, object);
    }

    /**
     * Triple
     */
    private RDFFactImpl(ObjectConstant subject, IRIConstant property, RDFConstant object) {
        this(subject, property, object, null);
    }

    public static RDFFact createQuadFact(ObjectConstant subject, IRIConstant property, RDFConstant object, ObjectConstant graph) {
        return new RDFFactImpl(subject, property, object, graph);
    }

    public static RDFFact createTripleFact(ObjectConstant subject, IRIConstant property, RDFConstant object) {
        return new RDFFactImpl(subject, property, object);
    }


    private static ObjectConstant extractClassOrProperty(IRIConstant property, RDFConstant object) {
        if (property.getIRI().equals(RDF.TYPE)) {
            if (object instanceof ObjectConstant)
                return (ObjectConstant) object;
            else
                throw new IllegalArgumentException("The class must be an IRI or a b-node");
        }
        else
            return property;
    }

    @Override
    public ObjectConstant getSubject() {
        return subject;
    }

    @Override
    public IRIConstant getProperty() {
        return property;
    }

    @Override
    public RDFConstant getObject() {
        return object;
    }

    @Override
    public Optional<ObjectConstant> getGraph() {
        return Optional.ofNullable(graph);
    }

    @Override
    public ObjectConstant getClassOrProperty() {
        return classOrProperty;
    }

    @Override
    public boolean isClassAssertion() {
        return !getClassOrProperty().equals(property);
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof RDFFact)) return false;
        RDFFact rdfFact = (RDFFact) o;
        return Objects.equal(getSubject(), rdfFact.getSubject()) &&
                Objects.equal(getProperty(), rdfFact.getProperty()) &&
                Objects.equal(getObject(), rdfFact.getObject()) &&
                Objects.equal(getGraph(), rdfFact.getGraph()) &&
                Objects.equal(getClassOrProperty(), rdfFact.getClassOrProperty());
    }

    @Override
    public int hashCode() {
        return Objects.hashCode(getSubject(), getProperty(), getObject(), getGraph(), getClassOrProperty());
    }

    @Override
    public String toString() {
        return "RDFFact{" +
                "subject=" + subject +
                ", property=" + property +
                ", object=" + object +
                ", graph=" + graph +
                ", classOrProperty=" + classOrProperty +
                '}';
    }
}
