#!/usr/bin/env ruby
# WIS artillery v0.2
#
# created by hpx, thanks!
# modified for xml WIS "upgrade" by yac
# for usage, see bottom

require 'open-uri'
require 'net/https'

OpenSSL::SSL::VERIFY_PEER = OpenSSL::SSL::VERIFY_NONE # humus nechutný

class Register
	USER_AGENT = "Mozilla/5.0 (X11; Linux x86_64; rv:5.0) Gecko/20100101 Firefox/5.0 Iceweasel/5.0"

	attr_accessor :title, :url, :user, :password, :deltas, :start_time, :debug

	def initialize (title = nil, url = nil, user = nil, password = nil,
			start_time = nil, deltas = nil, debug = false)
		 @title, @url, @user, @password, @start_time, @deltas, @debug \
		= title,  url,  user,  password,  start_time,  deltas,  debug
	end

	def url2regurl(url)
		# transform URL of a variant with id= and item= in it
		# to registration URL
		if /^([^?]+)?/ =~ url
			base = $1
			if /\bid=(\d+)/ =~ url
				id = $1
				if /\bitem=(\d+)/ =~ url
					item = $1
					return "#{base}?xml=4&id=#{id}&log_#{item}=xml"
				end
			end
		end
		return nil
	end


	def arm(title, url)
		@title = title
		@url = url2regurl(url)
		unless @url
			puts "[OMG] URL translation for #{title} FAILED, ignoring! URL: #{url}"
			return
		end
		puts "Arming \"#{@title}\": #{@url}"
		run
	end

	def run
		pid = fork do 
			timer
		end
		Process.detach(pid)
	end

	def try
		open(@url,
		     :http_basic_authentication => [@user, @password],
		     "User-Agent" => USER_AGENT
		    ) { |response| 
			response.each_line { |line|
				if line.index('lof_')
					return true
				end
			}
		}
		return false
	end

	def sleep_until (time)
		sleep(time.to_f - Time.now.to_f)
		rescue ArgumentError # negative value
	end

	def timer
		@deltas.sort!
		threads = []
		success = false

		sleep_until(@start_time - 3599.99) # in case of hour switch ...
		sleep(0.02) # would be bad, but still hopefully better than the rest;)
		sleep_until(@start_time - 5)

		for tdelta in @deltas do
			threads << Thread.new(tdelta) do |delta|
				# latter deltas would survice even the hour switch ;)
				sleep_until(@start_time + delta - 0.2)
				sleep_until(@start_time + delta)

				puts "\"#{@title}\" try at    #{delta}\t#{(Time.now - @start_time).to_f}" if @debug
				if ! success
					success = true if try
					if @debug
						if success
							puts "\"#{@title}\" succeeded at #{delta}\t#{(Time.now - @start_time).to_f}"
						else
							puts "\"#{@title}\" failed at #{delta}\t#{(Time.now - @start_time).to_f}"
						end
					end
				else
					puts "\"#{@title}\" already succeeded at #{delta}\t#{(Time.now - @start_time).to_f}" if @debug
				end

			end
		end

		threads.each {|thread| thread.join}
		if success
			puts "\"#{@title}\" succeeded!"
			return true
		else
			puts "\"#{@title}\" failed!"
			return false
		end
	end
end


# main
reg            = Register.new
reg.debug      = true
reg.deltas     = [-1 -0.5, -0.25, -0.1, -0.05, 0, 0.01,  0.05, 0.2, 0.5, 1, 3, 2, 5, 10]

reg.user       = "xkloba42"
reg.password   = "kittenz"

# examples
reg.start_time = Time.local(2011, 9, 20, 11, 12, 0)
reg.arm('IZP lab', 'https://wis.fit.vutbr.cz/FIT/st/course-sl.php.cs?id=476883&item=32946')

reg.start_time = Time.local(2011, 9, 22, 20, 2, 0)
reg.arm('ISA lab', 'https://wis.fit.vutbr.cz/FIT/st/course-sl.php.cs?id=476881&item=33323')

