#!/usr/bin/env ruby

require 'gps_pvt'

# runnable quick example to solve PVT by using RINEX NAV/OBS or u-blox ubx

$stderr.puts <<__STRING__
Usage: #{__FILE__} GPS_file1 GPS_file2 ...
As GPS_file, rinex_nav(*.YYn, *.YYh, *.YYq, *.YYg), rinex_obs(*.YYo), and ubx(*.ubx) format are currently supported.
File format is automatically determined based on its extention described in above parentheses.
If you want to specify its format manually, --rinex_(nav|obs)=file_name or --ubx=file_name are available.
Supported RINEX versions are 2 and 3.
Note: YY = last two digit of year.
__STRING__

options = []
misc_options = {}

# check options and file format
files = ARGV.collect{|arg|
  next [arg, nil] unless arg =~ /^--([^=]+)=?/
  k, v = [$1.downcase.to_sym, $']
  next [v, k] if [:rinex_nav, :rinex_obs, :ubx].include?(k) # file type
  options << [$1.to_sym, $']
  nil
}.compact

options.reject!{|opt|
  case opt[0]
  when :start_time, :end_time
    require 'time'
    gpst_type = GPS_PVT::GPS::Time
    t = nil
    if opt[1] =~ /^(?:(\d+):)??(\d+(?:\.\d*)?)$/ then
      t = [$1 && $1.to_i, $2.to_f]
      t = gpst_type::new(*t) if t[0]
    elsif t = (Time::parse(opt[1]) rescue nil) then
      # leap second handling in Ruby Time is system dependent, thus 
      #t = gpst_type::new(0, t - Time::parse("1980-01-06 00:00:00 +0000"))
      # is inappropriate.
      subsec = t.subsec.to_f
      t = gpst_type::new(t.to_a[0..5].reverse)
      t += (subsec + gpst_type::guess_leap_seconds(t))
    else
      raise "Unknown time format: #{opt[1]}"
    end
    case t
    when gpst_type
      $stderr.puts(
          "#{opt[0]}: %d week %f (a.k.a %04d/%02d/%02d %02d:%02d:%02.1f)" \
            %(t.to_a + t.utc))
    when Array
      $stderr.puts("#{opt[0]}: #{t[0] || '(current)'} week #{t[1]}")
    end
    misc_options[opt[0]] = t
    true
  else
    false
  end
}

# Check file existence and extension
files.collect!{|fname, ftype|
  raise "File not found: #{fname}" unless File::exist?(fname)
  ftype ||= case fname
  when /\.\d{2}[nhqg]$/; :rinex_nav
  when /\.\d{2}o$/; :rinex_obs
  when /\.ubx$/; :ubx
  else
    raise "Format cannot be guessed, use --(format, ex. rinex_nav)=#{fname}"
  end
  [fname, ftype]
}

rcv = GPS_PVT::Receiver::new(options)

proc{
  run_orig = rcv.method(:run)
  t_start, t_end = [nil, nil]
  tasks = []
  task = proc{|meas, t_meas, *args|
    t_start, t_end = [:start_time, :end_time].collect{|k|
      res = misc_options[k]
      res.kind_of?(Array) \
          ? GPS_PVT::GPS::Time::new(t_meas.week, res[1]) \
          : res
    }
    task = tasks.shift
    task.call(*([meas, t_meas] + args))
  }
  tasks << proc{|meas, t_meas, *args|
    next nil if t_start && (t_start > t_meas)
    task = tasks.shift
    task.call(*([meas, t_meas] + args))
  }
  tasks << proc{|meas, t_meas, *args|
    next nil if t_end && (t_end < t_meas)
    run_orig.call(*([meas, t_meas] + args))
  }
  rcv.define_singleton_method(:run){|*args|
    task.call(*args)
  }
}.call if [:start_time, :end_time].any?{|k| misc_options[k]}

puts rcv.header

# parse RINEX NAV
files.each{|fname, ftype|
  rcv.parse_rinex_nav(fname) if ftype == :rinex_nav
}

# other files
files.each{|fname, ftype|
  case ftype
  when :ubx; rcv.parse_ubx(fname)
  when :rinex_obs; rcv.parse_rinex_obs(fname)
  end
}