#!/usr/bin/ruby

require 'optparse'
require 'rexml/document'
require 'pathname'
require "suse/connect"

def search_pkgs_in_repos(options, search_pat)
  base_path = '/var/cache/zypp/solv'
  repos = Dir[File.join(base_path, '*')]
  
  local_results = []
  ret = []
  
  repos.each do | repo |
    pn = Pathname.new(repo)
    reponame = pn.basename.to_s
    pkg_status = 'Available'
  
    if reponame == '@System'
      reponame = 'Installed'
      pkg_status = 'Installed'
    end
  
    index_file = File.join(repo, 'solv.idx')
    begin
      File.open(index_file).each do | line |
        components = line.chomp.split("\t")
        name = components[0]
        version = components[1]
	release = ''
	version_l = version.split('-')
	if version_l.size > 1
	  release = version_l.pop
	  version = version_l.join('-')
	end
        arch = components[2]
        local_results.push([name, version, release, arch, reponame, pkg_status, '', ''])
      end
    rescue => e
      print "Cannot read index for repository #{reponame}.\n"
    end
  end
  
  local_results.each do | p |
    search_pat.each do | q |
      if options[:match_exact]
        if p[0] == q
          ret.push p
        end
      else
        if p[0].include? q
          ret.push p
        end
      end
    end
  end
  return ret
end

options = {
    :match_exact => false,
    :exact_match => false,
    :xmlout => false,
    :group_by_module => false,
    :details => false
}

STDOUT.sync = true

save_argv = Array.new(ARGV)
params = ARGV

OptionParser.new do |opts|
  opts.banner = "Usage: zypper search-packages [options] package1 [package2 [...]]

Extended search for packages covering all potential SLE modules by querying RMT/SCC.
This operation needs access to a network.

Same as for the normal search operation the search string can be a part of a package
name unless the option --match-exact is used.

"

  opts.on("-x", "--match-exact", "Searches for an exact match of the search strings.") do |a|
    options[:match_exact] = a
  end
  opts.on("-g", "--group-by-module", "Group the results by module (default: group by package)") do |a|
    options[:group_by_module] = a
  end
  opts.on("-d", "--details", "Display more detailed information about found packages") do |a|
    options[:details] = a
  end
  opts.on("", "--xmlout", "Switch to XML output") do |a|
    options[:xmlout] = a
  end

end.parse!

results = []

params.each do |pkg_name|
  begin
    found = SUSE::Connect::PackageSearch.search pkg_name
  rescue => e
    print "Could not search for the package: #{e.class}: #{e.message}\n"
    found = []
  end

  if options[:match_exact]
    found.select! do | pkg |
      pkg["name"] == pkg_name
    end
  end

  found.each do | pkg |
    name = pkg["name"]
    version = pkg["version"]
    release = pkg["release"]
    arch = pkg["arch"]
    pkg["products"].each do | product |
      p_name = product["name"]
      p_id = product["identifier"]
      p_name = "#{p_name} (#{p_id})"
      p_edition = product["edition"]
      p_arch = product["architecture"]
      results.push([name, version, release, arch, p_id, p_name, p_edition, p_arch])
    end
  end
end


repo_results = search_pkgs_in_repos(options, params)
results.concat repo_results

if results.empty?
  if ! options[:xmlout]
    puts "No package found\n\n"
    exit 0
  end
end

if options[:xmlout]
  doc = REXML::Document.new "<packages></packages>"
  results.each do | entry |
    x_package = REXML::Element.new "package"
    x_name = REXML::Element.new "name"
    x_name.add_text "#{entry[0]}-#{entry[1]}-#{entry[2]}.#{entry[3]}"
    x_module = REXML::Element.new "module"
    x_module.add_text "#{entry[4]}"
    x_package.add_element x_name
    x_package.add_element x_module
    doc.root.push x_package
  end
  doc << REXML::XMLDecl.new
  doc.write($stdout, 2)
  puts "\n"
  exit 0
end


puts "Following packages were found in following modules:\n\n"

if options[:details]
  results.map! { | entry |
    [ "#{entry[0]}-#{entry[1]}-#{entry[2]}.#{entry[3]}", entry[4] ]
  }
else
  results.map! { | entry |
    [ entry[0], entry[5] ]
  }
end
results.uniq!

header = ["Package", "Module or Repository"]
if options[:group_by_module]
  modules = {}
  results.each do | entry |
    modules[entry[1]] ||= [];
    modules[entry[1]].push entry[0];
  end
  results = []
  modules.each do | mod, packages |
    pkg = packages.shift
    results.push [ mod, pkg ]
    packages.each do | pkg |
      results.push [ "", pkg ]
    end
  end
  header = ["Module or Repository", "Package"]
end
#else
  results.unshift header
  max_lengths = [0, 0]
  results.each do |x|
    x.each_with_index do |e, i|
      s = e.size
      max_lengths[i] = s if s > max_lengths[i]
    end
  end

  separator = max_lengths.map { |_| "-" * _ }
  results.insert(1, separator)

  format = max_lengths.map { |_| "%-#{_}s" }.join(" " * 2)
  results.each do | entry |
    puts format % entry
  end
#end
puts "\n"
puts "To activate the respective module or product, use SUSEConnect --product.\nUse SUSEConnect --help for more details.\n\n"

exit 0


