#!/usr/bin/env ruby

require 'pp'

PACKAGES=%w{optional sunstone quota cloud ozones_client ozones_server
    ozones_server_mysql ozones_server_sqlite}

DEFAULT=%w{optional sunstone quota cloud ozones_server acct}

if defined?(RUBY_VERSION) && RUBY_VERSION>="1.8.7"
    SQLITE='sqlite3'

    # xmlparser gem is not compatible with ruby 1.9
    OPTIONAL=%w{nokogiri}
else
    SQLITE='sqlite3-ruby --version 1.2.0'
    OPTIONAL=%w{nokogir xmlparser}
end

GROUPS={
    :optional => OPTIONAL,
    :quota => [SQLITE, 'sequel'],
    :sunstone => ['json', 'rack', 'sinatra', 'thin', 'sequel', SQLITE],
    :cloud => %w{amazon-ec2 rack sinatra thin uuid curb},
    :ozones_client => %w{json},
    :ozones_server => %w{json data_mapper dm-sqlite-adapter dm-mysql-adapter}+[
        SQLITE, 'mysql'
    ],
    :ozones_server_sqlite => %w{json data_mapper dm-sqlite-adapter}<<SQLITE,
    :ozones_server_mysql => %w{json data_mapper dm-mysql-adapter mysql},
    :acct => ['sequel', SQLITE, 'mysql'],
    :acct_sqlite => ['sequel', SQLITE],
    :acct_mysql => ['sequel', 'mysql']
}

DISTRIBUTIONS={
    :debian => {
        :id => ['Ubuntu', 'Debian'],
        :dependencies => {
            SQLITE      => ['gcc', 'libsqlite3-dev'],
            'mysql'     => ['gcc', 'libmysqlclient-dev'],
            'curb'      => ['gcc', 'libcurl4-openssl-dev'],
            'nokogiri'  => %w{gcc rake libxml2-dev libxslt1-dev},
            'xmlparser' => ['gcc', 'libexpat1-dev'],
            'thin'      => ['g++']
        },
        :install_command => 'apt-get install',
        :gem_env    => {
            'rake'      => '/usr/bin/rake'
        }
    },
    :redhat => {
        :id => ['CentOS', /^RedHat/],
        :dependencies => {
            SQLITE      => ['gcc', 'sqlite-devel'],
            'mysql'     => ['gcc', 'mysql-devel'],
            'curb'      => ['gcc', 'curl-devel'],
            'nokogiri'  => %w{gcc rubygem-rake libxml2-devel libxslt-devel},
            'xmlparser' => ['gcc', 'expat-devel'],
            'thin'      => ['gcc-c++']
        },
        :install_command => 'yum install'
    },
    :suse => {
        :id => [/^SUSE/],
        :dependencies => {
            SQLITE      => ['gcc', 'sqlite3-devel'],
            'mysql'     => ['gcc', 'libmysqlclient-devel'],
            'curb'      => ['gcc', 'libcurl-devel'],
            'nokogiri'  => %w{rubygem-rake gcc rubygem-rake libxml2-devel libxslt-devel},
            'xmlparser' => ['gcc', 'libexpat-devel'],
            'thin'      => ['rubygem-rake', 'gcc-c++']
        },
        :install_command => 'zypper install'
    }
}


class String
    def unindent(spaces=4)
        self.gsub!(/^ {#{spaces}}/, '')
    end
end

def try_library(name, error_message)
    begin
        require name.to_s
    rescue LoadError, Exception
        STDERR.puts error_message
        exit(-1)
    end
end

def install_warning(packages)
#    puts "Use -h for help"
#    puts
    puts "About to install the gems for these components:"
    puts "* "<<packages.join("\n* ")
    puts
    puts "Press enter to continue..."
    yes=STDIN.readline
end

def help
    puts "Specify the package dependencies from this list:"
    puts "* "<<PACKAGES.join("\n* ")
    puts
    puts "If no parameters are specified then this list will be used:"
    puts DEFAULT.join(' ')
end

def get_gems(packages)
    packages.map do |package|
        GROUPS[package.to_sym]
    end.flatten.uniq
end

def detect_distro
    begin
        lsb_info=`lsb_release -a`
    rescue
    end

    if $?.exitstatus!=0
        STDERR.puts(<<-EOT.unindent(12))
            lsb_release command not found. If you are using a RedHat based
            distribution install redhat-lsb

EOT
        return nil
    end

    distribution_id=nil

    lsb_info.scan(/^Distributor ID:\s*(.*?)$/) do |m|
        distribution_id=m.first.strip
    end

    return nil if !distribution_id

    distro=nil

    DISTRIBUTIONS.find do |dist, info|
        info[:id].find do |dist_id|
            dist_id===distribution_id
        end
    end
end

def get_dependencies(gems, dependencies)
    deps=[]

    gems.each do |gem_name|
        deps<<dependencies[gem_name]
    end

    deps.flatten!
    deps.compact!
    deps.uniq!

    deps
end

def install_dependencies(gems, distro)
    if !distro
        puts(<<-EOT.unindent(12))
            Distribution not detected. Make sure you manually install the
            dependencies described in Building from Source from the OpenNebula
            documentation.

            Press enter to continue...
        EOT
        STDIN.readline
    else
        puts "Distribution \"#{distro.first}\" detected."
        deps=get_dependencies(gems, distro.last[:dependencies])

        if deps.length==0
            return
        end

        puts "About to install these dependencies:"
        puts "* "<<deps.join("\n* ")
        puts
        puts "Press enter to continue..."
        STDIN.readline

        command=distro.last[:install_command]+" "<<deps.join(' ')
        puts command
        system command
    end
end

try_library :rubygems, <<-EOT.unindent
    rubygems required to use this tool

    Use one of these methods:

        * Debian/Ubuntu
            apt-get install rubygems libopenssl-ruby

        * RHEL/CENTOS
            yum install rubygems

        * SUSE
            zypper install rubygems

        * Specific rubygems package for your distro

        * Follow the instructions from http://rubygems.org/pages/download
EOT

try_library :mkmf, <<-EOT.unindent
    ruby development package is needed to install gems

    Use one of these methods:

        * Debian/Ubuntu
            apt-get install ruby-dev

        * RHEL/CENTOS
            yum install ruby-devel

        * Install the ruby development package for your distro
EOT

if ARGV.include?('-h')
    help
    exit(0)
end

if ARGV.length>0
    packages=ARGV
else
    packages=DEFAULT
end

gems_list=get_gems(packages)

dist=detect_distro

install_dependencies(gems_list, dist)

packages_string=gems_list.join(' ')

prefix=""

if dist && dist.last[:gem_env]
    prefix=dist.last[:gem_env].collect do |name, value|
        "#{name}=\"#{value}\""
    end.join(' ')+' '
end

command_string = "#{prefix}gem install --no-ri --no-rdoc"

install_warning(packages)

gems_list.each do |gem|
    cmd=command_string+" "<<gem
    puts cmd
    system cmd

    if $?!=0
        puts "Error installing #{gem}"
        exit(-1)
    end
end

