module YouTube
	require 'net/http'
	require 'uri'

	class Video
		def initialize uri = nil, params = nil, download_uri = nil,
				real_download_uri = nil, body = nil
			@uri = uri
			@params = params
			@download_uri = download_uri
			@real_download_uri = real_download_uri
			@body = body
		end

		def uri
			@uri
		end

		def uri= x
			@uri = x
			@video_id = nil
			params = nil
		end

		def video_id
			if !@video_id && uri
				match = uri.match(/watch\?v=([^&]*)/)
				if match
					@id = match[1]
				else
					raise "Error parsing video ID from YouTube URI"
				end
			end
			@id
		end

		def video_id= x
			uri = "http://www.youtube.com/watch?v=#{id}"
			@video_id = x
		end

		def params
			if !@params && uri
				# Get video page
				page = Net::HTTP::get_response URI.parse(uri)
				# Raise error if fetching page failed
				page.value

				match = page.body.match(/player2\.swf\?([^"]*)/)
				if match
					@params = match[1]
				else
					raise "Could not extract parameters from video page"
				end
			end
			@params
		end

		def params= x
			@params = x
			download_uri = nil
		end

		def download_uri
			if !@download_uri && params
				@download_uri = "http://www.youtube.com/get_video?#{params}"
			end
			@download_uri
		end

		def download_uri= x
			@download_uri = x
			real_download_uri = nil
		end

		def real_download_uri
			dl_uri = download_uri
			if !@real_download_uri && dl_uri
				while true
					uri = URI.parse dl_uri
					resp = Net::HTTP::start(
							uri.host, uri.port) \
							do |http|
						http.request_head "#{uri.path}?#{uri.query}"
					end

					case resp
					when Net::HTTPSuccess
						@real_download_uri = dl_uri
						break
					when Net::HTTPRedirection
						dl_uri = resp['location']
					else
						# Raise exception
						resp.value
					end
				end
			end

			@real_download_uri
		end

		def real_download_uri= x
			@real_download_uri = x
			@body = x
		end

		def body
			if @body
				if block_given?
					i = 0
					blocksize = 1024
					while i < @body.size
						yield @body[i,blocksize]
						i = i + blocksize
					end
				end
				@body
			elsif real_download_uri
				if block_given?
					fetch_body { |chunk| yield chunk }
					@body
				else
					fetch_body
				end
			else
				raise "Cannot fetch body without real download URI"
			end
		end

		def body= x
			@body = x
		end

		def fetch_body
			if block_given?
				@body = ''
				Net::HTTP::get_response(
					URI.parse(@real_download_uri)) \
					do |resp|
						resp.read_body do |chunk|
							@body << chunk
							yield chunk
						end
					end
			else
				@body = Net::HTTP::get @real_download_uri
			end
			@body
		end
	end
end
