#!/usr/bin/ruby

require 'time'

class App

  def help
    $stderr.puts "usage: #$0 [-opts] maptime outfile.(json|html) [zsort.txt ...]"
    exit 16
  end

  def initialize argv
    @flags = {}
    while /^-(\w+)(?:[:=](.*))?/ === argv.first
      argv.shift
      @flags[$1] = ($2 || true)
    end
    maptime = argv.shift
    help if maptime.nil?
    @outfile = argv.shift
    @files = argv
    @maptime = Time.parse(maptime).utc
    @level = 'sfc'
    @merge = {}
  rescue => e
    $stderr.puts "#{e.class}: #{e.message}"
    help
  end

  def htmlhead
    windbase = (@flags['WD'] ||
      'https://raw.githubusercontent.com/etoyoda/wxsymbols/master/img/')
    wxbase = (@flags['WX'] || @flags['WD'] ||
      'https://toyoda-eizi.net/wxsymbols/')
    bt = @maptime.strftime('%Y%m%d%H%M%S')
    <<HTML
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>bufrsort #{@maptime} #{@level}</title>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.6.0/dist/leaflet.css"
  integrity="sha512-xwE/Az9zrjBIphAcBb3F6JVqxf46+CDLwfLMHloNu6KEQCAWi6HcDUbeOfBIptF7tcCzusKFjFw2yuvEpDL9wQ=="
  crossorigin=""/>
<style type="text/css">
.stn { width: 64px; height: 64px; }
.wb { width: 64px; height: 64px; position: absolute; top: 0; left: 0; }
.cd { width: 16px; height: 16px; position: absolute; top: 24px; left: 24px; }
.nw { font-size: 10px; line-height: 10px; text-shadow: 1px 1px 0 #FFF; 
  position: absolute; top: 20px; right: 38px; min-width: 5px; }
.ww { width: 20; height: 20; position: absolute; bottom: 11px; right: 32px; }
.cl { width: 14; height: 14; position: absolute; top: 38px;    left: 30px; }
.cm { width: 14; height: 14; position: absolute; bottom: 36px; left: 30px; }
.ch { width: 14; height: 14; position: absolute; bottom: 48px; left: 30px; }
</style>
<script src="https://unpkg.com/leaflet@1.6.0/dist/leaflet.js"
   integrity="sha512-gZwIG9x3wUXg2hdXF6+rVkLF/0Vi9U8D2Ntg4Ga5I5BZpVkVxlJWbSQtXPSiUTtC0TjtGOmxa1AJPuV0CPthew=="
   crossorigin=""></script>
<script id="jsmain" type="text/javascript">
function plot(lgplot, obs) {
  const wxbase = '#{wxbase}';
    if (obs.La && obs.Lo) { 
      var dd = 'nil';
      var ff = 'nil';
      if (obs.d !== null) {
        dd = Math.floor((obs.d + 5) / 10);
        if (dd == 0) { dd = 36; }
      }
      if (obs.f !== null) {
        ff = Math.floor((obs.f + 1.25) / 2.5) * 5;
        if (ff > 100) {
          ff = Math.floor((obs.f + 2.5) / 5) * 10;
          if (ff > 150) { ff = 200; }
        }
        if ((ff == 0) && (obs.f > 0)) { ff = 5; }
        if (ff == 0) { dd = 0; }
      }
      var bn = 'd' + dd + 'f' + ff + '.png';
      var url = '#{windbase}' + bn;
      var nbn = 'nnil.png';
      if (obs.N === null) {
        if (!('ix' in obs)) {
          nbn = 'n9.png';
        } else if (obs.ix == 0) {
          nbn = 'nauto.png';
        }
      } else {
        var n = Math.floor((obs.N + 6) / 12.5);
	nbn = 'n' + n + '.png';
      }
      var surl = '#{windbase}' + nbn;
      var ts = '';
      if (typeof obs.T === 'number') {
        ts = '<div class="nw">' + Math.round(obs.T - 273.15) + '</div>';
      }
      var wx = '';
      if (typeof obs.w === 'number') {
        switch (obs.w) {
        case 0: case 1: case 2: case 3:
        case 100: case 101: case 102: case 103: case 508: case 509: case 510:
          break;
        default:
          wx = ('<img class="ww" src="' + wxbase + 'w' + obs.w + '.svg" alt="w' + obs.w + '" />');
        }
      }
      var cl = '';
      if (obs.CL) { cl = '<img class="cl" src="' + wxbase + 'cl' + obs.CL + '.svg" />'; }
      var cm = '';
      if (obs.CM) { cm = '<img class="cm" src="' + wxbase + 'cm' + obs.CM + '.svg" />'; }
      var ch = '';
      if (obs.CH) { ch = '<img class="ch" src="' + wxbase + 'ch' + obs.CH + '.svg" />'; }
      var ht = '<div class="stn"><img class="wb" src="' + url +
        '" /><img class="cd" src="' + surl +
        '" />' + ts + wx + cl + cm + ch + '</div>';
      var ic = L.divIcon({html: ht, className: 'stn', iconSize: [64, 64], iconAnchor: [32, 32]});
      var opt = {icon: ic, title: obs['@']};
      var pop = '<table border=1>';
      for (const field in obs) {
        pop += '<tr><th>' + field + '</th><td>' + obs[field] + '</td></tr>';
      }
      pop += '</table>';
      L.marker([obs.La, obs.Lo], opt).bindPopup(pop).addTo(lgplot);
    }
}
function init() {
  var tile1 = L.tileLayer('https://cyberjapandata.gsi.go.jp/xyz/pale/{z}/{x}/{y}.png', {
    attribution: '<a href="https://maps.gsi.go.jp/development/ichiran.html">地理院タイル</a>(淡色)',
    maxZoom: 14
  });
  var tile2 = L.tileLayer('https://cyberjapandata.gsi.go.jp/xyz/english/{z}/{x}/{y}.png', {
    attribution: '<a href="https://maps.gsi.go.jp/development/ichiran.html">地理院タイル</a>(標高)',
    maxZoom: 14
  });
  var tile3 = L.tileLayer('https://www.jma.go.jp/bosai/himawari/data/satimg/#{bt}/fd/#{bt}/REP/ETC/{z}/{x}/{y}.jpg', {
    attribution: '<a href="https://www.data.jma.go.jp/sat_info/himawari/satobs.html">気象庁</a>',
  });
  var tile4 = L.tileLayer('https://www.jma.go.jp/bosai/himawari/data/satimg/#{bt}/fd/#{bt}/B13/TBB/{z}/{x}/{y}.jpg', {
    attribution: '<a href="https://www.data.jma.go.jp/sat_info/himawari/satobs.html">気象庁</a>',
  });
  var tile5 = L.tileLayer('https://www.jma.go.jp/bosai/himawari/data/satimg/#{bt}/fd/#{bt}/SND/ETC/{z}/{x}/{y}.jpg', {
    attribution: '<a href="https://www.data.jma.go.jp/sat_info/himawari/satobs.html">気象庁</a>',
  });
  var basemaps = {
    "淡色地図": tile1,
    "標高": tile2,
    "ひまわり色": tile3,
    "ひまわり赤外": tile4,
    "ひまわり雲頂": tile5
  };
  var decimal1 = new Intl.NumberFormat('en-US', { minimumFractionDigits: 1,
    maximumFractionDigits: 1 });
  var lgplot = L.layerGroup([], {attribution: '<a href="https://github.com/OGCMetOceanDWG/WorldWeatherSymbols/">OGC</a>'});
  var mymap = L.map('mapid', {
    center: [35.0, 135.0],
    zoom: 5,
    layers: [tile1, lgplot]
  });
  var cl = {"plot": lgplot};
  var uHimdst = '#{@flags['HIMDST']}';
  var uHrpns = '#{@flags['HRPNS']}';
  if (uHimdst) {
    var himdst = L.imageOverlay(uHimdst, [[20,110],[50,150]], {attribution: 'Himawari'});
    cl[uHimdst] = himdst;
  }
  var uGpv1 = '#{@flags['GPV1']}';
  if (uGpv1) {
    var gpv1 = L.imageOverlay(uGpv1, [[-85.043,-179.3],[85.043,179.3]], {attribution: 'JMA', opacity:0.8});
    mymap.addLayer(gpv1);
    cl[uGpv1] = gpv1;
  }
  var uGpv2 = '#{@flags['GPV2']}';
  if (uGpv2) {
    var gpv2 = L.imageOverlay(uGpv2, [[-85.043,-179.3],[85.043,179.3]], {attribution: 'JMA', opacity:0.8});
    mymap.addLayer(gpv2);
    cl[uGpv2] = gpv2;
  }
  var uGpv3 = '#{@flags['GPV3']}';
  if (uGpv3) {
    var gpv3 = L.imageOverlay(uGpv3, [[-85.043,-179.3],[85.043,179.3]], {attribution: 'JMA', opacity:0.8});
    mymap.addLayer(gpv3);
    cl[uGpv3] = gpv3;
  }
  var uGpv4 = '#{@flags['GPV4']}';
  if (uGpv4) {
    var gpv4 = L.imageOverlay(uGpv4, [[-85.043,-179.3],[85.043,179.3]], {attribution: 'JMA', opacity:0.8});
    cl[uGpv4] = gpv4;
  }
  var uGpv5 = '#{@flags['GPV5']}';
  if (uGpv5) {
    var gpv5 = L.imageOverlay(uGpv5, [[-85.043,-179.3],[85.043,179.3]], {attribution: 'JMA', opacity:0.8});
    cl[uGpv5] = gpv5;
  }
  var uGpv6 = '#{@flags['GPV6']}';
  if (uGpv6) {
    var gpv6 = L.imageOverlay(uGpv6, [[-85.043,-179.3],[85.043,179.3]], {attribution: 'JMA', opacity:0.8});
    cl[uGpv6] = gpv6;
  }
  var uGpv7 = '#{@flags['GPV7']}';
  if (uGpv7) {
    var gpv7 = L.imageOverlay(uGpv7, [[-85.043,-179.3],[85.043,179.3]], {attribution: 'JMA', opacity:0.8});
    cl[uGpv7] = gpv7;
  }
  if (uHrpns) {
    var hrpns = L.imageOverlay(uHrpns, [[21.942986,118.124957],[48.922485,151.874957]], {attribution: 'JMA HRPNS'});
    cl[uHrpns] = hrpns;
  }
  L.control.layers(basemaps, cl).addTo(mymap);
  for (i in data) {
    plot(lgplot, data[i]);
  }
  mymap.on('keydown', function(ev){
    if (ev.originalEvent.code == 'KeyU') {
      mymap.panTo([50, 30]);
    } else if (ev.originalEvent.code == 'KeyJ') {
      mymap.panTo([35, 135]);
    }
  });
}
</script>
<script id="jsdata" type="text/javascript">
var data =
HTML
  end

  def htmltail
    <<HTML
;
</script>
</head>
<body onLoad="init();">
<div id="mapid" style="width:100%;height:100%">map will be here</div>
</body>
</html>
HTML
  end

  def outjson io
    io.puts '['
    first = true
    @merge.each {|k, v|
      io.puts(v + ',')
    }
    io.puts '{"@":"dummy"}]'
  end

  def iopen
    if @files.empty? then
      yield $stdin
    else
      @files.each{|fnam|
        File.open(fnam, 'r:UTF-8') {|fp| yield fp }
      }
    end
  end

  def oopen
    case @outfile
    when nil, '-' then
      yield $stdout
    else
      File.open(@outfile, 'w:UTF-8') {|fp| yield fp }
    end
  end

  def run
    pat = @maptime.utc.strftime('^%Y-%m-%dT%H:%MZ/') + @level
    pattern = Regexp.new(pat)
    n = 0
    iopen() {|fp|
      fp.each_line{|line|
        next unless pattern === line
        n += 1
        k, v = line.chomp.split(/ /, 2)
        @merge[k] = v
      }
    }
    $stderr.puts "#{n} lines" if $VERBOSE
    oopen() {|ofp|
      ofp.write htmlhead if /\.html?$/ === @outfile
      outjson(ofp)
      ofp.write htmltail if /\.html?$/ === @outfile
    }
  end

end

$VERBOSE = true if $stderr.tty?
App.new(ARGV).run