content: package tim.prune.gui.colour;
  
  import java.awt.Color;
  import java.text.DateFormat;
  import java.util.Calendar;
  import java.util.HashMap;
  import java.util.TimeZone;
  
  import tim.prune.config.Config;
  import tim.prune.config.TimezoneHelper;
  import tim.prune.data.DataPoint;
  import tim.prune.data.Timestamp;
  import tim.prune.data.Track;
  import tim.prune.data.TrackInfo;
  
  /**
   * Point colourer giving a different colour to each date.
   * Uses the currently selected timezone, so the results
   * may be different when selecting a different timezone
   */
  public class DateColourer extends DiscretePointColourer
  {
  	// Doesn't really matter what format is used here, as long as dates are different
  	private static final DateFormat DEFAULT_DATE_FORMAT = DateFormat.getDateInstance();
  	// Selected timezone for deciding which date a timestamp falls on
  	private TimeZone _selectedTimezone = null;
  
  	/**
  	 * Constructor
  	 * @param inStartColour start colour of scale
  	 * @param inEndColour end colour of scale
  	 * @param inWrapLength number of unique colours before wrap
  	 * @param inWideHues true for wide mode, false for narrow
  	 */
  	public DateColourer(Color inStartColour, Color inEndColour,
  		int inWrapLength, boolean inWideHues)
  	{
  		super(inStartColour, inEndColour, inWrapLength, inWideHues);
  	}
  
  	/**
  	 * Calculate the colours for each of the points in the given track
  	 * @param inTrackInfo track info object
  	 * @param inConfig config object, used to get timezone
  	 */
  	@Override
  	public synchronized void calculateColours(TrackInfo inTrackInfo, Config inConfig)
  	{
  		// Note, this method needs to be synchronized because otherwise the
  		// Calendar objects in the different threads get confused and the
  		// wrong colours are generated.
  
  		_selectedTimezone = TimezoneHelper.getSelectedTimezone(inConfig);
  		// initialise the array to the right size
  		Track track = inTrackInfo == null ? null : inTrackInfo.getTrack();
  		final int numPoints = track == null ? 0 : track.getNumPoints();
  		init(numPoints);
  		// Make a hashmap of the already-used dates
  		HashMap<String, Integer> usedDates = new HashMap<>(20);
  		// Also store the previous one, because they're probably consecutive
  		String prevDate = null;
  		int prevIndex = -1;
  
  		// loop over track points
  		for (int i=0; i<numPoints; i++)
  		{
  			DataPoint p = track.getPoint(i);
  			if (p != null && !p.isWaypoint())
  			{
  				int dayIndex = 0; // default index 0 will be used if no date found
  				String date = getDate(p.getTimestamp());
  				if (date != null)
  				{
  					// Check if it's the previous one
  					if (date.equals(prevDate)) {
  						dayIndex = prevIndex;
  					}
  					else
  					{
  						// Look up in the hashmap to see if it's been used before
  						Integer foundIndex = usedDates.get(date);
  						if (foundIndex == null)
  						{
  							// not been used before, so add it
  							dayIndex = usedDates.size() + 1;
  							usedDates.put(date, dayIndex);
  						}
  						else
  						{
  							// found it
  							dayIndex = foundIndex;
  						}
  						// Remember what we've got for the next point
  						prevDate = date;
  						prevIndex = dayIndex;
  					}
  				}
  				// if date is null (no timestamp or invalid) then dayIndex remains 0
  				setColour(i, dayIndex);
  			}
  		}
  
  		// generate the colours needed
  		generateDiscreteColours(usedDates.size() + 1);
  	}
  
  
  	/**
  	 * Find which date (in the currently selected timezone) the given timestamp falls on
  	 * @param inTimestamp timestamp
  	 * @return String containing description of date, or null
  	 */
  	private String getDate(Timestamp inTimestamp)
  	{
  		if (inTimestamp == null || !inTimestamp.isValid()) {
  			return null;
  		}
  		Calendar cal = inTimestamp.getCalendar(null);
  		// use selected time zone, not system one
  		DEFAULT_DATE_FORMAT.setTimeZone(_selectedTimezone);
  		return DEFAULT_DATE_FORMAT.format(cal.getTime());
  	}
  }
  