// Copyright 2026 International Digital Economy Academy
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Builtin types

///|
pub impl ToJson for Bool with to_json(self) {
  if self {
    Json::bool(true)
  } else {
    Json::bool(false)
  }
}

///|
pub impl ToJson for Int with to_json(self) {
  Json::int(self)
}

///|
pub impl ToJson for UInt with to_json(self) {
  Json::uint(self)
}

///|
pub impl ToJson for Int64 with to_json(self) {
  Json::int64(self)
}

///|
pub impl ToJson for UInt64 with to_json(self) {
  Json::uint64(self)
}

///|
pub impl ToJson for Double with to_json(self) {
  if self != self {
    // NaN
    Json::string("NaN")
  } else if self == @double.infinity {
    Json::string("Infinity")
  } else if self == @double.neg_infinity {
    Json::string("-Infinity")
  } else {
    Json::double(self)
  }
}

///|
pub impl ToJson for Float with to_json(self) {
  Json::float(self)
}

///|
pub impl ToJson for String with to_json(self) {
  Json::string(self)
}

///|
pub impl ToJson for Char with to_json(self) {
  Json::string(self.to_string())
}

///|
pub impl ToJson for @bigint.BigInt with to_json(self) {
  Json::bigint(self)
}

///|
pub impl[A : ToJson] ToJson for Array[A] with to_json(self) {
  let res = Array::new(capacity=self.length())
  for item in self {
    res.push(item.to_json())
  }
  Json::array(res)
}

///|
pub impl[A : ToJson] ToJson for ArrayView[A] with to_json(self) {
  let res = Array::new(capacity=self.length())
  for item in self {
    res.push(item.to_json())
  }
  Json::array(res)
}

///|
pub impl[A : ToJson] ToJson for MutArrayView[A] with to_json(self) {
  let res = Array::new(capacity=self.length())
  for item in self {
    res.push(item.to_json())
  }
  Json::array(res)
}

///|
pub impl[A : ToJson] ToJson for FixedArray[A] with to_json(self) {
  let res = Array::new(capacity=self.length())
  for item in self {
    res.push(item.to_json())
  }
  Json::array(res)
}

///|
pub impl[V : ToJson] ToJson for Map[String, V] with to_json(self) {
  let object = Map::new(capacity=self.capacity())
  for k, v in self {
    object[k] = v.to_json()
  }
  Json::object(object)
}

// Tuple types

///|
pub impl[A : ToJson, B : ToJson] ToJson for (A, B) with to_json(self) {
  let (a, b) = self
  Json::array([a.to_json(), b.to_json()])
}

///|
pub impl[A : ToJson, B : ToJson, C : ToJson] ToJson for (A, B, C) with to_json(
  self,
) {
  let (a, b, c) = self
  Json::array([a.to_json(), b.to_json(), c.to_json()])
}

///|
pub impl[A : ToJson, B : ToJson, C : ToJson, D : ToJson] ToJson for (A, B, C, D) with to_json(
  self,
) {
  let (a, b, c, d) = self
  Json::array([a.to_json(), b.to_json(), c.to_json(), d.to_json()])
}

///|
pub impl[A : ToJson, B : ToJson, C : ToJson, D : ToJson, E : ToJson] ToJson for (
  A,
  B,
  C,
  D,
  E,
) with to_json(self) {
  let (a, b, c, d, e) = self
  Json::array([a.to_json(), b.to_json(), c.to_json(), d.to_json(), e.to_json()])
}

///|
pub impl[A : ToJson, B : ToJson, C : ToJson, D : ToJson, E : ToJson, F : ToJson] ToJson for (
  A,
  B,
  C,
  D,
  E,
  F,
) with to_json(self) {
  let (a, b, c, d, e, f) = self
  Json::array([
    a.to_json(),
    b.to_json(),
    c.to_json(),
    d.to_json(),
    e.to_json(),
    f.to_json(),
  ])
}

///|
pub impl[
  A : ToJson,
  B : ToJson,
  C : ToJson,
  D : ToJson,
  E : ToJson,
  F : ToJson,
  G : ToJson,
] ToJson for (A, B, C, D, E, F, G) with to_json(self) {
  let (a, b, c, d, e, f, g) = self
  Json::array([
    a.to_json(),
    b.to_json(),
    c.to_json(),
    d.to_json(),
    e.to_json(),
    f.to_json(),
    g.to_json(),
  ])
}

///|
pub impl[
  A : ToJson,
  B : ToJson,
  C : ToJson,
  D : ToJson,
  E : ToJson,
  F : ToJson,
  G : ToJson,
  H : ToJson,
] ToJson for (A, B, C, D, E, F, G, H) with to_json(self) {
  let (a, b, c, d, e, f, g, h) = self
  Json::array([
    a.to_json(),
    b.to_json(),
    c.to_json(),
    d.to_json(),
    e.to_json(),
    f.to_json(),
    g.to_json(),
    h.to_json(),
  ])
}

///|
pub impl[
  A : ToJson,
  B : ToJson,
  C : ToJson,
  D : ToJson,
  E : ToJson,
  F : ToJson,
  G : ToJson,
  H : ToJson,
  I : ToJson,
] ToJson for (A, B, C, D, E, F, G, H, I) with to_json(self) {
  let (a, b, c, d, e, f, g, h, i) = self
  Json::array([
    a.to_json(),
    b.to_json(),
    c.to_json(),
    d.to_json(),
    e.to_json(),
    f.to_json(),
    g.to_json(),
    h.to_json(),
    i.to_json(),
  ])
}

///|
pub impl[
  A : ToJson,
  B : ToJson,
  C : ToJson,
  D : ToJson,
  E : ToJson,
  F : ToJson,
  G : ToJson,
  H : ToJson,
  I : ToJson,
  J : ToJson,
] ToJson for (A, B, C, D, E, F, G, H, I, J) with to_json(self) {
  let (a, b, c, d, e, f, g, h, i, j) = self
  Json::array([
    a.to_json(),
    b.to_json(),
    c.to_json(),
    d.to_json(),
    e.to_json(),
    f.to_json(),
    g.to_json(),
    h.to_json(),
    i.to_json(),
    j.to_json(),
  ])
}

///|
pub impl[
  A : ToJson,
  B : ToJson,
  C : ToJson,
  D : ToJson,
  E : ToJson,
  F : ToJson,
  G : ToJson,
  H : ToJson,
  I : ToJson,
  J : ToJson,
  K : ToJson,
] ToJson for (A, B, C, D, E, F, G, H, I, J, K) with to_json(self) {
  let (a, b, c, d, e, f, g, h, i, j, k) = self
  Json::array([
    a.to_json(),
    b.to_json(),
    c.to_json(),
    d.to_json(),
    e.to_json(),
    f.to_json(),
    g.to_json(),
    h.to_json(),
    i.to_json(),
    j.to_json(),
    k.to_json(),
  ])
}

///|
pub impl[
  A : ToJson,
  B : ToJson,
  C : ToJson,
  D : ToJson,
  E : ToJson,
  F : ToJson,
  G : ToJson,
  H : ToJson,
  I : ToJson,
  J : ToJson,
  K : ToJson,
  L : ToJson,
] ToJson for (A, B, C, D, E, F, G, H, I, J, K, L) with to_json(self) {
  let (a, b, c, d, e, f, g, h, i, j, k, l) = self
  Json::array([
    a.to_json(),
    b.to_json(),
    c.to_json(),
    d.to_json(),
    e.to_json(),
    f.to_json(),
    g.to_json(),
    h.to_json(),
    i.to_json(),
    j.to_json(),
    k.to_json(),
    l.to_json(),
  ])
}

///|
pub impl[
  A : ToJson,
  B : ToJson,
  C : ToJson,
  D : ToJson,
  E : ToJson,
  F : ToJson,
  G : ToJson,
  H : ToJson,
  I : ToJson,
  J : ToJson,
  K : ToJson,
  L : ToJson,
  M : ToJson,
] ToJson for (A, B, C, D, E, F, G, H, I, J, K, L, M) with to_json(self) {
  let (a, b, c, d, e, f, g, h, i, j, k, l, m) = self
  Json::array([
    a.to_json(),
    b.to_json(),
    c.to_json(),
    d.to_json(),
    e.to_json(),
    f.to_json(),
    g.to_json(),
    h.to_json(),
    i.to_json(),
    j.to_json(),
    k.to_json(),
    l.to_json(),
    m.to_json(),
  ])
}

///|
pub impl[
  A : ToJson,
  B : ToJson,
  C : ToJson,
  D : ToJson,
  E : ToJson,
  F : ToJson,
  G : ToJson,
  H : ToJson,
  I : ToJson,
  J : ToJson,
  K : ToJson,
  L : ToJson,
  M : ToJson,
  N : ToJson,
] ToJson for (A, B, C, D, E, F, G, H, I, J, K, L, M, N) with to_json(self) {
  let (a, b, c, d, e, f, g, h, i, j, k, l, m, n) = self
  Json::array([
    a.to_json(),
    b.to_json(),
    c.to_json(),
    d.to_json(),
    e.to_json(),
    f.to_json(),
    g.to_json(),
    h.to_json(),
    i.to_json(),
    j.to_json(),
    k.to_json(),
    l.to_json(),
    m.to_json(),
    n.to_json(),
  ])
}

///|
pub impl[
  A : ToJson,
  B : ToJson,
  C : ToJson,
  D : ToJson,
  E : ToJson,
  F : ToJson,
  G : ToJson,
  H : ToJson,
  I : ToJson,
  J : ToJson,
  K : ToJson,
  L : ToJson,
  M : ToJson,
  N : ToJson,
  O : ToJson,
] ToJson for (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O) with to_json(self) {
  let (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o) = self
  Json::array([
    a.to_json(),
    b.to_json(),
    c.to_json(),
    d.to_json(),
    e.to_json(),
    f.to_json(),
    g.to_json(),
    h.to_json(),
    i.to_json(),
    j.to_json(),
    k.to_json(),
    l.to_json(),
    m.to_json(),
    n.to_json(),
    o.to_json(),
  ])
}

///|
pub impl[
  A : ToJson,
  B : ToJson,
  C : ToJson,
  D : ToJson,
  E : ToJson,
  F : ToJson,
  G : ToJson,
  H : ToJson,
  I : ToJson,
  J : ToJson,
  K : ToJson,
  L : ToJson,
  M : ToJson,
  N : ToJson,
  O : ToJson,
  P : ToJson,
] ToJson for (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P) with to_json(self) {
  let (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) = self
  Json::array([
    a.to_json(),
    b.to_json(),
    c.to_json(),
    d.to_json(),
    e.to_json(),
    f.to_json(),
    g.to_json(),
    h.to_json(),
    i.to_json(),
    j.to_json(),
    k.to_json(),
    l.to_json(),
    m.to_json(),
    n.to_json(),
    o.to_json(),
    p.to_json(),
  ])
}