#!/usr/bin/ruby

=begin
* reads libECBufr's bufr_decoder -dump
* writes synoplike text
=end

class DCD

  Inf = 1.0 / 0.0

  def initialize hdr
    @h = {}
    @v = {}
    @s = nil
    @c = {}
    @hdr = hdr
  end

  attr_accessor :hdr

  def push k, v
    @h[k] = [] unless @h.include?(k)
    @h[k].push Hash[:val, v, :coord, @c.dup]
    @c[k] = v if /\A00/ === k
    self
  end

  def val d, pos = 0, quiet = false
    v = @h[d]
    unless v
      STDERR.puts "#{d}: missing" unless quiet
      return nil
    end
    v[pos][:val]
  end

  def sval d, pos = 0, quiet = false
    v = val(d, pos, quiet)
    case v
    when /"([^" ][^"]*)"/ then $1.strip
    when /" *"/ then '/EMPTY/'
    else nil
    end
  end

  def nval d, pos = 0, quiet = false
    v = val(d, pos, quiet)
    case v
    when 'MSNG' then nil
    when /\A-?\d+\z/ then v.to_i
    when /\A-?[.\d]+\z/ then v.to_f
    else nil
    end
  end

  def ncoord d, axis, pos = 0
    v = @h[d]
    unless v
      STDERR.puts "#{d}: missing"
      return nil
    end
    v = v[pos][:coord]
    unless v
      STDERR.puts "#{pos}: missing for #{d}"
      return nil
    end
    v = v[axis]
    case v
    when 'MSNG' then nil
    when /\A-?\d+\z/ then v.to_i
    when /\A-?[.\d]+\z/ then v.to_f
    else nil
    end
  end

  def compile
    return if @s
    # extract values
    @v['2Y'] = nval('004003')
    @v['2G'] = nval('004004')
    @v['2g'] = nval('004005')
    @v['iw'] = 0
    @v['2I'] = nval('001001', 0, :quiet)
    if @v['2I']
      @v['3i'] = nval('001002')
    else
      @v['DD'] = sval('001011')
      @v['3La'] = nval('005002')
      @v['4Lo'] = nval('006002')
    end
    @v['ix'] = nval('002001') 
    @v['h'] =  nval('020013') 
    @v['VV'] = nval('002001') 
    @v['N'] =  nval('020010') 
    @v['2d'] = nval('011001') 
    @v['2f'] = nval('011002') 
    @v['3T'] = nval('012101') 
    @v['3Td'] = nval('012103') 
    @v['3U'] = nval('013003') 
    @v['4P']  = nval('010004') 
    @v['4P0'] = nval('010051') 
    if @v['2I']
      @v['a3'] = nval('007004')
      @v['3h'] = nval('010009')
    end
    @v['3R'] = nval('013011')
    @v['tR'] = ncoord('013011', '004024')
    @v['a']  = nval('010063') 
    @v['3p'] = nval('010061') 
    @v['2w'] = nval('020003') 
    @v['w1'] = nval('020004') 
    @v['w2'] = nval('020005') 
    @v['Nh'] = nval('020011') 
    @v['CL'] = nval('020012', 0) 
    @v['CM'] = nval('020012', 1) 
    @v['CH'] = nval('020012', 2) 
    # adjust representation
    # iR
    @s = @v.dup
    @s['iR'] = case @v['3R'] when 0 then 3 when Float then 1 else 4 end 
    # ww, ix
    case @v['2w']
    when 0..99
      @s['ix'] = 1
    when 100..199
      @s['ix'] = 7
      @s['2w'] = @v['2w'] - 100
    when 508
      @s['ix'] = 2
      @s['2w'] = nil
    else
      @s['ix'] = 3
      @s['2w'] = nil
    end
    @s['ix'] += 3 if @v['ix'] == 0 and @s['ix'] != 7
    if @v['w1'] and @v['w1'] >= 10
      @s['w1'] = (@v['w1'] % 10)
      if [1, 4].include?(@s['ix'])
        if @v['w1'] == 10 then
	  @s['w1'] = nil
	else
	  STDERR.puts "Override ix(#{@s['ix']}) = 7 as w1 = #{@v['w1']}"
	  @s['ix'] = 7
	end
      else
        @s['ix'] = 7
      end
    end
    if @v['w2'] and @v['w2'] >= 10
      @s['w2'] = (@v['w2'] % 10)
      if [1, 4].include?(@s['ix'])
        if @v['w2'] == 10 then
	  @s['w2'] = nil
	else
	  STDERR.puts "Override ix(#{@s['ix']}) = 7 as w2 = #{@v['w2']}"
	  @s['ix'] = 7
	end
      else
	@s['ix'] = 7
      end
    end
    # h
    @s['h'] = case @v['h']
      when 0...50 then 0
      when 50...100 then 1
      when 100...200 then 2
      when 200...300 then 3
      when 300...600 then 4
      when 600...1000 then 5
      when 1000...1500 then 6
      when 1500...2000 then 7
      when 2000...2500 then 8
      when 2500...Inf then 9
      else nil
      end
    # VV
    @s['2V'] = case @v['2V']
      when 0..5000 then ((@v['2V'] + 50) / 100).to_i
      when 5000..30_000 then ((@v['2V'] + 50500) / 1000).to_i
      when 30_000..70_000 then ((@v['2V'] - 30_000) / 5000).to_i + 80
      when 70_000..Inf then 89
      else nil
      end
    # N
    @s['N'] = ((@v['N'] + 6) / 12.5).to_i if @v['N']
    # 2d
    @s['2d'] /= 10 if @s['2d']
    # 2f, 3f
    case @v['2f']
    when 0...100
      @s['2f'] = @v['2f'].to_i
    when Numeric
      @s['3f'] = @v['2f'].to_i
      @s['2f'] = 99
    end
    # 3T
    case @v['3T']
    when 0...273.15
      @s['s'] = 1
      @s['3T'] = ((273.2 - @v['3T']) * 10).to_i
    when Numeric
      @s['s'] = 0
      @s['3T'] = ((@v['3T'] - 273.1) * 10).to_i
    else
      @s['s'] = @s['3T'] = nil
    end
    # 3Td
    case @v['3Td']
    when 0...273.15
      @s['sd'] = 1
      @s['3Td'] = ((273.2 - @v['3Td']) * 10).to_i
    when Numeric
      @s['sd'] = 0
      @s['3Td'] = ((@v['3Td'] - 273.1) * 10).to_i
    else
      @s['sd'] = @s['3Td'] = nil
    end
    # 4P
    @s['4P'] = ((@v['4P'] + 5) / 10) % 10000 if @v['4P']
    @s['4P0'] = ((@v['4P0'] + 5) / 10) % 10000 if @v['4P0']
    if @v['2I']
      @s['a3'] = case @v['a3']
	when 1000_0 then 1
	when 925_0 then 2
	when 850_0 then 8
	when 700_0 then 7
	when 500_0 then 5
	else nil
	end
      @s['3h'] = (@v['3h'] % 1000) if @v['3h']
    else
      la = @v['3La'].to_f
      lo = @v['4Lo'].to_f
      @s['Qc'] = if lo >= 0 and la >= 0 then 1
        elsif lo >= 0 then 3
	elsif la >= 0 then 7
	else 5
	end
      @s['3La'] = (la.abs * 10).to_i
      @s['4Lo'] = (lo.abs * 10).to_i
    end
    @s['3p'] = @v['3p'].abs if @v['3p']
    # 3R
    @s['tR'] = case @v['tR']
      when -6 then 1
      when -12 then 2
      when -18 then 3
      when -24 then 4
      when -1 then 5
      when -2 then 6
      when -3 then 7
      when -9 then 8
      when -15 then 9
      else nil
      end
    @s['3R'] = (@s['3R'] * 10 + 0.5).to_i if @s['3R']
    @s['3R'] = case @s['3R']
      when -1 then 990
      when 0..9 then 990 + @s['3R'].to_i
      when 10..9884 then (@v['3R'] + 5) / 10
      when 9885..Inf then 989
      else nil
      end
    # group 8
    @s['CL'] = case @v['CL']
      when 30..39 then @v['CL'] - 30
      when 59, 62, nil then nil
      else STDERR.puts("suspicious CL #{@v['CL']}"); nil
      end
    @s['CM'] = case @v['CM']
      when 20..29 then @v['CM'] - 20
      when 59, 61, nil then nil
      else STDERR.puts("suspicious CM #{@v['CM']}"); nil
      end
    @s['CH'] = case @v['CH']
      when 10..19 then @v['CH'] - 10
      when 59, 60, nil then nil
      else STDERR.puts("suspicious CH #{@v['CH']}"); nil
      end
  end

  def e *keys
    r = []
    for key in keys
      v = @s[key]
      n = ((/\A[1-9]/ === key) ? $&.to_i : 1)
      s = (v ? sprintf(sprintf('%%0%uu', n), v) : ('/' * n))
      s = "[#{s}]" if s.length > n
      r.push s
    end
    r.join
  end

  @@nnn = 0

  def fm12
    m = []
    if @s['2I']
      m.push e('2I', '3i')
    else
      m.push(@s['DD'] || 'SHIP')
      m.push e('2Y', '2G', 'iw')
      m.push ['99', e('3La')].join
      m.push e('Qc', '4Lo')
    end
    m.push e('iR', 'ix', 'h', '2V')
    m.push e('N', '2d', '2f')
    m.push ['00', e('3f')].join if @s['3f']
    m.push ['1', e('s', '3T')].join
    m.push ['2', e('sd', '3Td')].join
    m.push ['3', e('4P')].join if @s['4P']
    if @s['a3']
      m.push ['4', e('a3', '3h')].join
    else
      m.push ['4', e('4P0')].join
    end
    m.push ['5', e('a', '3p')].join if @s['a']
    m.push ['6', e('3R', 'tR')].join if @s['tR']
    m.push ['7', e('2w', 'w1', 'w2')].join if [1, 4, 7].include?(@s['ix'])
    m.push ['8', e('Nh', 'CL', 'CM', 'CH')].join if @s['Nh'] and @s['CL']
    m.push ['9', e('GG', 'gg')].join if not @s['gg'].to_i.zero?
    m.last.sub!(/\z/, '=')
    f = []
    while not m.empty?
      f.push m[0,11].join(' ')
      m[0,11] = []
    end
    f.join("\r\r\n")
  end

  def report0 nnn
    compile
    f = []
    f.push "# this is not an official GTS report"
    f.push "# just converted from (#{@hdr.downcase})"
    f.push sprintf("ZCZC %03u     ", nnn % 1000)
    f.push ["SXXX99 XXXX ", e('2Y', '2G', '2g')].join
    if @s['2I'] then
      f.push ['AAXX ', e('2Y', '2G', 'iw')].join
    else
      f.push 'BBXX'
    end
    f.join("\r\r\n")
  end

  def report1
    compile
    if $VERBOSE
      for k, v in @v
	puts [k, v].inspect
      end
    end
    fm12
  end

  def report2
    "\nNNNN\r\r\n"
  end

end # class DCD

class Buffer

  def initialize
    @dcd = []
    @nnn = 0
  end

  def push dcd
    if @dcd.first and @dcd.first.hdr != dcd.hdr
      flush
    end
    @dcd.push dcd
  end

  def flush
    puts @dcd.first.report0(@nnn)
    @nnn += 1
    for dcd in @dcd
      puts dcd.report1
    end
    puts @dcd.last.report2
    @dcd = []
  end

end

hdr = nil
dcd = DCD.new(hdr)
buf = Buffer.new
for line in ARGF
  line.chomp!
  case line
  when /\A\z/ then
    buf.push dcd
    dcd = DCD.new(hdr)
  when /\A(0\d{5})( \{R=\d+\})? / then dcd.push($1, $')
  when /\AHEADER_STRING="(.*)"/ then
    hdr = $1.gsub(/\\040/, ' ').gsub(/\\01[25]/, '')
    dcd.hdr = hdr
  end
end
buf.flush