#!/usr/bin/ruby
# coding: utf-8

require 'time'
require 'rubygems'

class JMXParser
  require 'rexml/parsers/baseparser'
  require 'rexml/parsers/streamparser'
  require 'rexml/streamlistener'
  include REXML::StreamListener

  def initialize
    @path = ['']
    @xpath = nil
    @callback = proc
    @flag = nil
    @msgs = {}
    @row = nil
  end

  def endOfDocument
    @callback.call(@msgs.keys)
  end

  def tag_start(name, attrs)
    @path.push(name.sub(/^\w+:/, ''))
    @xpath = @path.join('/')
    case @xpath
    when '/Report/Body/MeteorologicalInfos/TimeSeriesInfo/Item/Kind/Property'
      @flag = true
      @row = {}
    end
  end

  def tag_end(name)
    case @xpath
    when '/Report/Body/MeteorologicalInfos/TimeSeriesInfo/Item/Kind/Property'
      if @row[:flag]
        msg = (@row[:text] || "#{@row[:type]}: #{@row[:flag]}. ")
        @msgs[msg] = true
      end
      @flag = false
    end
    @path.pop
    @xpath = @path.join('/')
    endOfDocument if @xpath == ''
  end

  def text(str)
    return unless @flag
    case @xpath
    when /\/Type$/ then @row[:type] = str
    when /\/Text$/ then @row[:text] = str
    when /\/PossibilityRankOfWarning$/ then
      case str
      when /中/ then
        unless @row[:flag] then @row[:flag] = $& end
      when /高/ then
        @row[:flag] = $&
      end
    end
  end

end

class App

  @@hacktitles = %w[
警報級の可能性(明日まで)
警報級の可能性(明後日以降)
  ]

  @@titles = %w[

津波警報・注意報・予報a
気象特別警報報知
津波情報a
全般気象情報
地方気象情報
府県気象情報
指定河川洪水予報
土砂災害警戒情報
警報級の可能性(明日まで)
警報級の可能性(明後日以降)
全般台風情報(定型)
記録的短時間大雨情報
竜巻注意情報(目撃情報付き)
特殊気象報
季節観測
噴火警報・予報
降灰予報(詳細)
生物季節観測
震度速報
異常天候早期警戒情報
地方高温注意情報
府県潮位情報
スモッグ気象情報
地方天候情報
地震の活動状況等に関する情報
地方1か月予報
地方3か月予報

  ]

  def initialize argv
    @argv = []
    @cutoff = nil
    argv.each{|arg|
      case arg
      when /^--cutoff=/ then
        @cutoff = Time.parse($').utc.strftime('%Y-%m-%dT%H:%M:%SZ')
      when /^--/ then
        raise "unknown option #{arg}"
      else @argv.push arg
      end
    }
    @db = {}
    @fixdb = {}
  end

  @@ioopts = {
    :invalid => :replace,
    :undef => :replace
  }

  def check row
    mtime, title, edof = row['mtime'], row['title'], row['edof']
    return if @cutoff and mtime < @cutoff
    return unless @@titles.include?(title)
    @db[title] = Hash.new unless @db[title]
    @db[title][edof] = [] unless @db[title][edof]
    rec = Hash.new
    rec[:rtime] = Time.parse(row['rtime']).localtime.strftime('%dT%H:%M')
    hdline = (row['hdline'] || '')
    hdline = nil if hdline.empty? and @@hacktitles.include?(title)
    rec[:hdline] = hdline
    ymd = Time.parse(row['mtime']).utc.strftime('%Y-%m-%d')
    uuid = row['msgid']
    @fixdb[uuid] = rec unless rec[:hdline]
    rec[:url] = "https://tako.toyoda-eizi.net/syndl/entry/#{ymd}/jmx/#{uuid}"
    @db[title][edof].unshift rec
  end

  def compile
    @argv.each{|fnam|
      File.open(fnam, 'rt') {|fp|
        fp.set_encoding('utf-8', @@ioopts)
        fp.each_line { |line|
          row = Hash[*(line.chomp.split(/\t/).map{|cell| cell.split(/:/,2)}.flatten)]
          check(row)
        }
      }
    }
  end

  def realmsg name, body
    listener = JMXParser.new {|msgs|
      @fixdb[name][:hdline] = msgs.join(' ') unless msgs.empty?
    }
    REXML::Parsers::StreamParser.new(body, listener).parse
  end

  def tarfile fnam
    require 'archive/tar/minitar'
    rawio = io = nil
    begin
      io = File.open(fnam, 'rb')
      io.set_encoding('BINARY')
    rescue Errno::ENOENT
      require 'zlib'
      rawio = File.open(fnam + ".gz", 'rb')
      rawio.set_encoding('BINARY')
      io = Zlib::GzipReader.new(rawio)
    end
    Archive::Tar::Minitar::Reader.open(io) { |tar|
      tar.each_entry {|ent|
        next unless @fixdb[ent.name]
        realmsg(ent.name, ent.read)
      }
    }
  ensure
    io.close if io
    rawio.close if rawio
  end

  def fix
    @argv.each{|fnam|
      tarfile fnam.sub(/(\.ltsv)?$/, '.tar')
    }
    ## filtering
    title_to_kill = []
    @@titles.each{|title|
      next unless @db[title]
      next unless @@hacktitles.include?(title)
      edof_to_kill = []
      @db[title].keys.each{|edof|
        mlist2 = []
        @db[title][edof].each{|rec|
        $stderr.puts "## #{title} #{edof} #{rec[:hdline]}"
          next unless rec[:hdline]
          next if rec[:hdline].empty?
          mlist2.push rec
        }
        @db[title][edof] = mlist2
        edof_to_kill.push edof if mlist2.empty?
      }
      edof_to_kill.each{|edof|
        @db[title].delete(edof)
      }
      title_to_kill.push title if @db[title].empty?
    }
    title_to_kill.each{|title|
      @db.delete(title)
    }
  end

  def subjline
    buf = ["~s 電文モニタ"]
    @@titles.each {|title|
      next unless @db[title]
      n = 0
      @db[title].each{|edof,mlist| n += mlist.size}
      buf.push "#{n}x#{title[0,4]}"
    }
    buf.join(', ')
  end

  def report
    @@titles.each {|title|
      writeln "= #{title}"
      unless @db[title]
        writeln "ありません。"
        next
      end
      writeln ''
      @db[title].each{|edof,mlist|
        bedof = edof.sub(/気象庁本庁/, '本庁').sub(/(管区気象台|地方気象台|測候所)$/, '')
        mlist.each {|msg|
          sgn = "[#{bedof} #{msg[:rtime]}] "
          sgn += msg[:hdline] if msg[:hdline]
          writeln sgn
          writeln "<#{msg[:url]}>"
        }
      }
      writeln ''
    }
    writeln '(END)'
    writeln subjline
  end

  def writeln str
    $stdout.puts str rescue Errno::EPIPE
  end

  def run
    compile
    fix
    report
  end

end

App.new(ARGV).run