#!/usr/bin/ruby

require 'optparse'
require 'cgi'
require 'net/http'
require 'rexml/document'


def expand(value, i, layer)
  while value =~ /(.*?)\$\{(.*?)\}(.*)/
    #warn "considering pre='#{$1}', key='#{$2}', post='#{$3}'"
    if $2 == "#i"
      sub = i.to_s
    else
      field = layer.elements[$2]
      sub = field ? field.text : "";
    end
    #warn "gluing '#{$1}' + '#{sub}' + '#{$3}'"
    value = $1 + sub + $3;
  end

  return value
end


options = { :skip => 0 }

opts = OptionParser.new do |x|
  x.banner = "Usage: #$0 [options] <realmUrl> <query> <field1>=<value1> %<field2>..."
  x.on("-v", "--verbose", "Verbose: commentate on what is being done") {
    options[:verbose] = true
  }
  x.on("-d", "--dry-run", "Dry run only: show what WOULD be done") {
    options[:dryRun] = true
  }
  x.on("-k", "--skip NUM", "Skip the first NUM records (do not edit)") { |n|
    options[:skip] = Integer(n)
  }
  x.on("-s", "--safe", "Safe: do not overwrite existing local values") {
    options[:safe] = true
  }
  x.on("-S", "--super-safe", "Safe: do not override inherited value") {
    options[:super_safe] = true
  }
end

begin
  opts.parse!(ARGV)
  raise "#$0: No realmUrl specified" if ARGV.size < 1
  raise "#$0: No query specified" if ARGV.size < 2
  raise "#$0: No substitutions specified" if ARGV.size < 3
rescue
  $stderr.puts $!, opts
  $stderr.puts "e.g. #$0 usi.indexdata.com/torus2/searchable.production udb=ABCCLIOAG supported=1"
  exit 1
end

url = ARGV.shift.sub(/^http:\/\//, "")
query = ARGV.shift
host, path = url.split("/", 2)
path.sub!(/\/$/, "");
sourceUrl = "/" + path + "/records/?layers=override,final&query=" + CGI::escape(query)
#puts "host=#{host}, path=#{path}, query=#{query}, sourceUrl=#{sourceUrl}"

Conn = Net::HTTP.new(host, 80)
resp, data = Conn.get(sourceUrl)
if resp.code !~ /^2/
  raise "HTTP GET error #{resp.code} #{resp.msg}\n#{resp.body}"
end

doc = REXML::Document.new(data)
i = 0
doc.elements.each('records/record') do |record|
  i += 1
  overrideLayer = record.elements['layer[@name="override"]']
  finalLayer = record.elements['layer[@name="final"]']
  id = overrideLayer.elements['id'].text
  print "##{i} #{id}:"
  if (i <= options[:skip]) then
    puts " skipping"
    next
  end

  substitutions = ""
  ARGV.each do |kv|
    key, value = kv.split("=", 2)

    if options[:safe] || options[:super_safe]
      layer = options[:super_safe] ? finalLayer : overrideLayer
      old = layer.elements[key]
      if old
        warn "old #{key} = '#{old.text}'" if options[:verbose]
        next
      end
    end

    if value
      tmp = expand(value, i, finalLayer)
      if (tmp != value)
        warn "#{key} '#{value}' -> '#{tmp}'" if options[:verbose]
        value = tmp
      end
      substitutions += "    <#{key}>#{value}</#{key}>\n"
    elsif kv =~ /^%/
      substitutions += "    <#{kv.sub(/^%/, '')} gone='yes'/>\n"
    else
      raise "unrecognised substitution '#{kv}'"
    end
  end

  if substitutions == ""
    warn "no substitutions for record #{i} (id=#{id}), skipping" if options[:verbose]
    puts
    next
  end

  xml = %[\
<record type="searchable">
  <layer name="override">
#{substitutions}  </layer>
</record>
]

  fullpath = "/#{path}/records/#{CGI.escape(id).gsub('+', '%20')}/"
  fullurl = "http://#{host}#{fullpath}"

  if options[:dryRun] then
    puts "\n#{fullurl}\n#{xml}"
  else
    resp, data = Conn.put(fullpath, xml,
                          { "Content-Type" => "application/xml" })
    if resp.code =~ /^2/
      puts " ok"
    elsif resp.code =~ /^3/
      puts "\n#{fullurl}\n#{xml}\n#{resp.code} #{resp.message} #{resp['Location']}"
    else
      puts "\n#{fullurl}\n#{xml}\n#{resp.code} #{resp.message}\n#{resp.body}"
    end
  end
end
