#!/usr/bin/ruby -w
require 'irrigation'
###############################################################################
#
# Calculate and display statistics about irrigation times.
#
# Make a symbolic link (or rename this file if you aren't using a real
# operating system) from this file to cli.rb or svg.rb to alter the behavior.
# cli.rb will generate text ouptut; svg.rb will write a file called out.svg,
# which is an SVG graph of the run times and overlaps.
#
# I wrote this application because I've got a complicated irrigation system.
# I've got 4 acres of land that is irrigated off of a pond and a 3hp pump.  I
# also have a yard irrigation system that is on a different control box, but
# which gets its water from the pasture irrigation.  What this means is that I
# have to coordinate the yard system with the pasture system, because if the
# pasture system isn't running, the yard system doesn't get any water.  To
# make matters more complicated, the pasture system has an expensive Rainbird
# control box, while the yard system uses a less expensive control box with
# fewer features.  One of the missing features is a station delay -- each
# station runs immediately after the previous, with no delay.  Also, the
# pasture system allows you to set each station to run for up to 24 hours; the
# yard system has a maximum of 99 minutes per station.
#
# I got tired of trying to adjust everything by hand; I had to adjust the
# pasture run times so as not to drain the pond, and then sync up the yard
# system so that each station got the right amount of water; the amount of
# water we get into the pond varies a bit throughout the year, and it was a
# royal pain in the ass to get everything just right.  By the time the irrigation 
# came on again in the spring of 2002, I had discovered Ruby, and I decided that 
# I'd had it with trying to diagram the water usage with a spreadsheet, so I
# wrote this little application.
#
# If you use this, you'll want to change the configuration options below; they
# should be self-explainatory.  If you want to add stations or programs
# (programs are run sequences of stations that start at a given time on a
# given day -- IE, you can have a program A that runs Monday, Wednesday, and
# Friday starting at 18:00, and a program B that runs Tuesday and Thursday
# starting at 12:00, for instance), you'll need to hack this script, or email
# me and ask me to do it for you.

#################### CONFIGURATION OPTIONS
START_HOUR = 18
START_MINUTE = 45
PASTURE_DELAY = 78
# *_run_times is the number of minutes each station is set to run for
pasture_run_times = [ 80, 80, 80, 80, 80, 95, 80, 95, 60 ]
# The yard system...
PROGRAM_A_START_HOUR = 18
PROGRAM_A_START_MINUTE = 45
PROGRAM_B_START_HOUR = 0
PROGRAM_B_START_MINUTE = 30
yard_run_times_a = [ 35, 75, 80, 10, 45 ]
yard_run_times_b = [ 60, 90, 70, 85, 45 ]
# *_days_* is the number of days per week the program runs
yard_days_a = 7
yard_days_b = 2
############ END OF CONFIGURATION OPTIONS ###################3

START = 60*START_HOUR + START_MINUTE
pasture_run = Run.new( PASTURE_DELAY, START )
yard_run_a = Run.new( 0, 60*PROGRAM_A_START_HOUR + PROGRAM_A_START_MINUTE )
yard_run_b = Run.new( 0, 60*PROGRAM_B_START_HOUR + PROGRAM_B_START_MINUTE )

if $0 =~ /cli/
	def write title, stations, base_stations = nil
		print "\n#{title}\tRun\tStart\tEnd"
		print "\tWater" if base_stations
		puts
		base = nil
		stations.each do |station|
			base = base_stations.find{|s| s.overlaps? station } if base_stations
			str = "Station #"
			str << "%2d"%(station.number)
			str << "\t(#{station.run_time})"
			str << "\t"+time(station.start)+"\t"+time(station.finish)
			if base
				str << "\t"
				str << station.overlap(base).to_s
			end
			puts str
		end
	end

	def write_table(*args)
		args.each{|e| print e+"\t"}
		puts
	end
elsif $0 =~ /svg/
	require 'rexml/document'
	@doc = REXML::Document.new
	@doc << REXML::XMLDecl.new
	@doc << REXML::DocType.new( "svg", 'PUBLIC "-//W3C//DTD SVG August 1999//EN" "http://www.w3.org/Graphics/SVG/SVG-19990812.dtd"' )
	svg = @doc.add_element "svg"
	svg.attributes['style'] = 'text-anchor: middle; opacity: 1;'
	svg.attributes['width'] = '1300'
	svg.attributes['height'] = '313'
	svg.attributes['viewBox'] = '0 0 1660 400'
	@y_offset = 20
	@x_offset = 200

	time_group = svg.root.add_element 'g'
	time_group.attributes['transform'] = "translate(#@x_offset,40)"
	time_group.attributes['style'] = 'stroke: #000000;'
	0.upto 288 do |x|
		len = '-5'
		len = '-7' if x % 6 == 0
		if x % 12 == 0
			len = '-10'
			time_text = time_group.add_element 'text'
			time_text.attributes['x'] = (x*5).to_s
			time_text.attributes['y'] = '-12'
			time_text.attributes['style'] = 'stroke: none'
			if x < 72
				time_text.text = (x/12+18).to_s
			else
				time_text.text = (x/12-6).to_s
			end
		end
		line = time_group.add_element 'line'
		line.attributes['x1'] = (x*5).to_s
		line.attributes['y1'] = '0'
		line.attributes['x2'] = (x*5).to_s
		line.attributes['y2'] = len
	end

	# Draw ticks and hours
	def write title, stations, base_stations = nil
		@y_offset += 30
		station_group = @doc.root.add_element 'g'
		station_group.attributes['title'] = title
		station_group.attributes['transform'] = "translate(0, #@y_offset)"
		station_text = station_group.add_element 'text'
		station_text.attributes['x'] = '5'
		station_text.attributes['y'] = '16'
		station_text.attributes['style'] = 'text-anchor: start'
		station_text.text = title[0..10]
		if base_stations
			rect_style = 'fill: #00FFFF; stroke: #000000; opacity: 0.7'
		else
			rect_style = 'fill: #00FF00; stroke: #000000' 
		end
		stations.each do |station|
			group = station_group.add_element "g"
			if base_stations
				base = base_stations.find {|sta| sta.overlaps? station}
				# Watering rect
				wrect = group.add_element 'rect'
				if base.start > station.start
					start = base.start - station.start
				else
					start = 0
				end
				wrect.attributes['x'] = start.to_s
				wrect.attributes['y'] = '15'
				wrect.attributes['width'] = station.overlap( base ).to_s
				wrect.attributes['height'] = '5'
				wrect.attributes['style'] = 'fill: #FF0000'
			end
			# Time rect
			rect = group.add_element "rect"
			rect.attributes['x'] = '0'
			rect.attributes['y'] = '15'
			rect.attributes['width'] = station.run_time.to_s
			rect.attributes['height'] = '5'
			rect.attributes['style'] = rect_style
			# Station number
			text = group.add_element 'text'
			text.attributes['x'] = (station.run_time/2).to_s
			text.attributes['y'] = '12'
			text.text = station.number.to_s

			if station.start >= 1080
				xoff = station.start - 1080
			else
				xoff = 360 + station.start
			end
			xoff += @x_offset
			group.attributes['transform'] = "translate(#{xoff},0)"
		end
	end

	def write_table(*args)
		@y_offset += 30
		@y_offset += 20 if args.size == 1
		xoff = 0
		args.each{|e| 
			text = @doc.root.add_element 'text'
			text.attributes['x'] = (xoff + @x_offset).to_s
			text.attributes['y'] = @y_offset.to_s
			text.attributes['style'] = 'text-anchor: start'
			text.text = e
			xoff += 150
		}
	end
else
	puts "USAGE: #$0"
	exit
end

def build run, times
	stations = []
	times.each do |length|
		stations << Station.new( run, length )
		stations[-1].preceeding = stations[-2] if stations[-2]
	end
	stations
end

# PASTURE
pasture_stations = build( pasture_run, pasture_run_times )
write "Pasture", pasture_stations

# YARD A
yard_stations_a = build( yard_run_a, yard_run_times_a )
write "Yard A (#{yard_days_a})", yard_stations_a, pasture_stations

# YARD B
yard_stations_b = build( yard_run_b, yard_run_times_b )
write "Yard B (#{yard_days_b})", yard_stations_b, pasture_stations

# Do totals for yard
yard_station_totals = [0,0,0,0,0]
[[yard_stations_a, yard_days_a], [yard_stations_b, yard_days_b]].each do |arry,days|
	arry.each do |station|
		base = pasture_stations.find{|s| s.overlaps? station }
		yard_station_totals[station.number-1] += (station.overlap(base)*days)
	end
end
write_table "Yard Watering Time Totals"
yard_station_totals.size.times do |id|
	write_table "Station ##{id+1}",time(yard_station_totals[id])
end

if defined? @doc and !@doc.nil?
	File.open("out.svg", 'w+') {|f| @doc.write f, 0}
end
