# $Id: importNOS_CurSubs.rb 7604 2022-01-08 14:42:26Z flaterco $
# Functions for importNOS.rb:
# Current subordinate station import

def importCurSubs(db)

  if File.exist?("renamings-cursubs.sql")
    puts "Don't want to overwrite renamings-cursubs.sql; do something with it"
    exit
  end

  stateMap = getOldStateMap(db)

  # The undocumented MDAPI (1.0) geogroup children query (getting stations)
  # does not work for currents.  In lieu of that, geogroups are parsed out of
  # the old style station list web pages by parse-oldcurlists.rb and left in
  # the table currents_geogroups.  This needn't be done if there are no new
  # stations.  getCurrentsGeogroups returns an empty hash if the table is not
  # present.
  currentsGeogroups = getCurrentsGeogroups(db)

  currents = readCurJson()
  File.open("renamings-cursubs.sql","w") {|renamings|
    currents.each {|csta|

      # Avoid adding code that isn't exercised.
      raise "Notices are not null anymore" unless csta["notices"].nil?
      raise "Disclaimers are not null anymore" unless csta["disclaimers"].nil?

      # type is H, S, W, or Y.
      #   H = harmonic
      #   S = subordinate
      #   W = weak and variable with null offsets
      #   Y = undocumented ?  Is that like X only 1 more?
      # 2020-12 type Y has gone away.
      type = csta["type"]
      raise "Unknown type #{type}" unless "HSW".include?(type)
      next if type != "S"

      sid = csta["id"]
      sidp = sid + "_" + csta["currbin"].to_s
      cname = csta["name"]
      comments = notes = legal = nil

      if isMaybeNotFreeCurrent(sid)
	print "Skipped ", sidp, " ", cname, " (non-US)\n"
	next
      end

      offsets = csta["currentpredictionoffsets"]
      refsid = offsets["refStationId"]
      raise "Current sub with no refsid" if refsid.nil? or refsid.empty?
      refsidp = refsid + "_" + offsets["refStationBin"].to_s

      # Demand exactly one ref station with refsidp.
      # Count 0  - complain.
      # Count 1  - good.
      # Count >1 - throw.
      if !refStationExists(db, refsidp)
	temp = refsidp.upcase
	if !refStationExists(db, temp)
	  print "** Reference station #{refsidp} for sub #{sidp} does not exist\n"
	  next
	else
	  refsidp = temp
	end
      end
      refIndex = getRefIndex(db,refsidp)  # Throws on count != 1

      # As of 2020-12, the only western Aleutians in currents.json are Rat
      # Islands, and they correctly show lng > 0.
      lat = csta["lat"]
      lng = csta["lng"]

      mindir = offsets["meanEbbDir"]    # degrees true
      maxdir = offsets["meanFloodDir"]  # degrees true
      mint   = offsets["mecTimeAdjMin"] # minutes
      maxt   = offsets["mfcTimeAdjMin"] # minutes
      sbft   = offsets["sbfTimeAdjMin"] # minutes
      sbet   = offsets["sbeTimeAdjMin"] # minutes
      minlm  = offsets["mecAmpAdj"]     # multiplier
      maxlm  = offsets["mfcAmpAdj"]     # multiplier
      raise "Nil offsets" if mint.nil? or maxt.nil? or sbft.nil? or
	sbet.nil? or minlm.nil? or maxlm.nil?

      fixednulls = false
      minlm, fixednulls = [0.001, true] if minlm <= 0
      maxlm, fixednulls = [0.001, true] if maxlm <= 0
      if fixednulls
	print "Fixed null multipliers at ", sidp, " ", cname, "\n"
	notes = append(notes, "Velocities for this station should be disregarded--use times only.")
	legal = "IGNORE VELOCITIES"
      end

      # There should be no existing reference station for this BIN.
      if refStationExists(db, sidp)
	raise "Current sub #{sidp} collides with existing reference station in same bin"
      end
      # For name/state/timezone, we have three options.
      # 1 - harmonize with existing reference station *
      # 2 - keep from data_sets_old
      # 3 - guess
      # * In TideSubs this means a (sub) suffix.  Here it means that we have
      # one depth that is ref and the others are subs of that.
      newStation = false
      reftuple = getRefCurrentNameStateTz(db, sid)
      if !reftuple.nil?  # 1 - harmonize with existing reference station
	name, state, tz = reftuple  # name should already be fixed.
        name = chompname(name)      # and this should be redundant.
      else
	oldtuple = getOldCurrentNameStateTz(db, sid)
	if !oldtuple.nil?  # 2 - keep from data_sets_old
	  oldname, state, tz = oldtuple
	  oldname = chompname(oldname)
	  name = fixname(oldname)
	  if name != oldname
	    print "#{sid} fixing up name from data_sets_old\n"
	    print "  #{oldname}\n"
	    print "  #{name}\n"
	  end
        else  # 3 - guess
	  newStation = true
	  begin
	    geogroup = orig_geogroup = guessCurrentsGeogroups(currentsGeogroups, sid)
	  rescue
	    print "** #{lat} #{lng} #{cname}\n"
	    raise
	  end
	  # geogroup = fixgeogroup(orig_geogroup)     Not applied to currents
	  statename = orig_geogroup[0]
	  raise "Geogroup contained a null state" if statename.nil?
	  raise "Need StateCode for #{statename}" unless StateCode.include?(statename)
	  gstate = StateCode[statename]

	  # currents.json has no state field and geogroups states are unreliable.
	  # Hunt for errors.
	  state, dist = guessStateDumb(stateMap, lat, lng)
	  raise "State guess is nil" if state.nil?
	  if state != gstate
	    print "** Possible wrong state, #{sid} #{lat} #{lng} #{cname}\n"
	    print "   Geogroup state #{gstate}\n"
	    print "   Guessed #{state} (d=#{"%.0e" % dist})\n"
	    raise "No StateName for #{state}" unless StateName.include?(state)
	    statename = StateName[state]
	    geogroup = [statename, geogroup[1], geogroup[2]]
	  end

	  # As of 2020-12, currents from MDAPI are all assigned to 21 states and
	  # Puerto Rico, notwithstanding a few WA-BC border cases.  guessTimezone
	  # needs the name parameter only for FM, MH, and Pacific atolls.
	  tz = guessTimezone(state, cname, lat, lng)
	end
      end

      # Time warps and other meridian problems
      submerid = TZmerids[tz]
      if submerid.nil?
	print "Missing from TZmerids: ", tz, "\n"
	raise "Missing tz"
      end
      # "timezone_offset" is *usually* an empty string for sub stations.
      # Hours E of GMT.
      # -4, -5, -6, -8, -9, or -10 (as a char string, for some reason)
      # tides.json instead has "timemeridian" and "timezonecorr".
      unless csta["timezone_offset"].empty?
	claimed_meridian = csta["timezone_offset"].to_i
	if submerid != claimed_meridian
	  print "** Inconsistent meridian: #{sid} #{cname}\n"
	  print "  timezone_offset in JSON = #{claimed_meridian}\n"
	  print "  Meridian of guessed time zone = #{submerid}\n"
	end
      end
      reftz = getRefTimezone(db, refsidp)
      if reftz != tz
	refmerid = TZmerids[reftz]
	if refmerid.nil?
	  print "Missing from TZmerids: ", reftz, "\n"
	  raise "Missing tz"
	end
	if refmerid != submerid
	  print "Time warp at ", sidp, " ", cname, "\n"
	  print "  Ref ", refsidp, " is ", reftz, " (merid ", refmerid, ")\n"
	  print "  Sub is ", tz, " (merid ", submerid, ")\n"
	  adjustMin = (refmerid - submerid) * 60
	  mint += adjustMin
	  maxt += adjustMin
	  sbft += adjustMin
	  sbet += adjustMin
	  notes = append(notes, "Reference is in time zone " + reftz +
			 "; adjusted offsets " + (refmerid-submerid).to_s +
			 " hr\nto undo LST compensation.")
	end
      end

      depth = csta["depth"]
      if depth.nil?
	comments = append(comments, "No depth was provided in the stations list.")
      else
	comments = append(comments, "Depth is as provided in the stations list.")
	case csta["depthType"]
	when "B"
	  comments = append(comments, "Depth is below chart datum.")
	when "S"
	  comments = append(comments, "Depth is below surface.")
	when "U"
	  comments = append(comments, "Unknown whether it is below surface or chart datum.")
	else
	  raise "Unknown depth type"
	end
      end

      if newStation
	name = generateName(cname, depth, geogroup, "Current")
	mname = db.escape_string(name)
	renamings.print "-- #{sidp} #{lat} #{lng}\n"
	renamings.print "-- orig name: #{cname}\n"
	gtext = (currentsGeogroups.include?(sid) ? "geogroup" : "geogroup (GUESSED)")
        if orig_geogroup != geogroup
          renamings.print "-- orig_#{gtext}: #{orig_geogroup}\n"
        end
	renamings.print "-- #{gtext}: #{geogroup}\n"
	if gstate != state
	  ts1 = (gstate.nil? ? "null" : gstate)
	  ts2 = (state.nil? ? "null" : state)
	  renamings.print "-- Possibly wrong state:  orig geogroup said #{ts1}, guessed #{ts2}\n"
	end
	renamings.print "update data_sets\n"
	renamings.print "  set state = ",
	  (state.nil? ? "null" : "'#{state}'"), ",\n"
	renamings.print "  name = '#{mname}'\n  where name = '#{mname}';\n"
      else
	depthpat = /\(depth [0-9\.]+ ft\)/
	if depth.nil?
	  unless depthpat.match(name).nil?
	    raise "Old name #{name} has depth but new station does not"
	  end
	else
	  if depthpat.match(name).nil?
	    raise "Old name #{name} has no depth but new station does"
	  else
	    # Rounding and formatting here must match that in generateName.
	    depth = depth.round.to_s
	    name.sub!(depthpat, "(depth #{depth} ft)")
	  end
	end
      end
      raise "Name in use" if nameInUse(db, name)

      units = "knots"    # Future support SI units
      country = "USA"    # Saved for future use
      xfields = "Credit:NOAA data processed by David Flater for XTide\n https://flaterco.com/xtide/"
      mint = mint.to_s + " minutes"
      maxt = maxt.to_s + " minutes"
      sbft = sbft.to_s + " minutes"
      sbet = sbet.to_s + " minutes"
      res = db.exec("insert into data_sets (name, original_name, station_id_context, station_id, lat, lng, timezone, state, country, units, source, legalese, notes, comments, restriction, xfields, ref_index, min_time_add, min_level_multiply, max_time_add, max_level_multiply, flood_begins, ebb_begins, min_dir, max_dir) values ($1, $2, 'NOS', $3, $4, $5, $6, $7, $8, $9, 'CO-OPS Metadata API', $10, $11, $12, 'Public domain', $13, $14, $15, $16, $17, $18, $19, $20, $21, $22)", [name, cname, sidp, lat, lng, tz, state, country, units, legal, notes, comments, xfields, refIndex, mint, minlm, maxt, maxlm, sbft, sbet, mindir, maxdir])
    }
  }
end
