#!/usr/bin/env ruby

module Jigdo
  require 'open-uri'

  class JIGDO
    def make_rc
    end

    def perror(str)
      print "#{@cmd_name}: Aborted - #{str}\n"
    end

    def cmd_name
      @cmd_name
    end

    def usage
      print "#{@cmd_name}/#{@jigdo_version} - Jigsaw Download for Momonga Linux\n\n"
      print "  Usage: #{@cmd_name} dotjigdo\n"
      exit
    end

    def fetch(uri)
      cmd_line  = @fetch_cmd + " "
      cmd_line += "--non-verbose --force-directories "
      cmd_line += "--directory-prefix=" + @fetch_dir + " "
      cmd_line += @fetch_opts.join(" ") + " " + uri
      `#{cmd_line} 2>&1`
      $? == 0
    end

    def fetch_cmd=(cmd)
      @fetch_cmd = cmd
    end

    def fetch_cmd
      @fetch_cmd
    end

    def fetch_opts=(opt)
      @fetch_opts = opt
    end

    def fetch_opts
      @fetch_opts
    end

    def fetch_dir
      @fetch_dir
    end

    def read_jigdo
      return unless @jigdo_name
      return if @readed != 0
      if @jigdo_name =~ /^(http|ftp):/
	begin
	  fi = open @jigdo_name
	  fo = open @jigdo_name.split(/\//)[-1], "w"
	  fo.write fi.read
	  fo.close
	  fi.close
	  @jigdo_name = File.expand_path @jigdo_name.split(/\//)[-1]
	rescue
	  self.perror "Can't get dot jigdo file.\n"
	  exit
	end
      end
      begin
	@jigdo_data = `zcat #{@jigdo_name} 2> /dev/null`
	if $? != 0
	  f = open @jigdo_name
	  @jigdo_data = f.readlines
	  f.close
	else
	  @jigdo_data = @jigdo_data.split($/)
	end
      rescue
	self.perror "Can't read dot jigdo file.\n"
	exit
      end
      index = 0
      while index < @jigdo_data.size
	break if @jigdo_data[index] =~ /^\[Include\s+(\S+)\]/
	index += 1
      end
      if index != @jigdo_data.size
	inc_uri = $1
	begin
          f = open(inc_uri)
          inc_data = f.readlines
          f.close
          @jigdo_data.delete_at(index)
          inc_data.each do |l|
            @jigdo_data.insert(index, l)
            index += 1
          end
        rescue
          @jigdo_data.delete_at(index)
          print "#{@cmd_name}: Warning - '[Include]' statement found, but file not found\n"
        end
      end
      @jigdo_name += ".unpacked"
      f = open @jigdo_name, "w"
      @jigdo_data.each do |l|
	f.print l, "\n" if l !~ /^#.*/
      end
      f.close
      @readed = 1
    end

    def retrieve_template
      return if @template_name !~ /^(http|ftp):/
      local_name = @template_name.split(/\//)[-1]
      begin
	fi = open @template_name
	fo = open local_name, "w"
	fo.write fi.read
	fo.close
	fi.close
	@template_name = File.expand_path local_name
      rescue
	self.perror "Can't retrieve jigdo template file.\n"
	exit
      end
    end

    def template_md5sum
      self.retrieve_template
      l = `#{@jigdo_cmd} md5sum --report=quiet #{@template_name}`
      l =~ /^\s*(\S+)\s*\S*$/
      $1
    end

    def get_tag(section, tag)
      self.read_jigdo
      found = 0
      @jigdo_data.each do |l|
	if found.zero? && l =~ /^\[#{section}\]/
	  found = 1
	else
	  break if l =~ /^#{tag}\s*=\s*(.+)/
	end
      end
      $1
    end

    def Version
      @version = self.get_tag "Jigdo", "Version"
    end

    def Generator
      @generator = self.get_tag "Jigdo", "Generator"
    end

    def Filename
      @filename = self.get_tag "Image", "Filename"
    end

    def Template
      @template = self.get_tag "Image", "Template"
    end

    def Template_MD5Sum
      @template_md5sum = self.get_tag "Image", "Template-MD5Sum"
    end

    def ShortInfo
      @shortinfo = self.get_tag "Image", "ShortInfo"
    end

    def Info
      @info = self.get_tag "Image", "Info"
    end

    def jigdo_name
      @jigdo_name
    end

    def template_name
      @template_name
    end

    def Servers
      @servers = Hash.new
      self.read_jigdo
      found = 0
      @jigdo_data.each do |l|
	if found.zero? && l =~ /^\[Servers\]/
	  found = 1
	elsif found == 1
	  break if l =~ /^\[.+\]/
	  label, uri = l.split(/=/)
	  unless label.nil? || uri.nil?
	    if @servers.has_key?(label)
	      @servers[label] += [uri]
	    else
	      @servers[label] = [uri]
	    end
	  end
	end
      end
      @servers
    end

    def Parts
      @parts = Array.new
      self.read_jigdo
      found = 0
      @jigdo_data.each do |l|
	if found.zero? && l =~ /^\[Parts\]/
	  found = 1
	elsif found == 1
	  break if l =~ /^\[.+\]/
	  (md5, uri) = l.split(/=/)
	  (label, path) = uri.split(/:/)
	  unless md5.nil? || label.nil? || path.nil? 
	    @parts += [[path, md5, label]]
	  end
	end
      end
      @parts
    end

    def makeImage(path)
      cmd_line  = @jigdo_cmd
      cmd_line += " make-image "
      cmd_line += "--image=" + self.Filename + " "
      cmd_line += "--jigdo=" + @jigdo_name + " "
      cmd_line += "--template=" + @template_name + " "
      cmd_line += "--cache=jigdo-file-cache.db "
      cmd_line += path
      `#{cmd_line}`
      $? == 0
    end

    def verifyImage
      cmd_line  = @jigdo_cmd
      cmd_line += " verify "
      cmd_line += "--image=" + self.Filename + " "
      cmd_line += "--jigdo=" + @jigdo_name + " "
      cmd_line += "--template=" + @template_name + " "
      cmd_line += "--cache=jigdo-file-cache.db"
      `#{cmd_line}`
      $? == 0
    end

    def lostFiles
      cmd_line  = @jigdo_cmd
      cmd_line += " print-missing-all "
      cmd_line += "--image=" + self.Filename + " "
      cmd_line += "--jigdo=" + @jigdo_name + " "
      cmd_line += "--template=" + @template_name + " "
      cmd_line += "--cache=jigdo-file-cache.db"
      # .split($/) $B$@$H:G8e$N6u9T$,:o$i$l$k(B
      files = Array.new
      `#{cmd_line}`.each_line do |l|
	files += [l.chop]
      end
      @lost_files = Array.new
      i = 0
      files.each do |l|
	if l =~ /^(http|ftp|MD5Sum):/
	  l = l.split(/:/)[-1] if l =~ /^MD5Sum/
	  if @lost_files[i]
	    @lost_files[i] += [l]
	  else
	    @lost_files[i] = [l]
	  end
	elsif l =~ /^$/
	  i += 1
	end
      end
      @lost_files
    end

    def mergeFiles
      cmd_line  = @jigdo_cmd
      cmd_line += " --cache=jigdo-file-cache.db "
      cmd_line += "--no-cache "
      cmd_line += "make-image "
      cmd_line += "--image=" + self.Filename + " "
      cmd_line += "--jigdo=" + @jigdo_name + " "
      cmd_line += "--template=" + @template_name + " "
      cmd_line += @fetch_dir
      `#{cmd_line}`
      $? == 0
    end

    def checkFile(uri, sum1)
      cmd_line  = @jigdo_cmd
      cmd_line += " md5sum --report=quiet "
      cmd_line += @fetch_dir + "/" + uri.split(/\/\//)[-1]
      `#{cmd_line}` =~ /^\s*(\S+)\s*\S*$/
      sum2 = $1
      sum1 == sum2
    end

    def cleanUp
      `rm -rf #{@fetch_dir}`
      File.delete @jigdo_name if File.readable? @jigdo_name
      File.delete "jigdo-file-cache.db" if File.readable? "jigdo-file-cache.db"
    end

    def initialize(dotjigdo)
      @cmd_name = $0.split(/\//)[-1]
      @jigdo_cmd = "jigdo-file"
      @jigdo_version = `#{@jigdo_cmd} --version`.split[-1]
      self.fetch_cmd = "wget"
      self.fetch_opts  = ["--non-verbose", "--passive-ftp", "--dot-style=mega"]
      self.fetch_opts += ["--continue", "--timeout=60"]
      self.usage if dotjigdo == nil
      @readed = 0
      @jigdo_name = dotjigdo
      if @jigdo_name !~ /^(http|ftp):/
	@jigdo_name = File.expand_path @jigdo_name
      end
      jigdo_base = @jigdo_name.split(/\//)[0..-2].join("/")
      @fetch_dir = @jigdo_name.split(/\//)[-1] + ".fetch"
      @template_name = self.Template
      if @template_name !~ /^(http|ftp):/
	@template_name = jigdo_base + "/" + @template_name
      end
    end
  end

end

# main
jigdo = Jigdo::JIGDO.new(ARGV[0])

print "Checking template MD5SUM - "
sum1 = jigdo.Template_MD5Sum
sum2 = jigdo.template_md5sum
if sum1 != sum2
  print "NG\n"
  jigdo.perror "MD5SUM mismatch\n"
  print "MD5SUM - .jigdo:#{sum1} .template:#{sum2}\n"
  exit
end
print "OK\n"

# check already have files
print "Files to scan: "
str = STDIN.gets

if str !~ /^$/
  if jigdo.makeImage(str.chop) && File.readable?(jigdo.Filename)
    jigdo.verifyImage
    jigdo.cleanUp
    exit 0
  end
end

prev_lost = 0
while 1
  lost_files = jigdo.lostFiles
  break if lost_files.size.zero?
  if prev_lost == lost_files.size
    print "#{jigdo.cmd_name}: Can't retrieve any files.\n"
#     lost_files.each do |lf|
#       print " ", lf, "\n"
#     end
    print "Retry?(y/n)"
    k = STDIN.gets.chop
    exit(1) if k == "n"
  end
  prev_lost = lost_files.size
  unless File.directory? jigdo.fetch_dir
    Dir.mkdir jigdo.fetch_dir
  end
  i = 0
  lost_files.each do |lf|
    lf.each do |uri|
      next if uri !~ /^(http|ftp):/
      print "Retrieving #{uri.split(/\//)[-1]} @ #{uri.split(/\//)[2]} - \e[0J"
      STDOUT.flush
      if jigdo.fetch(uri)
	if jigdo.checkFile(uri, lf[-1])
	  print "OK\n"
	  break
	else
	  print "\r"
	end
      else
	print "\r"
      end
    end
    i += 1
    break if i >= 10
  end
  jigdo.mergeFiles
  `rm -rf #{jigdo.fetch_dir}/*`
  print "\n"
end

if File.readable?(jigdo.Filename)
  if jigdo.verifyImage
    jigdo.cleanUp
    exit 0
  else
    exit 1
  end
end

exit 1
