/*
 * Copyright (C) 2006  Mike Murray
 *
 *  All rights reserved.
 *  This document may not be reused without
 *  express written permission from Mike Murray.
 */

package net.sf.j3d.physics.pfield;

import java.util.Iterator;
import java.util.Set;
import java.util.HashSet;

import net.sf.j3d.physics.pfield.util.PhysicalConstants;

/**
 * A Clock object keeps track of time (usually measured in microseconds).
 * The clock keeps track of a set of photon fields that will be notified
 * when a clock tick occurs.
 * 
 * @author Mike Murray
 */
public class Clock {
	private Set fields;
	private long ticks;
	private double interval = Math.pow(10.0, -9.0);
	
	/**
	 * Constructs a new Clock object.
	 */
	public Clock() { this.fields = new HashSet(); }
	
	/**
	 * Adds the specified PhotonField instance to this clock. The photon field
	 * will be notified when a clock tick occurs.
	 * 
	 * @param f  PhotonField instance to add.
	 * @return  The total number of photon fields stored by this Clock object.
	 */
	public int addPhotonField(PhotonField f) {
		this.fields.add(f);
		f.setClock(this);
		return this.fields.size();
	}
	
	/**
	 * Removes the specified PhotonField instance from this clock. The photon field
	 * will no longer be notified when a clock tick occurs.
	 * 
	 * @param f  PhotonField instance to remove.
	 * @return  True if the photon field was removed, false otherwise.
	 */
	public boolean removePhotonField(PhotonField f) { return this.fields.remove(f); }
	
	/**
	 * @return  The Set used by this clock to store PhotonField objects.
	 */
	public Set getPhotonFields() { return this.fields; }
	
	/**
	 * @param time  The time for this clock (usually in microseconds).
	 */
	public void setTime(double time) { this.ticks = (long) (time / this.interval); }
	
	/**
	 * @return  The time for this clock (usually in microseconds).
	 */
	public double getTime() { return this.ticks * this.interval; }
	
	/**
	 * @param tick  The interval for one tick of this clock (usually in microseconds).
	 */
	public void setTickInterval(double tick) { this.interval = tick; }
	
	/**
	 * @return  The interval for one tick of this clock (usually in microseconds).
	 */
	public double getTickInterval() { return this.interval; }
	
	/**
	 * @return  The distance traveled by a photon in one tick of this clock,
	 *          usually measured in micrometers. (The same scale of the tick interval
	 *          time is adopted because the speed of light is a ratio of meters and
	 *          seconds. IE, when the tick interval is measured in microseconds,
	 *          multiply the tick interval by the speed of light yields micrometers.
	 *          If the tick interval where measured in another unit, the tick distance
	 *          will be measured in a distance unit carrying the same prefix. Nanoseconds
	 *          yields nanometers, picoseconds yields picometers, milliseconds yields
	 *          millimeters, etc.)
	 */
	public double getTickDistance() { return this.interval * PhysicalConstants.C; }
	
	/**
	 * @return  The number of ticks since the start of this clock. This number is usually
	 *          not as useful as the actual time of the clock (see getTime). Be careful
	 *          when make assumptions about what a "tick" is. It is not a good idea to
	 *          write code (or XML source) that depends on something without a physical
	 *          counterpart.
	 */
	public long getTicks() { return this.ticks; }
	
	/**
	 * Increments the time of this clock by one tick and notifies all photon fields
	 * of the tick event.
	 * 
	 * @return  The total time on the clock after the tick.
	 */
	public double tick() {
		this.ticks++;
		
		Iterator itr = this.fields.iterator();
		while (itr.hasNext()) ((PhotonField)itr.next()).tick(this.interval);
		
		return this.getTime();
	}
}
