#!/usr/bin/env ruby
#  Phusion Passenger - http://www.modrails.com/
#  Copyright (C) 2008  Phusion
#
#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation; version 2 of the License.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License along
#  with this program; if not, write to the Free Software Foundation, Inc.,
#  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

PASSENGER_ROOT = File.expand_path(File.dirname(__FILE__) << "/..")
$LOAD_PATH.unshift("#{PASSENGER_ROOT}/lib")

# The Apache executable may be located in an 'sbin' folder. We add
# the 'sbin' folders to $PATH just in case. On some systems
# 'sbin' isn't in $PATH unless the user is logged in as root from
# the start (i.e. not via 'su' or 'sudo').
ENV["PATH"] += ":/usr/sbin:/sbin:/usr/local/sbin"

require 'passenger/platform_info'
require 'passenger/dependencies'
require 'passenger/console_text_template'
include PlatformInfo

class Installer
	include Passenger
	
	PASSENGER_WEBSITE = "http://www.modrails.com/"
	PHUSION_WEBSITE = "www.phusion.nl"
	USERS_GUIDE = "#{PASSENGER_ROOT}/doc/Users guide.html"
	File.read("#{PASSENGER_ROOT}/Rakefile") =~ /^PACKAGE_VERSION = "(.*)"$/
	PASSENGER_VERSION = $1
	
	REQUIRED_DEPENDENCIES = [
		Dependencies::GCC,
		Dependencies::Ruby_DevHeaders,
		Dependencies::Ruby_OpenSSL,
		Dependencies::RubyGems,
		Dependencies::Rake,
		Dependencies::Apache2,
		Dependencies::Apache2_DevHeaders,
		Dependencies::APR_DevHeaders,
		Dependencies::FastThread
	]
	
	def start
		Dir.chdir(PASSENGER_ROOT)
		show_welcome_screen
		check_dependencies || exit(1)
		check_whether_apache_uses_prefork_mpm
		check_write_permission_to_passenger_root || exit(1)
		if install_apache2_module
			show_apache2_config_snippets
			show_deployment_example
		else
			show_possible_solutions_for_compilation_and_installation_problems
			exit(1)
		end
	ensure
		reset_terminal_colors
	end

private
	def init_terminal_colors
		STDOUT.write("\e[0m\e[37m\e[40m")
		STDOUT.flush
	end
	
	def reset_terminal_colors
		STDOUT.write("\e[0m")
		STDOUT.flush
	end
	
	def show_welcome_screen
		render_template 'welcome', :version => PASSENGER_VERSION
		wait
	end

	def check_dependencies
		missing_dependencies = []
		color_puts "<banner>Checking for required software...</banner>"
		puts
		REQUIRED_DEPENDENCIES.each do |dep|
			color_print " * #{dep.name}... "
			result = dep.check
			if result.found?
				if result.found_at
					color_puts "<green>found at #{result.found_at}</green>"
				else
					color_puts "<green>found</green>"
				end
			else
				color_puts "<red>not found</red>"
				missing_dependencies << dep
			end
		end
		
		if missing_dependencies.empty?
			return true
		else
			puts
			color_puts "<red>Some required software is not installed.</red>"
			color_puts "But don't worry, this installer will tell you how to install them.\n"
			color_puts "<b>Press Enter to continue, or Ctrl-C to abort.</b>"
			wait
			
			line
			color_puts "<banner>Installation instructions for required software</banner>"
			puts
			missing_dependencies.each do |dep|
				print_dependency_installation_instructions(dep)
				puts
			end
			return false
		end
	end
	
	def check_whether_apache_uses_prefork_mpm
		line
		# 'httpd -V' output is in the form of:
		#
		# Server MPM:      Prefork     # <--- this line is not always available!
		# ...
		# Server compiled with....
		#  -D APACHE_MPM_DIR="server/mpm/prefork"
		output = `#{HTTPD} -V`
		output =~ /^Server MPM: +(.*)$/
		if $1
			mpm = $1.downcase
		else
			output =~ /APACHE_MPM_DIR="server\/mpm\/(.*)"/
			if $1
				mpm = $1.downcase
			else
				mpm = nil
			end
		end
		if mpm != "prefork"
			render_template 'apache_must_be_compiled_with_prefork_mpm',
				:current_mpm => mpm
			wait
		end
	end
	
	def check_write_permission_to_passenger_root
		File.new("__test__.txt", "w").close
		return true
	rescue
		puts
		line
		if Process.uid == 0
			render_template 'no_write_permission_to_passenger_root'
		else
			render_template 'run_installer_as_root'
		end
		return false
	ensure
		File.unlink("__test__.txt") rescue nil
	end
	
	def install_apache2_module
		puts
		line
		color_puts '<banner>Compiling and installing Apache 2 module...</banner>'
		puts "cd #{PASSENGER_ROOT}"
		puts "rake clean apache2"
		return system("rake", "clean", "apache2")
	end
	
	def show_apache2_config_snippets
		puts
		line
		render_template 'apache2_config_snippets',
			:module_location => "#{PASSENGER_ROOT}/ext/apache2/mod_passenger.so",
			:spawn_server_location => "#{PASSENGER_ROOT}/bin/passenger-spawn-server",
			:ruby => RUBY
		wait
	end
	
	def show_deployment_example
		puts
		line
		render_template 'deployment_example',
			:users_guide => USERS_GUIDE,
			:phusion_website => PHUSION_WEBSITE,
			:passenger_website => PASSENGER_WEBSITE
	end
	
	def show_possible_solutions_for_compilation_and_installation_problems
		puts
		line
		render_template 'possible_solutions_for_compilation_and_installation_problems',
			:users_guide => USERS_GUIDE,
			:passenger_website => PASSENGER_WEBSITE
	end

private
	def color_print(text)
		STDOUT.write(ConsoleTextTemplate.new(:text => text).result)
		STDOUT.flush
	end
	
	def color_puts(text)
		color_print("#{text}\n")
	end
	
	def render_template(name, options = {})
		puts ConsoleTextTemplate.new({ :file => name }, options).result
	end
	
	def line
		puts "--------------------------------------------"
	end
	
	def wait
		begin
			STDIN.readline
		rescue Interrupt
			exit 2
		end
	end
	
	def print_dependency_installation_instructions(dep)
		color_puts " * To install <yellow>#{dep.name}</yellow>:"
		if !dep.install_command.nil?
			color_puts "   Please run <b>#{dep.install_command}</b> as root."
		elsif !dep.install_instructions.nil?
			color_puts "   " << dep.install_instructions
		elsif !dep.website.nil?
			color_puts "   Please download it from <b>#{dep.website}</b>"
			if !dep.website_comments.nil?
				color_puts "   (#{dep.website_comments})"
			end
		else
			color_puts "   Search Google."
		end
	end
end

Installer.new.start
