package jp.riken.brain.ni.samuraigraph.base;

/**
 * The axis with the range and the scale type.
 */
public class SGAxis {

	/**
	 * A constant for the linear scale.
	 */
	public static final int LINEAR_SCALE = 0;

	/**
	 * A constant for the log scale.
	 */
	public static final int LOG_SCALE = 1;

	// The type of scale.
	private int mScaleType = LINEAR_SCALE;

	// The minimum value.
	private double mMinValue;

	// The maximum value.
	private double mMaxValue;

	// The flag for invert coordinates
	private boolean mInvertCoordinates = false;

	/**
	 * Creates an axis object with the given minimum and maximum value.
	 * The scale is linear type by default.
	 * The minValue must be smaller than maxValue.
	 * @param minValue - the minumum value
	 * @param maxValue - the maximum value
	 */
	public SGAxis(final double minValue, final double maxValue) {
		this(minValue, maxValue, LINEAR_SCALE);
	}

	/**
	 * Creates an axis object with the given minimum and maximum value.
	 * The scale is linear type by default.
	 * The minValue must be smaller than maxValue.
	 * @param minValue - the minumum value
	 * @param maxValue - the maximum value
	 * @param type - scale type
	 */
	public SGAxis(final double minValue, final double maxValue, final int type) {
		super();
		this.setRange(minValue, maxValue);
		this.setScaleType(type);
	}

	/**
	 * Creates an axis object with the given range.
	 * The scale is linear type by default.
	 * @param range - range of the axis
	 */
	public SGAxis(final SGTuple2d range) {
		this(range, LINEAR_SCALE);
	}

	/**
	 * Creates an axis object with the given range.
	 * @param range - range of the axis
	 * @param type - scale type
	 */
	public SGAxis(final SGTuple2d range, final int type) {
		super();
		this.setRange(range);
		this.setScaleType(type);
	}

	/**
	 * Set the axis range.
	 * @param minValue - the minumum value
	 * @param maxValue - the maximum value
	 * @throws IllegalArgumentException
	 * @return
	 */
	public boolean setRange(final double minValue, final double maxValue) {
		if (minValue >= maxValue) {
			new IllegalArgumentException("minValue>=maxValue"); //$NON-NLS-1$
		}

		// nonpositive value is not accepted in the log scale
		if (this.getScaleType() == LOG_SCALE && minValue <= 0.0) {
			new IllegalArgumentException(
			"this.getScaleType()==LOG_SCALE && minValue<=0.0"); //$NON-NLS-1$
		}

		// set to the attribute
		this.mMinValue = minValue;
		this.mMaxValue = maxValue;

		return true;
	}

	/**
	 * Set the axis range.
	 * @param range - axis range
	 * @throws NullPointerException
	 */
	public boolean setRange(final SGTuple2d range) {
		if (range == null) {
			throw new NullPointerException("range==null"); //$NON-NLS-1$
		}
		return this.setRange(range.x, range.y);
	}

	/**
	 * Set the invert coordinates flag.
	 * @param b - The invert coordinates flag.
	 * @return true:succeeded, false:failed
	 */
	public boolean setInvertCoordinates(final boolean b) {
		this.mInvertCoordinates = b;
		return true;
	}

	/**
	 * Set the scale type.
	 * @param scaleType - The scale type.
	 * @throws IllegalArgumentException - 
	 * @return true:succeeded, false:failed
	 */
	public boolean setScaleType(final int scaleType) {
		// for the wrong input value
		if (scaleType != LINEAR_SCALE && scaleType != LOG_SCALE) {
			new IllegalArgumentException(
			"scaleType!=LINEAR_SCALE && scaleType!=LOG_SCALE"); //$NON-NLS-1$
		}

		// nonpositive value is not accepted in the log scale
		if (scaleType == LOG_SCALE && this.getMinValue() <= 0.0) {
			new IllegalArgumentException(
			"scaleType==LOG_SCALE && this.getMinValue()<=0.0"); //$NON-NLS-1$
		}

		// set to attribute
		this.mScaleType = scaleType;

		return true;
	}

	/**
	 * Returns the minimum value.
	 * @return the minimum value of this axis.
	 */
	public double getMinValue() {
		return this.mMinValue;
	}

	/**
	 * Returns the maximum value.
	 * @return the maximum value of this axis.
	 */
	public double getMaxValue() {
		return this.mMaxValue;
	}

	/**
	 * Returns the invert coordinate flag.
	 * @return the invert coordinate flag.
	 */
	public boolean isInvertCoordinates() {
		return this.mInvertCoordinates;
	}

	/**
	 * Returns the range.
	 * @return the range of this axis.
	 */
	public SGTuple2d getRange() {
		return new SGTuple2d(this.mMinValue, this.mMaxValue);
	}

	/**
	 * Returns the scale type.
	 * @return the scale type of this axis.
	 */
	public int getScaleType() {
		return this.mScaleType;
	}

	/**
	 * Returns whether the given value is inside the range.
	 * @param value - the value to be checked
	 * @return whether the given value is inside the range.
	 */
	public boolean insideRange(final double value) {
		return (this.mMinValue <= value && value <= this.mMaxValue);
	}

	/**
	 * Returns whether the given value is "valid" in the present axis.
	 * @param value - the value to be checked
	 * @return whether the given value is "valid" in the present axis.
	 */
	public boolean isValidValue(final double value) {
		final int type = this.getScaleType();

		// negative value is "invalid" in the log scale
		if (type == SGAxis.LOG_SCALE) {
			if (value <= 0.0) {
				return false;
			}
		}

		return true;
	}

}
