NOTE not shown here are the 50+ test cases that are run against this script.

  def parse_length(params)
    raise UnresolvedCase unless params['length_unit'].empty?
    return "{{convert|#{params['length']}|km|mi|abbr=on}}#{", #{params['length_orientation']}" unless params['length_orientation'].empty?}#{params['length_note']}" unless params['length'].empty?
    return "{{convert|#{params['length_imperial']}|mi|km|abbr=on}}#{", #{params['length_orientation']}" unless params['length_orientation'].empty?}#{params['length_note']}" unless params['length_imperial'].empty?
  end
  
  def parse_watershed(params)
    raise UnresolvedCase unless params['watershed_unit'].empty?
    return "{{convert|#{params['watershed']}|km2|abbr=on}}#{params['watershed_note']}" unless params['watershed'].empty?
    return "{{convert|#{params['watershed_imperial']}|sqmi|abbr=on}}#{params['watershed_note']}" unless params['watershed_imperial'].empty?
  end
  
  def parse_mouth_elevation(params)
    raise UnresolvedCase unless params['mouth_elevation_unit'].empty?
    return "{{convert|#{params['mouth_elevation']}|m|abbr=on}}#{params['mouth_elevation_note']}" unless params['mouth_elevation'].empty?
    return "{{convert|#{params['mouth_elevation_imperial']}|ft|abbr=on}}#{params['mouth_elevation_note']}" unless params['mouth_elevation_imperial'].empty?
  end
  
  def parse_tributaries_l(params)
    values = [
        autolink(params['tributary_left']),
        autolink(params['tributary_left1']),
        autolink(params['tributary_left2']),
        autolink(params['tributary_left3']),
        autolink(params['tributary_left4']),
        autolink(params['tributary_left5']),
        autolink(params['tributary_left6']),
        autolink(params['tributary_left7']),
        autolink(params['tributary_left8']),
        autolink(params['tributary_left9']),
        autolink(params['tributary_left10']),
        autolink(params['tributary_left11']),
        autolink(params['tributary_left12']),
        autolink(params['tributary_left13']),
        autolink(params['tributary_left14']),
        autolink(params['tributary_left15'])
    ].reject{|val| val.empty?}
    return "#{values.join(', ')}"
  end
  
  def parse_tributaries_r(params)
    values = [
        autolink(params['tributary_right']),
        autolink(params['tributary_right1']),
        autolink(params['tributary_right2']),
        autolink(params['tributary_right3']),
        autolink(params['tributary_right4']),
        autolink(params['tributary_right5']),
        autolink(params['tributary_right6']),
        autolink(params['tributary_right7']),
        autolink(params['tributary_right8']),
        autolink(params['tributary_right9']),
        autolink(params['tributary_right10']),
        autolink(params['tributary_right11']),
        autolink(params['tributary_right12']),
        autolink(params['tributary_right13']),
        autolink(params['tributary_right14']),
        autolink(params['tributary_right15'])
    ].reject{|val| val.empty?}
    return "#{values.join(', ')}"
  end

  def parse_source_elevation(params, value)
    raise UnresolvedCase unless params["#{value}_elevation_unit"].empty?
    return "{{convert|#{params["#{value}_elevation"]}|m|abbr=on}}#{params["#{value}_elevation_note"]}" unless params["#{value}_elevation"].empty?
    return "{{convert|#{params["#{value}_elevation_imperial"]}|ft|abbr=on}}#{params["#{value}_elevation_note"]}" unless params["#{value}_elevation_imperial"].empty?
  end
  
  def parse_river_location(params, value)
    [
        autolink(params["#{value}_location"]), 
        autolink(params["#{value}_municipality"]), 
        autolink(params["#{value}_district"]), 
        autolink(params["#{value}_region"]), 
        autolink(params["#{value}_state"]), 
        autolink(params["#{value}_country"]), 
    ].reject{|val| val.empty?}.join(', ')
  end
  
  def parse_sources(params)
    result = "
| source1            = #{autolink params['source']}#{autolink params['source_name']}
| source1_location   = #{parse_river_location(params, 'source')}
| source1_coordinates= #{params['source_coordinates']}#{params['source_coordinates_note']}
| source1_elevation  = #{parse_source_elevation(params, 'source')}"
    unless params['source1'].empty?
      result += "
| source2            = #{autolink params['source1']}#{autolink params['source1_name']}
| source2_location   = #{parse_river_location(params, 'source1')}
| source2_coordinates= #{params['source1_coordinates']}#{params['source1_coordinates_note']}
| source2_elevation  = #{parse_source_elevation(params, 'source1')}"
    end
    unless params['source2'].empty?
      result += "
| source3            = #{autolink params['source2']}#{autolink params['source2_name']}
| source3_location   = #{parse_river_location(params, 'source2')}
| source3_coordinates= #{params['source2_coordinates']}#{params['source2_coordinates_note']}
| source3_elevation  = #{parse_source_elevation(params, 'source2')}"
    end
    unless params['source3'].empty?
      result += "
| source4            = #{params['source3']}#{params['source3_name']}
| source4_location   = #{parse_river_location(params, 'source3')}
| source4_coordinates= #{params['source3_coordinates']}#{params['source3_coordinates_note']}
| source4_elevation  = #{parse_source_elevation(params, 'source3')}"
    end
    unless params['source3'].empty?
      result += "
| source5            = #{params['source4']}#{params['source4_name']}
| source5_location   = #{parse_river_location(params, 'source4')}
| source5_coordinates= #{params['source4_coordinates']}#{params['source4_coordinates_note']}
| source5_elevation  = #{parse_source_elevation(params, 'source4')}"
    end
    result
  end

  def parse_discharge_rate2(params, pre)
    raise UnresolvedCase unless params["#{pre}_unit"].empty?
    #NOTE: If either of the next two lines are ever raised, perhaps they should just return nil string instead of raising error
    return '' unless (params["#{pre}_average"].empty? && params["#{pre}_average_imperial"].empty?)
    return "{{convert|#{params["#{pre}"]}|m3/s|cuft/s|abbr=on}}#{params["#{pre}_note"]}" unless params["#{pre}"].empty?
    return "{{convert|#{params["#{pre}_imperial"]}|cuft/s|m3/s|abbr=on}}#{params["#{pre}_note"]}" unless params["#{pre}_imperial"].empty?
  end


  def parse_discharge_rate(params, pre, val)
    raise UnresolvedCase unless params["#{pre}_unit"].empty?
    raise UnresolvedCase unless params["#{pre}_#{val}_unit"].empty?
    return "{{convert|#{params["#{pre}_#{val}"]}|m3/s|cuft/s|abbr=on}}#{params["#{pre}_#{val}_note"]}" unless params["#{pre}_#{val}"].empty?
    return "{{convert|#{params["#{pre}_#{val}_imperial"]}|cuft/s|m3/s|abbr=on}}#{params["#{pre}_#{val}_note"]}" unless params["#{pre}_#{val}_imperial"].empty?
  end

  def parse_discharge(params)
    result = "
| discharge1_location= #{autolink(params['discharge_location'])}#{params['discharge_note'] unless params['discharge_location'].empty? }
| discharge1_min     = #{parse_discharge_rate(params, 'discharge', 'min')}
| discharge1_avg     = #{parse_discharge_rate(params, 'discharge', 'average')}#{parse_discharge_rate2(params, 'discharge')}
| discharge1_max     = #{parse_discharge_rate(params, 'discharge', 'max')}"
    unless params['discharge1_location'].empty?
      result += "
| discharge2_location= #{autolink(params['discharge1_location'])}#{params['discharge1_note'] unless params['discharge1_location'].empty?}
| discharge2_min     = #{parse_discharge_rate(params, 'discharge1', 'min')}
| discharge2_avg     = #{parse_discharge_rate(params, 'discharge1', 'average')}#{parse_discharge_rate2(params, 'discharge1')}
| discharge2_max     = #{parse_discharge_rate(params, 'discharge1', 'max')}"
    end
    unless params['discharge2_location'].empty?
      result += "
| discharge3_location= #{autolink(params['discharge2_location'])}#{params['discharge2_note'] unless params['discharge2_location'].empty?}
| discharge3_min     = #{parse_discharge_rate(params, 'discharge2', 'min')} 
| discharge3_avg     = #{parse_discharge_rate(params, 'discharge2', 'average')}#{parse_discharge_rate2(params, 'discharge2')}
| discharge3_max     = #{parse_discharge_rate(params, 'discharge2', 'max')}"
    end
    unless params['discharge3_location'].empty?
      result += "
| discharge4_location= #{autolink(params['discharge3_location'])}#{params['discharge3_note'] unless params['discharge3_location'].empty?}
| discharge4_min     = #{parse_discharge_rate(params, 'discharge3', 'min')} 
| discharge4_avg     = #{parse_discharge_rate(params, 'discharge3', 'average')}#{parse_discharge_rate2(params, 'discharge3')}
| discharge4_max     = #{parse_discharge_rate(params, 'discharge3', 'max')}"
    end
    unless params['discharge4_location'].empty?
      result += "
| discharge5_location= #{autolink(params['discharge4_location'])}#{params['discharge4_note'] unless params['discharge4_location'].empty?}
| discharge5_min     = #{parse_discharge_rate(params, 'discharge4', 'min')} 
| discharge5_avg     = #{parse_discharge_rate(params, 'discharge4', 'average')}#{parse_discharge_rate2(params, 'discharge4')} 
| discharge5_max     = #{parse_discharge_rate(params, 'discharge4', 'max')}"
    end
    result
  end
  
  def parse_multi_location(params, val)
    # val = 'country', 'district', 'town', 'city', etc...
    result = ["#{autolink(params[val])}#{params["#{val}_note"] unless params[val].empty? }"]
    (1..15).each do |i|
      param = val+i.to_s
      result << "#{autolink(params[param])}#{params["#{param}_note"]}" unless params[param].empty?
    end
    result.join(', ')
  end
  
  def parse_city_type(params)
    raise UnresolvedCase unless (params["municipality"].empty? || params["city"].empty?)
    return '' if (params["municipality"].empty? && params["city"].empty?)
    if params["municipality"].empty?
      return params['city_type'] unless params['city_type'].empty?
      return params["city1"].empty? ? 'City' : 'Cities'
    else
      return params['municipality_type'] unless params['municipality_type'].empty?
      return params["municipality1"].empty? ? 'Municipality' : 'Municipalities'
    end
  end
  
  def parse_city(params)
    raise UnresolvedCase unless (params["municipality"].empty? || params["city"].empty?)
    return '' if (params["municipality"].empty? && params["city"].empty?)
    result = []
    if params["municipality"].empty?
      result = ["#{autolink(params['city'])}#{params["city_note"] unless params['city'].empty? }"]
      (1..15).each do |i|
        param = 'city'+i.to_s
        result << "#{autolink(params[param])}#{params["#{param}_note"]}" unless params[param].empty?
      end
    else
      result = ["#{autolink(params['municipality'])}#{params["municipality_note"] unless params['municipality'].empty? }"]
      (1..15).each do |i|
        param = 'municipality'+i.to_s
        result << "#{autolink(params[param])}#{params["#{param}_note"]}" unless params[param].empty?
      end
    end
    result.join(', ')
  end
  
  def parse_other_name(params)
    result = ["#{params['other_name']}#{params["other_name_note"] unless params['other_name'].empty? }"]
    (1..7).each do |i|
      param = 'other_name'+i.to_s
      result << "#{params[param]}#{params["#{param}_note"]}" unless params[param].empty?
    end
    result.reject{|val| val.empty?}.join(', ')
  end
  
  def autolink(value)
    return '' if value.nil? || value.empty?  
    return value if (value.include?('[[') || value.include?('{{'))
    "{{subst:#ifexist:#{value}|[[#{value}]]|#{value}}}"
  end

def parse_geobox_to_river(page)
    page.force_encoding('UTF-8')
    templates = page.scan(/(?=\{\{(?:geobox|geo box}))(\{\{(?>[^{}]++|\g<1>)*}})/i).flatten
    raise NoTemplatesFound if templates.empty?
    raise UnresolvedCase if templates.size > 1

    template = templates.first
    old_template = template.dup
    
    raise UnresolvedCase.new('Contains invalid ref') if template.match?(/<ref\s*name=".*\|.*\"/i)
    
    template.gsub!(/<!--[\w\W]*?-->/,'')
    template.gsub!('{{USA}}', '[[United States]]')
    template.gsub!(/\{\{flag\|(.*)\}\}/,'[[\1]]')
    params = Helper.parse_template(template)

    raise UnresolvedCase.new('contains unsupported param') unless params["depth"].empty?
    raise UnresolvedCase.new('contains unsupported param') unless params["depth_imperial"].empty?
    raise UnresolvedCase.new('contains unsupported param') unless params["depth1"].empty?
    raise UnresolvedCase.new('contains unsupported param') unless params["depth1_imperial"].empty?
    raise UnresolvedCase.new('contains unsupported param') unless params["free"].empty?
    raise UnresolvedCase.new('contains unsupported param') unless params["free2"].empty?
    raise UnresolvedCase.new('contains unsupported param') unless params["source_confluence_coordinates"].empty?
    raise UnresolvedCase.new('contains unsupported param') unless params["source_confluence"].empty?
    raise UnresolvedCase.new('contains unsupported param') unless params["source_confluence_location"].empty?
    
    result = "{{Infobox river
| name               = #{params["name"]}#{"{{subst:PAGENAME}}" if params['name'].empty?}
| name_native        = #{params['native_name']}
| name_native_lang   = 
| name_other         = #{parse_other_name(params)}
| name_etymology     = #{params['etymology']}#{params['etymology_note']}
<!---------------------- IMAGE & MAP -->
| image              = #{params["image"]}
| image_size         = #{params['image_size']}
| image_caption      = #{params["image_caption"]}
| map                = #{params['map']}
| map_size           = #{params['map_size']}#{params['pushpin_map_size'] if params['map_size'].empty?}
| map_caption        = #{params['map_caption']}
| pushpin_map        = #{get_map(params)}
| pushpin_map_size   = #{params['map_size']}#{params['pushpin_map_size'] if params['map_size'].empty?}
| pushpin_map_caption= #{params['pushpin_map_caption']}
<!---------------------- LOCATION -->
| subdivision_type1  = Country
| subdivision_name1  = #{parse_multi_location(params, 'country')}
| subdivision_type2  = #{('State' unless(params["state"].empty? || !params['state_type'].empty?))||params["state_type"]}
| subdivision_name2  = #{parse_multi_location(params, 'state')}
| subdivision_type3  = #{('Region' unless (params["region"].empty? || !params['region_type'].empty?))||params["region_type"]}
| subdivision_name3  = #{parse_multi_location(params, 'region')}
| subdivision_type4  = #{('District' unless (params["district"].empty? || !params['district_type'].empty?))||params["district_type"]}
| subdivision_name4  = #{parse_multi_location(params, 'district')}
| subdivision_type5  = #{parse_city_type(params)}
| subdivision_name5  = #{parse_city(params)}
<!---------------------- PHYSICAL CHARACTERISTICS -->
| length             = #{parse_length(params)} 
| width_min          = 
| width_avg          = 
| width_max          = 
| depth_min          = 
| depth_avg          = 
| depth_max          = #{parse_discharge(params)}
<!---------------------- BASIN FEATURES -->#{parse_sources(params)}
| mouth              = #{autolink(params['mouth_name'])}#{autolink(params['mouth'])}#{params['mouth_note'] unless (params['mouth'].empty? && params['mouth_name'].empty?)}
| mouth_location     = #{parse_river_location(params, 'mouth')}
| mouth_coordinates  = #{params['mouth_coordinates']}#{params['mouth_coordinates_country']}#{params['mouth_coordinates_note']}
| mouth_elevation    = #{parse_mouth_elevation(params)}
| progression        = 
| river_system       = #{autolink(params['parent'])}
| basin_size         = #{parse_watershed(params)}
| tributaries_left   = #{parse_tributaries_l(params)}
| tributaries_right  = #{parse_tributaries_r(params)}
| custom_label       = 
| custom_data        = 
| extra              = #{params['footnotes']}
}}"  
    page.sub!(old_template, result)
    page
  end