# encoding: utf-8

require 'fileutils'
require 'pathname'
require 'uconv'
require 'zipruby'
require 'win32ole'

require 'rumix/const'
require 'rumix/function'
require 'rumix/registry'
require 'rumix/config'

module Rumix
	class Application
		def self.run(config, log_io = $stderr, &block)
			self.new.run(config, log_io, &block)
		end
	
		public
		
		def initialize
			@maked_path_list = []
		end
		
		def run(config, log_io = $stderr, &block)
			log_io.puts(<<ECHO)
---------------
Rumix Installer
---------------
dest : #{config.dest_dir}
ext : #{config.ext_dest_dir}
ECHO
			@maked_path_list.clear
			

		
			# インストール先ディレクトリを作成
			mkpath(config, log_io, config.dest_dir)
		
			# レジストリに情報を書き込む
			reg = Rumix::Registry.load
			reg.install_path = config.dest_dir.gsub('/', '\\')
			reg.store or raise 'failure to store registry'

			
			# アンインストーラのインストール
			block.call(:on_uninstaller_install_start) if block
			install_from_zip(config, log_io, 'uninstaller.zip', config.dest_dir, &block)
			block.call(:on_uninstaller_install_complete) if block

			
			# rubyのインストール
			block.call(:on_ruby_install_start) if block
			if config.ruby_type then
				install_from_zip(config, log_io, config.ruby_src_zip_name, File.join(config.dest_dir, 'ruby'), &block)
			end
			block.call(:on_ruby_install_complete) if block
			
			# 外部ライブラリのインストール
			block.call(:on_ext_install_start) if block
			if config.ext_dest_dir then
				install_from_zip(config, log_io, 'ext.zip', config.ext_dest_dir, &block)
			end
			block.call(:on_ext_install_complete) if block
			
			# リファレンスのインストール
			block.call(:on_man_install_start) if block
			case config.man_type
			when :new_chm
				case config.ruby_type
				when Config::RUBY_191
					install_from_zip(config, log_io, 'rubyman-newchm-191.zip', config.dest_dir, &block)
				when Config::RUBY_18
					install_from_zip(config, log_io, 'rubyman-newchm-18.zip', config.dest_dir, &block)
				end
			when :chm
				install_from_zip(config, log_io, 'rubyman-chm.zip', config.dest_dir, &block)
			when :html
				install_from_zip(config, log_io, 'rubyman-html.zip', File.join(config.dest_dir, 'rubymanjp'), &block)
			end
			block.call(:on_man_install_complete) if block
			
			# シェルのインストール
			block.call(:on_shell_install_start) if block
			case config.shell_type
			when :nyacus
				install_from_zip(config, log_io, 'nyacus.zip', config.dest_dir, &block)
				unless File.exist?(File.join(config.dest_dir, '_nya')) then
					install_from_zip(config, log_io, '_nya.zip', config.dest_dir, &block)
				end
			when :nyados
				install_from_zip(config, log_io, 'nyados.zip', config.dest_dir, &block)
				unless File.exist?(File.join(config.dest_dir, '_nya')) then
					install_from_zip(config, log_io, '_nya.zip', config.dest_dir, &block)
				end
			end
			block.call(:on_shell_install_complete) if block
			
			
			# 関連ツールのインストール
			if config.installing_tool_ids.include?(:rubygems) then
				block.call(:on_tool_install_start, [:rubygems]) if block
				install_tool(config, log_io, 'rubygems', 'setup.rb')
				block.call(:on_tool_install_complete, [:rubygems]) if block
			end
			
			if config.installing_tool_ids.include?(:rake) then
				block.call(:on_tool_install_start, [:rake]) if block
				install_tool(config, log_io, 'rake', 'install.rb')
				
				# rake.bat を作成（なぜか rake のインストーラは作成をやってくれない）
				cmd_dest = File.join(config.dest_dir, 'ruby/bin/rake.bat')
				original_cmd = File.join(config.dest_dir, 'ruby/bin/rake.rb')

				open(cmd_dest, 'w'){|f|
					f.puts(<<BAT)
@ECHO OFF
IF NOT "%~f0" == "~f0" GOTO :WinNT
@"ruby.exe" "#{original_cmd}" %1 %2 %3 %4 %5 %6 %7 %8 %9
GOTO :EOF
:WinNT
@"ruby.exe" "%~dpn0.rb" %*
BAT
					log_io.puts "#{cmd_dest} maked."
				}
				
				block.call(:on_tool_install_complete, [:rake]) if block
			end
			
			if config.installing_tool_ids.include?(:exerb) then
				block.call(:on_tool_install_start, [:exerb]) if block
				install_tool(config, log_io, 'exerb', 'setup.rb')
				block.call(:on_tool_install_complete, [:exerb]) if block
			end
			
			if config.installing_tool_ids.include?(:facets) then
				block.call(:on_tool_install_start, [:facets]) if block
				install_tool(config, log_io, 'facets', 'setup.rb')
				block.call(:on_tool_install_complete, [:facets]) if block
			end

			if config.installing_tool_ids.include?(:visualuruby) then
				block.call(:on_tool_install_start, [:visualuruby]) if block
				install_tool(config, log_io, 'visualuruby', 'setup.rb')
				
				# swin.so を手動でコピー（setup.rb はこの作業をやってくれない）
				install_from_zip(config, log_io, "swin.zip", File.join(config.dest_dir, 'ruby/lib/ruby/site_ruby/1.8/i386-msvcrt'))
				
				# FormDesigner.bat を作成
				cmd_dest = File.join(config.dest_dir, 'ruby/bin/FormDesigner.bat')
				original_cmd = File.join(config.dest_dir, 'ruby/bin/FormDesigner.rb')

				open(cmd_dest, 'w'){|f|
					f.puts(<<BAT)
@ECHO OFF
IF NOT "%~f0" == "~f0" GOTO :WinNT
@"ruby.exe" "#{original_cmd}" %1 %2 %3 %4 %5 %6 %7 %8 %9
GOTO :EOF
:WinNT
@"ruby.exe" "%~dpn0.rb" %*
BAT
					log_io.puts "#{cmd_dest} maked."
				}

				block.call(:on_tool_install_complete, [:visualuruby]) if block
			end


			
			
			# 環境変数PATHの追加
			if config.add_path_env? then
				block.call(:on_path_env_add_start) if block
				shell = WIN32OLE.new('Wscript.Shell')
				ruby_bin_dir = File.join(config.dest_dir, 'ruby/bin')
				
				paths = shell.Environment.item('PATH').split(';')
				unless paths.include?(ruby_bin_dir) then
					paths << ruby_bin_dir.gsub('/', '\\')
					new_path = paths.join(';')
					log_io.puts "old PATH = #{shell.Environment.item('PATH')}"
					log_io.puts "new PATH = #{new_path}"
					if config.real_operating? then
						shell.Environment.setproperty('item', 'PATH', new_path)
					end
				end
				block.call(:on_path_env_add_complete) if block
			end
			
			# スタートメニューにRumix追加
			if config.add_start_menu? then
				block.call(:on_start_menu_add_start) if block
				dir_path = Rumix.start_menu_dir_path
				#cd(config, log_io, dir_path) do
				#	Dir.glob('**/*.lnk') do |path|
				#		remove_file(config, log_io, path)
				#	end
				#end
				mkpath(config, log_io, dir_path)
				
				
				
				create_shortcut(config, log_io, shell, dir_path, 'irb - Interactive Ruby Console', 'ruby/bin/irb.bat')
				case config.shell_type
				when :nyacus
					create_shortcut(config, log_io, shell, dir_path, 'NYACUS（コマンドラインシェル）', 'NYACUS.exe')
				when :nyados
					create_shortcut(config, log_io, shell, dir_path, 'NYADOS（コマンドラインシェル）', 'NYADOS.exe', 'IconLocation' => File.join(config.dest_dir, 'greencat.ico'))
				end

				case config.man_type
				when :new_chm
					create_shortcut(config, log_io, shell, dir_path, 'Rubyリファレンスマニュアル', 'ruby-refm.chm')
				when :chm
					create_shortcut(config, log_io, shell, dir_path, 'Rubyリファレンスマニュアル', 'rubymanjp.chm')
				when :html
					create_shortcut(config, log_io, shell, dir_path, 'Rubyリファレンスマニュアル', 'rubymanjp/index.html')
				end
				
				create_shortcut(config, log_io, shell, dir_path, 'Rumixのアンインストール', 'rumix_uninstall.exe')
				
				if config.installing_tool_ids.include?(:visualuruby) then
					create_shortcut(config, log_io, shell, dir_path, 'VisualuRuby/FormDesigner', 'ruby/bin/FormDesigner.bat')
					create_shortcut(config, log_io, shell, dir_path, 'VisualuRuby/VisualuRuby計画（仮称）リファレンス', 'ruby/share/visualuruby/vrswin-ja.chm')
				end
				
				block.call(:on_start_menu_add_complete) if block
			end
			
			
			
			block.call(:on_complete) if block
		end
		
		
		
		private
		
		def cd(config, log_io, to)
			log_io.puts "[cd] #{to}"
			if config.real_operating? then
				FileUtils.cd(to) do
					yield
				end
			else
				yield
			end
			log_io.puts "[cd] -"
		end
		
		def install_from_zip(config, log_io, src_zip, dest_dir, &block)
			list = []
			Zip::Archive.open(File.join(config.src_dir, src_zip)){|zip|
				# contentディレクトリがあるかどうかをチェック
				zip.each do |file|
					list << file.name
				end
				
				from_content_dir = list.include?('content/')
				

				zip.each_with_index do |src_file, i|
					dest_name = nil
					if not from_content_dir or (dest_name = src_file.name[/^content\/(.+)/, 1]) then
						dest_name ||= src_file.name
						dest = File.join(dest_dir, dest_name)
						
						
						if src_file.directory? then
							mkpath(config, log_io, dest)
						else
							log_io.puts "#{src_zip} : #{src_file.name} -> #{dest}"
							mkpath(config, log_io, File.dirname(dest))
							
							extract_file(config, log_io, src_file, dest)
						end
						block.call(:on_child_installed, i) if block
					end
				end
			
			}

		end
		
		def extract_file(config, log_io, zip_file, dest)
			if not config.force_overwriting? and File.exist?(dest) and zip_file.mtime <= File.mtime(dest) then
				log_io.puts "skipped." 
				return
			end
			
			if config.real_operating? then
				open(dest, 'wb'){|out|
					while (got = zip_file.read(ZIP_CHUNK_SIZE)) do
						written_size = out.write(got)
					end
				}
				File.utime(File.atime(dest), zip_file.mtime, dest)
			end
		end
		
		def remove_file(config, log_io, path)
			log_io.puts "[rm] #{path}"
			if config.real_operating? then
				FileUtils.rm(path)
			end
		end
		
		def remove_dir(config, log_io, dir_path)
			log_io.puts "[rmdir] #{dir_path}"
			if config.real_operating? then
				FileUtils.remove_entry_secure(dir_path)
			end
		end
		
		def mkpath(config, log_io, dir_path)
			if not File.exist?(dir_path) and not @maked_path_list.include?(dir_path) then
				log_io.puts "[mkpath]  #{dir_path}"
				if config.real_operating? then
					FileUtils.mkpath(dir_path)
				else
					# On no-op mode
					@maked_path_list << dir_path
				end
			end
		end
		alias make_path mkpath
		
		def create_shortcut(config, log_io, shell, dir_path, name, target, properties = {})
			shell = WIN32OLE.new('Wscript.Shell') 
			lnk_path = File.join(dir_path, Uconv.u8tosjis(name) + '.lnk')
			mkpath(config, log_io, File.dirname(lnk_path))
			log_io.puts "[shortcut] #{lnk_path} -> #{target}"
		
			if config.real_operating? then
				shortcut = shell.CreateShortcut(lnk_path.gsub('/', '\\'))
				shortcut.TargetPath = File.join(config.dest_dir, target).gsub('/', '\\')
				properties.each do |key, value|
					shortcut.setproperty(key.to_s, value)
				end
				shortcut.Save
			end

		end
		
		def install_tool(config, log_io, package_name, script_name)
			
			log_name = "../../#{package_name}_install.log"
			#old = ENV['LOG_FILE']
			#ENV['LOG_FILE'] = "../../#{package_name}_install.log"
			ruby_path = File.join(config.dest_dir, 'ruby/bin/ruby.exe')
			old_ruby_opts = ENV['RUBYOPTS']
			ENV['RUBYOPTS'] = nil
			
			temp_dir = 'temp/'
			dest_dir = File.join(temp_dir, package_name)
			install_from_zip(config, log_io, "#{package_name}.zip", dest_dir)
			
			cd(config, log_io, dest_dir) do
				log_io.puts "[tool install] #{ruby_path} #{script_name} (logfile = #{log_name})"
				if config.real_operating then
					# 標準出力と標準エラー出力を、ログファイルに変更するため
					# tool_install.rb を介して実行
					success = system("#{ruby_path} ../../res/script/tool_install.rb #{ruby_path} #{script_name} #{log_name} #{package_name}")
					
					if success then
						log_io.puts "#{package_name} install success."
					else
						log_io.puts "#{package_name} install failed! (code=#{$?}, see the log)"
					end
				
				
					#result = %x(../../tool/doslog.exe #{ruby_path} #{script_name})
					#code = nil
					
					# doslog.exeの出力を解析して、終了コードをチェック
					#if result =~ Regexp.compile(Uconv.u8tosjis('^終了コード＝(\d+)$'), nil, 's') then
					#	code = $1.to_i
					#	if code == 0 then
					#		log_io.puts "#{package_name} install success."
					#	else
					#		log_io.puts "#{package_name} install failed! (code=#{code})"
					#	end
					#else
					#	log_io.puts "#{package_name} install failed! (code=?)"
					#end
				end
			end
			
			#ENV['LOG_FILE'] = old
			ENV['RUBYOPTS'] = old_ruby_opts
			
			remove_dir(config, log_io, dest_dir)
			
			
		end

	end

end
