/*******************************************************************************
 * Copyright (c) 2000, 2011 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.compare.rangedifferencer;

/**
 * Description of a change between two or three ranges of comparable entities.
 * <p>
 * <code>RangeDifference</code> objects are the elements of a compare result returned from
 * the <code>RangeDifferencer</code> <code>find* </code> methods.
 * Clients use these objects as they are returned from the differencer.
 * This class is not intended to be instantiated outside of the Compare framework.
 * <p>
 * Note: A range in the <code>RangeDifference</code> object is given as a start index
 * and length in terms of comparable entities. However, these entity indices and counts
 * are not necessarily character positions. For example, if an entity represents a line
 * in a document, the start index would be a line number and the count would be in lines.
 * </p>
 *
 * @see RangeDifferencer
 * @noinstantiate This class is not intended to be instantiated by clients.
 */
public class RangeDifference {

	/** Two-way change constant indicating no change. */
	public final static int NOCHANGE = 0;

	/**
	 * Two-way change constant indicating two-way change (same as
	 * <code>RIGHT</code>)
	 */
	public final static int CHANGE = 2;

	/** Three-way change constant indicating a change in both right and left. */
	public final static int CONFLICT = 1;

	/** Three-way change constant indicating a change in right. */
	public final static int RIGHT = 2;

	/** Three-way change constant indicating a change in left. */
	public final static int LEFT = 3;

	/**
	 * Three-way change constant indicating the same change in both right and
	 * left, that is only the ancestor is different.
	 */
	public final static int ANCESTOR = 4;

	/** Constant indicating an unknown change kind. */
	public final static int ERROR = 5;

	/**
	 * the kind of change: NOCHANGE, CHANGE, LEFT, RIGHT, ANCESTOR, CONFLICT,
	 * ERROR
	 *
	 * @since org.eclipse.compare.core 3.5
	 */
	protected int kind;

	/**
	 * @since org.eclipse.compare.core 3.5
	 */
	protected int leftStart;

	/**
	 * @since org.eclipse.compare.core 3.5
	 */
	protected int leftLength;

	/**
	 * @since org.eclipse.compare.core 3.5
	 */
	protected int rightStart;

	/**
	 * @since org.eclipse.compare.core 3.5
	 */
	protected int rightLength;

	/**
	 * @since org.eclipse.compare.core 3.5
	 */
	protected int ancestorStart;

	/**
	 * @since org.eclipse.compare.core 3.5
	 */
	protected int ancestorLength;

	/**
	 * Creates a new range difference with the given change kind.
	 *
	 * @param changeKind
	 *            the kind of change
	 * @since org.eclipse.compare.core 3.5
	 */
	protected RangeDifference(int changeKind) {
		this.kind = changeKind;
	}

	/**
	 * Creates a new <code>RangeDifference</code> with the given change kind and
	 * left and right ranges.
	 *
	 * @param kind
	 *            the kind of change
	 * @param rightStart
	 *            start index of entity on right side
	 * @param rightLength
	 *            number of entities on right side
	 * @param leftStart
	 *            start index of entity on left side
	 * @param leftLength
	 *            number of entities on left side
	 * @since org.eclipse.compare.core 3.5
	 */
	protected RangeDifference(int kind, int rightStart, int rightLength, int leftStart, int leftLength) {
		this.kind= kind;
		this.rightStart= rightStart;
		this.rightLength= rightLength;
		this.leftStart= leftStart;
		this.leftLength= leftLength;
	}

	/**
	 * Creates a new <code>RangeDifference</code> with the given change kind and
	 * left, right, and ancestor ranges.
	 *
	 * @param kind
	 *            the kind of change
	 * @param rightStart
	 *            start index of entity on right side
	 * @param rightLength
	 *            number of entities on right side
	 * @param leftStart
	 *            start index of entity on left side
	 * @param leftLength
	 *            number of entities on left side
	 * @param ancestorStart
	 *            start index of entity on ancestor side
	 * @param ancestorLength
	 *            number of entities on ancestor side
	 * @since org.eclipse.compare.core 3.5
	 */
	protected RangeDifference(int kind, int rightStart, int rightLength, int leftStart, int leftLength,
									int ancestorStart, int ancestorLength) {
		this(kind, rightStart, rightLength, leftStart, leftLength);
		this.ancestorStart= ancestorStart;
		this.ancestorLength= ancestorLength;
	}

	/**
	 * Returns the kind of difference.
	 *
	 * @return the kind of difference, one of
	 * <code>NOCHANGE</code>, <code>CHANGE</code>, <code>LEFT</code>, <code>RIGHT</code>,
	 * <code>ANCESTOR</code>, <code>CONFLICT</code>, <code>ERROR</code>
	 */
	public int kind() {
		return this.kind;
	}

	/**
	 * Returns the start index of the entity range on the ancestor side.
	 *
	 * @return the start index of the entity range on the ancestor side
	 */
	public int ancestorStart() {
		return this.ancestorStart;
	}

	/**
	 * Returns the number of entities on the ancestor side.
	 *
	 * @return the number of entities on the ancestor side
	 */
	public int ancestorLength() {
		return this.ancestorLength;
	}

	/**
	 * Returns the end index of the entity range on the ancestor side.
	 *
	 * @return the end index of the entity range on the ancestor side
	 */
	public int ancestorEnd() {
		return this.ancestorStart + this.ancestorLength;
	}

	/**
	 * Returns the start index of the entity range on the right side.
	 *
	 * @return the start index of the entity range on the right side
	 */
	public int rightStart() {
		return this.rightStart;
	}

	/**
	 * Returns the number of entities on the right side.
	 *
	 * @return the number of entities on the right side
	 */
	public int rightLength() {
		return this.rightLength;
	}

	/**
	 * Returns the end index of the entity range on the right side.
	 *
	 * @return the end index of the entity range on the right side
	 */
	public int rightEnd() {
		return this.rightStart + this.rightLength;
	}

	/**
	 * Returns the start index of the entity range on the left side.
	 *
	 * @return the start index of the entity range on the left side
	 */
	public int leftStart() {
		return this.leftStart;
	}

	/**
	 * Returns the number of entities on the left side.
	 *
	 * @return the number of entities on the left side
	 */
	public int leftLength() {
		return this.leftLength;
	}

	/**
	 * Returns the end index of the entity range on the left side.
	 *
	 * @return the end index of the entity range on the left side
	 */
	public int leftEnd() {
		return this.leftStart + this.leftLength;
	}

	/**
	 * Returns the maximum number of entities in the left, right, and ancestor sides of this range.
	 *
	 * @return the maximum number of entities in the left, right, and ancestor sides of this range
	 */
	public int maxLength() {
		return Math.max(this.rightLength, Math.max(this.leftLength, this.ancestorLength));
	}

	@Override
	public boolean equals(Object obj) {
		if (obj instanceof RangeDifference) {
			RangeDifference other = (RangeDifference) obj;
			return this.kind == other.kind
				&& this.leftStart == other.leftStart
				&& this.leftLength == other.leftLength
				&& this.rightStart == other.rightStart
				&& this.rightLength == other.rightLength
				&& this.ancestorStart == other.ancestorStart
				&& this.ancestorLength == other.ancestorLength;
		}
		return super.equals(obj);
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + this.kind;
		result = prime * result + this.leftStart;
		result = prime * result + this.leftLength;
		result = prime * result + this.rightStart;
		result = prime * result + this.rightLength;
		result = prime * result + this.ancestorStart;
		result = prime * result + this.ancestorLength;
		return result;
	}

	@Override
	public String toString() {
		StringBuilder buf = new StringBuilder("RangeDifference {"); //$NON-NLS-1$
		switch (this.kind) {
		case NOCHANGE:
			buf.append("NOCHANGE"); //$NON-NLS-1$
			break;
		case CHANGE:
			buf.append("CHANGE/RIGHT"); //$NON-NLS-1$
			break;
		case CONFLICT:
			buf.append("CONFLICT"); //$NON-NLS-1$
			break;
		case LEFT:
			buf.append("LEFT"); //$NON-NLS-1$
			break;
		case ERROR:
			buf.append("ERROR"); //$NON-NLS-1$
			break;
		case ANCESTOR:
			buf.append("ANCESTOR"); //$NON-NLS-1$
			break;
		default:
			break;
		}

		buf.append(", "); //$NON-NLS-1$

		buf.append("Left: " + toRangeString(this.leftStart, this.leftLength) + " Right: " + toRangeString(this.rightStart, this.rightLength)); //$NON-NLS-1$ //$NON-NLS-2$
		if (this.ancestorLength > 0 || this.ancestorStart > 0)
			buf.append(" Ancestor: " + toRangeString(this.ancestorStart, this.ancestorLength)); //$NON-NLS-1$

		buf.append("}"); //$NON-NLS-1$
		return buf.toString();
	}

	private String toRangeString(int start, int length) {
		return "(" + start + ", " + length + ")"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
	}
}

