package com.taxonic.carml.model.impl;

import com.google.common.collect.ImmutableSet;
import com.taxonic.carml.model.LogicalSource;
import com.taxonic.carml.model.Resource;
import com.taxonic.carml.rdf_mapper.annotations.RdfProperty;
import com.taxonic.carml.vocab.Rdf;
import com.taxonic.carml.vocab.Rml;
import java.util.Objects;
import java.util.Set;
import org.apache.commons.lang3.builder.MultilineRecursiveToStringStyle;
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
import org.eclipse.rdf4j.model.IRI;
import org.eclipse.rdf4j.model.util.ModelBuilder;
import org.eclipse.rdf4j.model.vocabulary.RDF;

public class CarmlLogicalSource extends CarmlResource implements LogicalSource {

	private Object source;
	private String iterator;
	private IRI referenceFormulation;

	public CarmlLogicalSource() {
		// Empty constructor for object mapper
	}

	public CarmlLogicalSource(
		Object source,
		String iterator,
		IRI referenceFormulation
	) {
		this.source = source;
		this.iterator = iterator;
		this.referenceFormulation = referenceFormulation;
	}

	@RdfProperty(
		value = Rml.source,
		handler = LogicalSourceSourcePropertyHandler.class
	)
	@Override
	public Object getSource() {
		return source;
	}

	@RdfProperty(Rml.iterator)
	@Override
	public String getIterator() {
		return iterator;
	}

	@RdfProperty(Rml.referenceFormulation)
	@Override
	public IRI getReferenceFormulation() {
		return referenceFormulation;
	}

	public void setSource(Object source) {
		this.source = source;
	}

	public void setIterator(String iterator) {
		this.iterator = iterator;
	}

	public void setReferenceFormulation(IRI referenceFormulation) {
		this.referenceFormulation = referenceFormulation;
	}

	@Override
	public String toString() {
		return new ReflectionToStringBuilder(this, new MultilineRecursiveToStringStyle()).toString();
	}

	@Override
	public int hashCode() {
		return Objects.hash(source, iterator, referenceFormulation);
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj) {
			return true;
		}
		if (obj == null) {
			return false;
		}
		if (getClass() != obj.getClass()) {
			return false;
		}
		CarmlLogicalSource other = (CarmlLogicalSource) obj;
		return Objects.equals(source, other.source) &&
				Objects.equals(iterator, other.iterator) &&
				Objects.equals(referenceFormulation, other.referenceFormulation);
	}

	@Override
	public Set<Resource> getReferencedResources() {
		if (source != null && source instanceof Resource) {
			return ImmutableSet.of((Resource) source);
		} else {
			return ImmutableSet.of();
		}
	}

	@Override
	public void addTriples(ModelBuilder modelBuilder) {
		modelBuilder.subject(getAsResource())
				.add(RDF.TYPE, Rdf.Rml.LogicalSource);
		if (source != null) {
			if (source instanceof Resource) {
				modelBuilder.add(Rml.source, ((Resource) source).getAsResource());
			} else {
				modelBuilder.add(Rml.source, source);
			}
		}
		if (iterator != null) {
			modelBuilder.add(Rml.iterator, iterator);
		}
		if (referenceFormulation != null) {
			modelBuilder.add(Rml.referenceFormulation, referenceFormulation);
		}
	}

	public static Builder newBuilder() {
		return new Builder();
	}

	public static class Builder {

		private Object source;
		private String iterator;
		private IRI referenceFormulation;

		public Builder source(Object source) {
			this.source = source;
			return this;
		}

		public Builder iterator(String iterator) {
			this.iterator = iterator;
			return this;
		}

		public Builder referenceFormulation(IRI referenceFormulation) {
			this.referenceFormulation = referenceFormulation;
			return this;
		}

		public CarmlLogicalSource build() {
			return new CarmlLogicalSource(
				source,
				iterator,
				referenceFormulation
			);
		}
	}
}
