package eu.dnetlib.functionality.index.model.document;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

/**
 * The Class IndexField defines a common representation of field that each back-end of index should contain.
 */
public class IndexField implements Iterable<Object> {

	/** The name. */
	private String name;

	/** The value. */
	private Object value = null;

	public IndexField(final String n) {
		this.name = n;
	}

	/**
	 * Set the value for a field. Arrays will be converted to a collection. If a collection is given, then that collection will be used as
	 * the backing collection for the values.
	 * 
	 * @param v
	 *            the v
	 */
	public void setValue(final Object v) {

		if (v instanceof Object[]) {
			Object[] arr = (Object[]) v;
			Collection<Object> c = new ArrayList<Object>(arr.length);
			for (Object o : arr) {
				c.add(o);
			}
			value = c;
		} else {
			value = v;
		}
	}

	/**
	 * Add values to a field. If the added value is a collection, each value will be added individually.
	 * 
	 * @param v
	 *            the v
	 */
	@SuppressWarnings("unchecked")
	public void addValue(final Object v) {
		if (value == null) {
			if (v instanceof Collection) {
				Collection<Object> c = new ArrayList<Object>(3);
				for (Object o : (Collection<Object>) v) {
					c.add(o);
				}
				setValue(c);
			} else {
				setValue(v);
			}

			return;
		}
		Collection<Object> vals = null;
		if (value instanceof Collection) {
			vals = (Collection<Object>) value;
		} else {
			vals = new ArrayList<Object>(3);
			vals.add(value);
			value = vals;
		}

		// Add the new values to a collection
		if (v instanceof Iterable) {
			for (Object o : (Iterable<Object>) v) {
				vals.add(o);
			}
		} else if (v instanceof Object[]) {
			for (Object o : (Object[]) v) {
				vals.add(o);
			}
		} else {
			vals.add(v);
		}
	}

	/**
	 * Gets the first value.
	 * 
	 * @return the first value
	 */
	@SuppressWarnings("unchecked")
	public Object getFirstValue() {
		if (value instanceof Collection) {
			@SuppressWarnings("rawtypes")
			Collection c = (Collection<Object>) value;
			if (c.size() > 0) return c.iterator().next();
			return null;
		}
		return value;
	}

	/**
	 * Gets the value.
	 * 
	 * @return the value for this field. If the field has multiple values, this will be a collection.
	 */
	public Object getValue() {
		return value;
	}

	/**
	 * Gets the values.
	 * 
	 * @return the values for this field. This will return a collection even if the field is not multi-valued
	 */
	@SuppressWarnings("unchecked")
	public Collection<Object> getValues() {
		if (value instanceof Collection) return (Collection<Object>) value;
		if (value != null) {
			Collection<Object> vals = new ArrayList<Object>(1);
			vals.add(value);
			return vals;
		}
		return null;
	}

	/**
	 * Gets the value count.
	 * 
	 * @return the number of values for this field
	 */
	@SuppressWarnings("rawtypes")
	public int getValueCount() {
		if (value instanceof Collection) return ((Collection) value).size();
		return (value == null) ? 0 : 1;
	}

	// ---------------------------------------------------------------
	// ---------------------------------------------------------------

	/**
	 * Gets the name.
	 * 
	 * @return the name
	 */
	public String getName() {
		return name;
	}

	/**
	 * Sets the name.
	 * 
	 * @param name
	 *            the new name
	 */
	public void setName(final String name) {
		this.name = name;
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see java.lang.Iterable#iterator()
	 */
	@Override
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public Iterator<Object> iterator() {
		if (value instanceof Collection) return ((Collection) value).iterator();
		return new Iterator<Object>() {

			boolean nxt = (value != null);

			@Override
			public boolean hasNext() {
				return nxt;
			}

			@Override
			public Object next() {
				nxt = false;
				return value;
			}

			@Override
			public void remove() {
				throw new UnsupportedOperationException();
			}
		};
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		return name + value;
	}

	public IndexField deepCopy() {
		IndexField clone = new IndexField(name);

		// We can't clone here, so we rely on simple primitives
		if (value instanceof Collection) {
			@SuppressWarnings("unchecked")
			Collection<Object> values = (Collection<Object>) value;
			Collection<Object> cloneValues = new ArrayList<Object>(values.size());
			cloneValues.addAll(values);
			clone.value = cloneValues;
		} else {
			clone.value = value;
		}
		return clone;
	}

}
