package tim.prune.gui.colour;

import java.awt.Color;

/**
 * Abstract class to do the discrete colouring of points,
 * using start and end colours and a wrapping index
 */
public abstract class DiscretePointColourer extends PointColourer
{
	/** array of discrete colours to use */
	private Color[] _discreteColours = null;
	/** array of colour indexes */
	private int[] _colourIndexes = null;


	/**
	 * Constructor
	 * @param inStartColour start colour of scale
	 * @param inEndColour end colour of scale
	 * @param inMaxColours number of unique colours before wrap
	 * @param inWideHues true for wide mode, false for narrow
	 */
	public DiscretePointColourer(Color inStartColour, Color inEndColour,
		int inMaxColours, boolean inWideHues)
	{
		super(inStartColour, inEndColour, inMaxColours, inWideHues);
	}

	/**
	 * Initialise the array to the right size
	 * @param inNumPoints number of points in the track
	 */
	protected void init(int inNumPoints)
	{
		if (_colourIndexes == null || _colourIndexes.length != inNumPoints)
		{
			// Array needs to be created or resized
			if (inNumPoints > 0) {
				_colourIndexes = new int[inNumPoints];
			}
			else {
				_colourIndexes = null;
			}
		}
	}

	/**
	 * Set the colour at the given index
	 * @param inPointIndex point index
	 * @param inColourIndex index of colour to use
	 */
	protected void setColour(int inPointIndex, int inColourIndex)
	{
		if (_colourIndexes != null && _colourIndexes.length > inPointIndex && inPointIndex >= 0)
		{
			_colourIndexes[inPointIndex] = inColourIndex;
		}
	}

	/**
	 * Get the colour for the given point index
	 * @param inPointIndex index of point in track
	 * @return colour object
	 */
	public Color getColour(int inPointIndex)
	{
		if (_colourIndexes != null && _colourIndexes.length > inPointIndex && inPointIndex >= 0 && getMaxColours() > 0)
		{
			int colourIndex = _colourIndexes[inPointIndex] % getMaxColours();
			if (colourIndex >= 0 && _discreteColours != null && colourIndex < _discreteColours.length) {
				return _discreteColours[colourIndex];
			}
		}
		// not found, use default
		return super.getDefaultColour();
	}

	/**
	 * Generate the set of discrete colours to use
	 * @param inNumCategories number of different categories found in the data
	 */
	void generateDiscreteColours(int inNumCategories)
	{
		int maxColours = Math.max(2, getMaxColours());
		int numCategories = Math.max(1, inNumCategories);
		numCategories = Math.min(numCategories, maxColours);

		// Use this number of categories to generate the colours
		_discreteColours = new Color[numCategories];
		for (int i=0; i<numCategories; i++) {
			_discreteColours[i] = mixColour(i, numCategories);
		}
	}

	/**
	 * Mix the given colours together by interpolating H,S,B values
	 * @param inIndex index from 0 to inWrap-1
	 * @param inWrap wrap length
	 * @return mixed colour
	 */
	private Color mixColour(int inIndex, int inWrap)
	{
		float fraction = inWrap < 2 ? 0.0f : (float) inIndex / (float) (inWrap - 1);
		return mixColour(fraction);
	}

	/**
	 * @param inIndex specified colour index
	 * @return precalculated colour at the given index
	 */
	protected Color getDiscreteColour(int inIndex)
	{
		if (_discreteColours == null || inIndex < 0 || getMaxColours() <= 1) {
			return getDefaultColour();
		}
		return _discreteColours[inIndex % getMaxColours()];
	}

	public double getColourIndex(int inPointIndex)
	{
		if (_colourIndexes != null && _colourIndexes.length > inPointIndex && inPointIndex >= 0 && getMaxColours() > 0)
		{
			final int colourIndex = _colourIndexes[inPointIndex] % getMaxColours();
			final int numDiscreteColours = _discreteColours.length;
			double result = colourIndex * 1.0 / (numDiscreteColours - 1);
			return result;
		}
		return 0.0;
	}

	/** @return the number of distinct colours being used */
	public int getNumDistinctColours() {
		return (_discreteColours == null ? 1 : _discreteColours.length);
	}

	public Color getColourForColourIndex(double inColourIndex)
	{
		final int numDiscreteColours = _discreteColours.length;
		final double colourIndex = Math.round(inColourIndex * (numDiscreteColours - 1));
		return _discreteColours[(int) colourIndex];
	}
}
