package worms.util;

import be.kuleuven.cs.som.annotate.Immutable;
import be.kuleuven.cs.som.annotate.Value;

import java.util.Objects;


/**
 * A class of 2-tuples or pairs.
 *
 * @param <T1> first element type
 * @param <T2> second element type
 *
 *
 * @version 1.2
 * @author Arthur Bols & Leen Dereu
 */
@Value
public class Tuple<T1, T2> {

    /**
     * The variable referencing the first item of this Tuple.
     */
    public final T1 item1;

    /**
     * The variable referencing the second item of this Tuple.
     */
    public final T2 item2;


    /**
     * Initialize this new Tuple as a Tuple with the given items.
     *
     * @param item1
     *        The first element for this tuple.
     * @param item2
     *        The second element for this tuple.
     */
    public Tuple(T1 item1, T2 item2) throws IllegalArgumentException {

        this.item1 = item1;
        this.item2 = item2;
    }

    /**
     *
     * @param   item1
     *          The value for the first element.
     * @param   item2
     *          The value for the second element.
     * @return
     *          The Tuple created from item1 and item2.
     *          | result == new Tuple<>(item1, item2);
     */
    @Immutable
    public static <T1, T2> Tuple<T1, T2> create(T1 item1, T2 item2){
        return new Tuple<>(item1, item2);
    }

    /**
     *  Return a new Tuple created from the elements of the array.
     *
     * @param   arr
     *          The Array to create the tuple of.
     * @return  The Tuple created from the elements of the given array.
     *          | result == new Tuple<>(arr[0], arr[1])
     * @throws IllegalArgumentException
     *          The given array is null or the length of the given array
     *          is not 2.
     *          | arr == null || arr.length != 2
     */
    @Immutable
    public static Tuple<Double, Double> create(double[] arr) throws IllegalArgumentException {
        if (arr == null || arr.length != 2)
            throw new IllegalArgumentException("Invalid parameter arr");
        return new Tuple<>(arr[0], arr[1]);
    }

    /**
     * Compares the Tuple to the given object.
     *
     * @param o
     *        The object to compare to.
     * @return  True if and only if the given object is this Tuple.
     *          | if (this == 0) then result == true
     *          Otherwise, true if the given object is a Tuple and
     *          the items are the same as this Tuple.
     *          | else result ==
     *          |   Objects.equals(item1, tuple.item1) &&
     *          |   Objects.equals(item2, tuple.item2);
     */
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Tuple)) return false;
        Tuple<?, ?> tuple = (Tuple<?, ?>) o;
        return Objects.equals(item1, tuple.item1) &&
                Objects.equals(item2, tuple.item2);
    }

    /**
     * Generates a hash code of the items of the Tuple
     *
     * @return The hash code of item1 and item2
     *          | result == Objects.hash(item1, item2)
     */
    @Override
    public int hashCode() {

        return Objects.hash(item1, item2);
    }
}