///|
/// moongpx 可运行示例。
///
/// 跑法:moon run cmd/main
///
/// 演示三件事:
///   1. 坐标解析与范围校验(含那个不对称的经纬度边界)
///   2. 严格 xsd:decimal —— 指数记法必须被拒
///   3. 序列化时的指数陷阱 —— 输出必须永远是合法 GPX
///   4. 解析真实文档、诊断行列号
///   5. 序列化 + 往返一致性(模型 → 文本 → 模型,必须完全相等)

///|
fn show_lat(text : String) -> Unit {
  match @moongpx.Latitude::from_string(text) {
    Ok(lat) =>
      println(
        "  lat \"\{text}\" → 合法,回写为 \"\{lat.to_gpx_string()}\"",
      )
    Err(e) => println("  lat \"\{text}\" → 拒绝:\{e.message()}")
  }
}

///|
fn show_lon(text : String) -> Unit {
  match @moongpx.Longitude::from_string(text) {
    Ok(lon) =>
      println(
        "  lon \"\{text}\" → 合法,回写为 \"\{lon.to_gpx_string()}\"",
      )
    Err(e) => println("  lon \"\{text}\" → 拒绝:\{e.message()}")
  }
}

///|
fn main {
  println("=== 1. 纬度是闭区间 [-90, 90] ===")
  show_lat("31.2304")
  show_lat("90")
  show_lat("-90")
  show_lat("90.0001")

  println("")
  println("=== 2. 经度是半开区间 [-180, 180) —— 两端不对称 ===")
  show_lon("121.4737")
  show_lon("-180")
  show_lon("179.9999")
  show_lon("180")
  println(
    "  ↑ 注意:纬度 90 合法,但经度 180 非法。这是 GPX 最容易被做错的地方。",
  )

  println("")
  println("=== 3. xsd:decimal 不接受指数记法 ===")
  for text in ["1e2", "1e-7", "NaN", "INF", "31.2304"] {
    match @moongpx.parse_decimal(text) {
      Ok(v) => println("  \"\{text}\" → \{v}")
      Err(e) => println("  \"\{text}\" → 拒绝:\{e.message()}")
    }
  }

  println("")
  println(
    "=== 4. 序列化陷阱:Double 会输出指数,但 GPX 不允许 ===",
  )
  for v in [1.0e-7, 1.0e21, 2.5e-3, 31.2304] {
    println(
      "  \{v} → 原始 \{v.to_string()} → 合规 \{@moongpx.format_decimal(v)}",
    )
  }
  println(
    "  ↑ 若直接用 Double::to_string,lat=\"0.0000001\" 会被回写成 \"1e-7\",产出非法文档。",
  )

  println("")
  println("=== 5. 解析一个真实 GPX 文档 ===")
  let doc = "" +
    "晨跑" +
    "" +
    "早跑" +
    "4" +
    "6" +
    "9" +
    ""
  match @moongpx.parse_gpx(doc) {
    Ok(gpx) => {
      println("  解析成功")
      println("  version = \{gpx.version}   creator = \{gpx.creator}")
      match gpx.metadata {
        Some(m) =>
          match m.name {
            Some(n) => println("  metadata.name = \{n}")
            None => println("  metadata.name = 无")
          }
        None => println("  metadata = 无")
      }
      println("  轨迹数 = \{gpx.tracks.length()}")
      if gpx.tracks.length() > 0 {
        let segs = gpx.tracks[0].segments
        println(
          "  轨迹段数 = \{segs.length()}   点数 = \{segs[0].points.length()}",
        )
        let mut total = 0.0
        for i = 1; i < segs[0].points.length(); i = i + 1 {
          let a = segs[0].points[i - 1]
          let b = segs[0].points[i]
          total = total + distance_m(a, b)
        }
        println("  轨迹总长 = \{total} m")
      }
    }
    Err(diags) => println(@moongpx.format_diagnostics(diags))
  }

  println("")
  println("=== 6. 序列化:模型 → 合规 GPX 文本 ===")
  let doc2 = "" +
    "晨跑 & 夜跑" +
    "" +
    "0.0000001a<b" +
    "早跑" +
    "4" +
    "" +
    "私有数据" +
    ""
  match @moongpx.parse_gpx(doc2) {
    Ok(g) => {
      let out = @moongpx.to_gpx(g)
      println(out)
      println(
        "  ↑ 注意三处:①  输出 0.0000001 而不是 1e-7;" +
        "② 文本里的 & 被转义成 &;③ extensions 原样保留未被转义。",
      )
      println("")
      match @moongpx.parse_gpx(out) {
        Ok(g2) =>
          println(
            "  往返一致性 parse(to_gpx(g)) == g :\{if g == g2 { "成立" } else { "失败" }}",
          )
        Err(d) =>
          println("  往返后解析失败:\{@moongpx.format_diagnostics(d)}")
      }
    }
    Err(d) => println(@moongpx.format_diagnostics(d))
  }

  println("")
  println("=== 7. 一份有问题的文档 —— 诊断带行列号 ===")
  let bad = "\n" +
    "  \n" +
    "  \n" +
    "  \n" +
    ""
  match @moongpx.parse_gpx(bad) {
    Ok(_) => println("  不该成功")
    Err(diags) => {
      println("  共 \{diags.length()} 条诊断:")
      for d in diags {
        println("    \{d.to_string()}")
      }
    }
  }

  println("")
  println(
    "=== 8. 独立校验:warning 不阻塞解析,但会被记下来 ===",
  )
  let suspicious = "" +
    "99999" +
    "空的"
  match @moongpx.parse_gpx(suspicious) {
    Ok(_) => println("  parse_gpx   → Ok(文档合法,可以正常用)")
    Err(d) =>
      println("  parse_gpx   → Err: \{@moongpx.format_diagnostics(d)}")
  }
  println("  is_valid_gpx      = \{@moongpx.is_valid_gpx(suspicious)}")
  println("  has_schema_errors = \{@moongpx.has_schema_errors(suspicious)}")
  println(
    "  validate_gpx      = \{@moongpx.validate_gpx(suspicious).length()} 条:",
  )
  for d in @moongpx.validate_gpx(suspicious) {
    println("    \{d.to_string()}")
  }

  println("")
  println(
    "=== 9. 一次报出全部问题(error 与 warning 混合,不中断) ===",
  )
  let mixed = "" +
    "99999bogus" +
    "空的"
  println(@moongpx.validate_gpx_report(mixed))

  println("")
  println("=== 10. GPX 1.0 解析并升级为 1.1 ===")
  let g10 = "" +
    "周末徒步" +
    "张三zhangsan@example.com" +
    "http://example.com示例" +
    "" +
    "10" +
    "45.51.25" +
    "40" +
    ""
  match @moongpx.parse_gpx_1_0(g10) {
    Ok(g) => {
      println("  version = \{g.version}(1.0 文档已转成 1.1 模型)")
      match g.metadata {
        Some(m) => {
          match m.name {
            Some(n) => println("  根级  → metadata.name = \{n}")
            None => ()
          }
          match m.author {
            Some(p) =>
              match p.email {
                Some(e) =>
                  println("   整串 → id=\{e.id}  domain=\{e.domain}")
                None => ()
              }
            None => ()
          }
          if m.links.length() > 0 {
            println("   → link.href = \{m.links[0].href}")
          }
        }
        None => ()
      }
      let pt = g.tracks[0].segments[0].points[0]
      match pt.extensions {
        Some(x) => println("  / → extensions = \{x}")
        None => ()
      }
    }
    Err(d) => println(@moongpx.format_diagnostics(d))
  }

  println("")
  println("=== 11. 轨迹统计 ===")
  match @moongpx.parse_gpx_1_0(g10) {
    Ok(g) =>
      if g.tracks.length() > 0 {
        let s = @moongpx.track_stats(g.tracks[0])
        println("  点数 = \{s.point_count}")
        println("  距离 = \{@moongpx.format_decimal(s.distance_m)} m")
        println(
          "  爬升 = \{@moongpx.format_decimal(s.ascend_m)} m   下降 = \{@moongpx.format_decimal(s.descend_m)} m",
        )
        match (s.min_ele, s.max_ele) {
          (Some(lo), Some(hi)) =>
            println(
              "  高程 = \{@moongpx.format_decimal(lo)} ~ \{@moongpx.format_decimal(hi)} m",
            )
          _ => println("  高程 = 无")
        }
      }
    Err(_) => ()
  }

  println("")
  println("示例结束。")
}

///|
fn distance_m(a : @moongpx.Waypoint, b : @moongpx.Waypoint) -> Double {
  let r = 6371008.8
  let d2r = 0.017453292519943295
  let lat1 = a.lat.to_double()
  let lon1 = a.lon.to_double()
  let lat2 = b.lat.to_double()
  let lon2 = b.lon.to_double()
  let dlat = (lat2 - lat1) * d2r
  let dlon = (lon2 - lon1) * d2r
  let sdlat = @math.sin(dlat / 2.0)
  let sdlon = @math.sin(dlon / 2.0)
  let h = sdlat * sdlat +
    @math.cos(lat1 * d2r) * @math.cos(lat2 * d2r) * sdlon * sdlon
  2.0 * r * @math.asin(h.sqrt())
}