#!/usr/bin/env ruby
require "fileutils"
require "yaml"

RDBMS = %w(MySQL SQLite3)
@src_top = File.dirname( File.expand_path(__FILE__) )

def setup_parameter
  print <<"EOF"
Set destination directory name and copy files.
gfdnavi will be installed to "/path_you_input/gfdnavi"
EOF
  dest_path = @params[:dest_top] ? File.dirname(@params[:dest_top]) : ENV["HOME"]
  print "Please input destination path (default: #{dest_path}): "
  res = $stdin.gets.chomp!
  unless res == ""
    dest_path = res.sub(/\/$/,"")
  end
	unless dest_path == ""
    if RUBY_PLATFORM == "i386-mswin32"
      dest_path = File.join(ENV['RBPATH'],"..","..") 
		end
  end
  @params[:dest_top] = File.join(dest_path,"gfdnavi")

  if File.exists?( File.join(dest_path, "gfdnavi", "config", "database.yml") )
    print "There already exists #{dest_path}/gfdnavi/config/database.yml.\n"
    print "Do you want to keep the settings? (yes/no, default:yes): "
    unless $stdin.gets.chomp! == "no"
      if !$load_inst_params
        install_params_file = File.join(dest_path,"gfdnavi","install_params.yml")
        if File.exists?( install_params_file )
          @params = YAML.load(File.read(install_params_file))
          $load_inst_params = true
        end
      end
      @params[:skip_rdb] = true
    end
  end
  if @params[:skip_rdb]
    print "skipped setting for database configurations\n"
  else
    print <<"EOF"

***
The database settings you will specify in the following will
be written in the file:

   #{dest_path}/gfdnavi/config/database.yml

YOU CAN MANUALLY EDIT IT LATER IF YOU LIKE.

You must finish settings of RDBMS before installing gfdnavi unless you
will use "SQLite3". If you have not, use another terminal now to set up
an RDBMS. If you would like to use mysql, I can show you how to do that. 
EOF
    print "Would you like read the how-to for mysql? (yes/no, default:yes): "

    unless $stdin.gets.chomp! == "no"
      print <<"EOF"
***
>>>>> doc for mysql >>>>>
You can set mysql database as follows:

  % mysql -u root -p
  Enter password: 
  -- type in password of root. 
     (the user name specified with the -u option does not need to be root, 
     but it must be a privileged one in order to create new databases.)
  mysql> create database gfdnavi_development;
  mysql> create database gfdnavi_test;
  mysql> create database gfdnavi_production;
  mysql> grant all on gfdnavi_development.* to 'davis'@'localhost' identified by 'hogehero';
  mysql> grant all on gfdnavi_test.* to 'davis'@'localhost' identified by 'hogehero';
  mysql> grant all on gfdnavi_production.* to 'davis'@'localhost' identified by 'hogehero';

Here, DO NOT forget to use your own password. DO NOT use 'hogehero'.
Note that the password will appear in the database.yml file
WITHOUT ENCRYPTION. So DO NOT USE YOUR LOGIN PASSWORD of any computer 
system either.
<<<<< doc for mysql <<<<<

EOF
    end

    print <<"EOF"
***
If you have finished the settings, specify the RDBMS.
EOF
    RDBMS.each_with_index{|name,i| print "#{i+1}. #{name}\n"}
    print "(Sorry, other RDBMSs are not supported in this installation script.)\n"
    while true
      rdb_type = @params[:rdb_type] || 1
      print "Select the RDBMS (default:#{rdb_type}): "
      type = $stdin.gets.chomp!
      rdb_type = type=="" ? rdb_type : type.to_i
      unless rdb_type >= 1 && rdb_type <= RDBMS.length
        print "Invalid number was input.\n"
      else
        break
      end
    end
    @params[:rdb_type] = rdb_type
    if RDBMS[rdb_type-1] == "MySQL"
      # CYGWIN PATCH BEGIN
      if /cygwin/ =~ RUBY_PLATFORM
        print <<"EOF"
 1. windows native MySQL
 2. MySQL compiled on cygwin
EOF
        while true
          rdb_plf = @params[:rdb_plf] || 1
          print "Select the RDBMS platform (default:#{rdb_plf}): "
          type = $stdin.gets.chomp!
          rdb_plf = type=="" ? rdb_plf : type.to_i
          unless [1,2].include?(rdb_plf)
            print "Invalid number was input.\n"
          else
            break
          end
        end
        @params[:rdb_plf] = rdb_plf
        end
      # CYGWIN PATCH END
      print "Input the RDB user name for gfdnavi_* databases "
      print "(default: #{@params[:rdb_user]})" if @params[:rdb_user]
      print ": "
      rdb_user = $stdin.gets.chomp!
      @params[:rdb_user] = rdb_user unless rdb_user == ""
      print "Input the password for the databases: "
      @params[:rdb_password] = $stdin.gets.chomp!
    end
  end

  print "\n***\nGfdanvi configuration\n\n"
  if File.exists?( File.join(dest_path, "gfdnavi", "config", "gfdnavi.yml") )
    print "There already exists #{dest_path}/gfdnavi/config/gfdnavi.yml.\n"
    print "Do you want to keep the settings? (yes/no, default:yes): "
    unless $stdin.gets.chomp! == "no"
      @params[:skip_configure] = true
    end
  end
  if @params[:skip_configure]
    print "skipped setting for gfdnavi configurations\n"
  else
    print <<"EOF"

***
The gfdnavi settings you will specify in the following will
be written in the file:

   #{dest_path}/gfdnavi/config/gfdnavi.yml

YOU CAN MANUALLY EDIT IT LATER IF YOU LIKE.

EOF

    print "Set salt for encryption\n"
    while true
      print "input phrase (at least 5 characters): "
      salt = $stdin.gets.chomp!
      if salt.length < 5
        print "The phrase which you input is too short.\n"
      else
        break
      end
    end
    @params[:salt] = salt

    print "Set admin's email address\n"
    while true
      print "Input email address"
      print "(default: #{@params[:email]})" if @params[:email]
      print ": "
      email = $stdin.gets.chomp!
      email = @params[:email] if email == ""
      unless /^(\w|-)+@(\w|-)+\.(\w|-)+/ =~ email
        print "Invalid email address is input.\n"
      else
        break
      end
    end
    @params[:email] = email

    print <<"EOF"
Select server type to set default configuration parameters
The configurations in "config/gfdnavi.yml" will be set according to your choice of server type.
 1. private server
 2. open-access server
EOF
    while true
      print "Select the server type (default:1): "
      server_type = $stdin.gets.chomp!
      server_type = server_type=="" ? 1 : server_type.to_i
      unless [1,2].include?(server_type)
        print "Invalid number was input.\n"
      else
        break
      end
    end
    @params[:server_type] = server_type
  end
end

DIRS_TO_EXCLUDE = /#{@src_top}\/public\/data\/.+|#{@src_top}\/tmp\/.+|#{@src_top}\/public\/diagrams\/.+|#{@src_top}\/public\/temp_data\/.+/

def copy_files_in_dir(src, dest, sampledata=false)
  Dir.foreach(src) do |name|
    next if name == "." || name == ".."
    next if name == "install.rb"
    next if /~$/ =~ name
    dest_name = File.join(dest,name)
    src_name = File.join(src,name)
    if File.directory?(src_name) && !File.symlink?(src_name)
      next if !sampledata and (DIRS_TO_EXCLUDE =~ src_name)
      FileUtils.mkdir(dest_name) unless File.exist?(dest_name)
      copy_files_in_dir( src_name, dest_name, sampledata)
    else
      FileUtils.copy( src_name, dest_name ) if File.file?(src_name)
    end
  end
end
def copy_files(src, dest, sampledata=false)
  copy_files_in_dir(src, dest, sampledata)
end
def install_files
  dest_top = @params[:dest_top]
  if File.exists?( dest_top )
    unless File.directory?(dest_top)
      abort "#{dest_top} is not a directory."
    end
    print "#{dest_top} already exists.\n"
    print "Can I overwrite it? (no/yes, default: no): "
    unless $stdin.gets.chomp! == "yes"
      abort "operation was canceled."
    end
    dest_existed = true
  else
    print "#{dest_top} does not exist.\n"
    print "Can I create the directories? (yes/no, default:yes): "
    unless $stdin.gets.chomp! == "no"
      FileUtils.makedirs(dest_top)
    else
      abort "operation was canceled."
    end
    dest_existed = false
  end
  Dir.chdir( File.join(@params[:dest_top],"..") )
  if dest_existed
    print "Do you want execute \"rails\" command? (no/yes, default:no): "
    unless $stdin.gets.chomp! == "yes"
      execute_rails = true
    else
      execute_rails = false
    end
  end
  if !dest_existed || execute_rails
    if @params[:skip_rdb]
      rails_option = "--skip"
    else
      rails_option = "--force"
    end
    if RUBY_PLATFORM == "i386-mswin32"
      rbpath = ENV['RBPATH']
      dev_null = "nul"
    else
      dev_null = "/dev/null"
    end
    rails_bin = File.join(rbpath,"rails")
    unless system("ruby #{rails_bin} gfdnavi #{rails_option} -d #{RDBMS[@params[:rdb_type]-1].downcase} > #{dev_null}")
    abort "failed to execute \"rails gfdnavi\" at #{Dir.pwd}"
    end
  end
  copy_files( @src_top, dest_top )
  print "Do you want to install sample data? (yes/no, default:yes): "
  unless $stdin.gets.chomp! == "no"
    dest_sample_dir = File.join(dest_top,"public","data","samples")
    FileUtils.mkdir( dest_sample_dir ) unless File.exists?( dest_sample_dir )
    copy_files( File.join(@src_top,"public","data","samples"), dest_sample_dir , sampledata=true)
  end
end



def set_configure_files
  Dir.chdir( @params[:dest_top] )
  unless @params[:skip_rdb]
    database_file = File.join("config","database.yml")
    if File.exists?( database_file )
      FileUtils.move( database_file, database_file + ".bak" )
    end
    rdb_type = @params[:rdb_type]
    rdb_plf = @params[:rdb_plf]
    name = %w(mysql sqlite3)[rdb_type-1]
    File.open(database_file,"w"){|file|
      File.foreach( database_file + ".bak" ){|line|
        if RDBMS[rdb_type-1] == "MySQL"
          if /^(\s*username:)/ =~ line
            line = "#$1 #{@params[:rdb_user]}\n"
          elsif /^(\s*password:)/ =~ line
            line = "#$1 #{@params[:rdb_password]}\n"
          end
          if rdb_plf == 1
            if /^(\s*host:)/ =~ line
              line = "#$1 127.0.0.1\n"
            end
          end
        elsif RDBMS[rdb_type-1] == "SQLite3"
        end
        file.write line
      }
    }
  end

  unless @params[:skip_configure]
    config_file = File.join("config","gfdnavi.yml")
    if File.exists?( config_file )
      FileUtils.move( config_file, config_file + ".bak" )
    end
    File.open(config_file,"w") do |file|
      server_type = @params[:server_type]
      salt = @params[:salt]
      email = @params[:email]
      File.foreach( config_file + ".example" ){|line|
        if /^(passphrase:)/ =~ line
          line = "#$1 #{salt}\n"
        elsif /^(\s*email:) root@example\.org/ =~ line
          line = "#$1 #{email}\n"
        end
        if server_type == 1 # private
        elsif server_type == 2 # open
          if /^(disable_user:)/ =~ line
            line = "#$1 true\n"
          elsif /^(enable_email:)/ =~ line
            line = "#$1 true\n"
          elsif /^(:domain:) example\.org/ =~ line
            key = $1
            /[\w|-]*@(.+)/ =~ @email
            line = "#{key} #$1\n"
          end
        end
        file.write line
      }
    end
  end

end

def execute_setup
  Dir.chdir(@params[:dest_top])
  if Variable.table_exists?
    print <<"EOF"
If you already have data in the gfdnavi database, skip this step,
and execute "rake update" manually if it is necessary.
EOF
    print "Do you skip? (no/yes, default:no): "
    if $stdin.gets.chomp! == "yes"
      print "skiped\n"
      return
    end
  end
  # WHILE EXECUTION OF RAKE SETUP, WE NEED TO REPLY TO QUESTIONS
  #unless system("rake setup > /dev/null")
  unless system("rake setup")
    abort "failed to execute \"rake setup\" at #{Dir.pwd}\n"
  end
end


$load_inst_params = false
install_params_file = File.join(@src_top,"install_params.yml")
if File.exists?( install_params_file )
  @params = YAML.load(File.read(install_params_file))
  $load_inst_params = true
end
@params = Hash.new unless Hash === @params

print <<"EOF"
Welcome to Gfdnavi.
In order install Gfdnavi,
there are the following steps.
Step 1: Setup parameters
Step 2: Execute rails and copy files"
Step 3: Create "database.yml" and "gfdnavi.yml"
Step 4: Execute "rake setup"
Step 5: Set root's password
press any key to continue
EOF
$stdin.gets

print "***************************************\nStep 1: Setup parametes\n"
setup_parameter
para = @params.dup
para.delete(:salt)
para.delete(:rdb_password)
File.open(install_params_file,"w"){|file|
  file.write para.to_yaml
}

print "***************************************\nStep 2: Execute rails and copy files\n"
install_files

print "***************************************\nStep 3: Create \"database.yml\" and \"gfdnavi.yml\"\n"
set_configure_files

require File.join(Dir.pwd, "config", "environment")
print "***************************************\nStep 4: Execute rake setup\n"
execute_setup

=begin
print "\n"
print "***************************************\nStep 5: Set root's password\n"
=end

print <<"EOF"
Congraturations!
You sucessed to install gfdnavi to #{@params[:dest_top]}.
Before you start gfdnavi,
change the following configurations
 1. "config/database.yml" 
 2. "config/gfdnavi.yml"
and check file pamissoin mode of their files (only webserver can read the files).
EOF
