#!/usr/bin/ruby

# This represents a watering station.  A watering station is basically just a
# run time -- how many minutes the station runs.  However, the start and end
# times of a station's run are valuable pieces of information, and given
# certain other pieces of information, the start and end times can be derived.
# Since stations run in a chain, any given station has a preceeding and a
# following station, except for the first and last stations.  The whole
# environment can have a starting time and an amount of delay between each
# station (this is common behavior for Rainbird controllors).
class Station
	attr_accessor :run, :run_time
	attr_reader :preceeding, :following

	# Create a new station, given a running environment and a run length.  The
	# first argument must be a Run object, and the second argument is the number
	# of minutes the station runs.  The preceeding station should also be set
	# after initialization:
	#  # Create a new run environment, with a 30 minute delay between stations
	#  # and a start time of 6:30 AM ( 60 * hour + minute = total_minutes )
	#  run = Run.new( 30, 60 * 6 + 30 )
	#  station_0 = Station.new(run, 30)  # station 0 runs for 30 minutes
	#  station_1 = Station.new(run, 90)  # station 1 runs for 90 minutes
	#  station_1.previous = station_0    # station 1 follows station 0
	def initialize run, run_time
		@run = run
		@run_time = run_time
		@preceeding = @following = nil
	end

	# Get the time that the station starts.  This is in minutes since midnight.
	#  station_0.start  # => 390, or 6:30 AM
	def start
		if @preceeding.nil?
			@run.start
		else
			(@preceeding.finish + @run.delay) % 1440
		end
	end

	# Get the time that the station turns off.  This is in minutes since
	# midnight.
	#  station_0.finish  # => 420, or 7:00 AM
	def finish
		(start + @run_time) % 1440
	end

	# Set the preceeding station.  Sets the preceeding station's following to
	# self.  You only need to call a.preceeding(b) or b.following(a), not both. 
	def preceeding= prev
		@preceeding = prev
		@preceeding.following = self unless @preceeding.following == self
	end

	# Set the following station.  Sets the following station's preceeding to
	# self.  You only need to call a.preceeding(b) or b.following(a), not both. 
	def following= nxt
		@following = nxt
		@following.preceeding = self unless @following.preceeding == self
	end

	# The station number, counting from 1.
	#  station_0.number  # => 1
	#  station_1.number  # => 2
	def number
		if @preceeding
			@preceeding.number + 1
		else
			1
		end
	end

	# Returns true if the given time occurs during this station's run time.  The
	# time given should be in minutes since midnight.
	def contains? time
		start <= time and time <= finish
	end

	# Returns true if the given station's run time overlaps with this station's
	# run time
	def overlaps? station
		not (finish <= station.start or start >= station.finish)
	end

	# Returns the amount of time by which two stations overlap, in minutes.
	def overlap base
		max_start = [base.start, start].max
		min_finish = [base.finish, finish].min
		run = min_finish-max_start
		while base.following and base.following.overlaps? self
			run += overlap( base.following )
			base = base.following
		end
		run
	end
end

# A Run is a start time and a delay between stations, and is a required part
# of a Station's environment.  Only one of these should be created per
# program.
Run = Struct.new( "Run", :delay, :start )

# Turns an Integer value of minutes since midnight into a String
# representation of the time.
#  time 420   # => "7:00"
#  time 880   # => "14:40"
def time val
	hour = (val / 60)
	minute = val-(hour*60)
	#hour = hour % 24
	hour.to_s + ":%02d"%minute
end
