///|
pub suberror FakeError {
  Invalid(String)
} derive(Debug)

///|
pub struct Generator {
  mut state : UInt
} derive(Debug)

///|
pub(all) struct Profile {
  name : String
  email : String
  address : String
  birth_date : String
  id : String
} derive(Debug, Eq)

///|
pub fn Generator::new(seed : UInt) -> Generator {
  { state: if seed == 0U { 0x9e3779b9U } else { seed }, }
}

///|
fn Generator::next(self : Generator) -> UInt {
  let mut x = self.state
  x = x ^ (x << 13)
  x = x ^ (x >> 17)
  x = x ^ (x << 5)
  self.state = x
  x
}

///|
pub fn Generator::integer(
  self : Generator,
  min : Int,
  max : Int,
) -> Int raise FakeError {
  if min > max || min < -1000000000 || max > 1000000000 {
    raise Invalid("integer bounds")
  }
  let span = (max - min + 1).reinterpret_as_uint()
  let threshold = (0U - span) % span
  let mut x = self.next()
  while x < threshold {
    x = self.next()
  }
  min + (x % span).reinterpret_as_int()
}

///|
fn Generator::pick(self : Generator, values : Array[String]) -> String {
  values[(self.next() % values.length().reinterpret_as_uint()).reinterpret_as_int()]
}

///|
pub fn Generator::name(self : Generator) -> String {
  self.pick([
    "赵", "钱", "孙", "李", "周", "吴", "郑", "王", "陈", "林", "黄",
    "张", "刘", "杨", "许", "叶",
  ]) +
  self.pick([
    "明", "雨桐", "子安", "清和", "若云", "小川", "嘉宁", "星辰",
    "思远", "晓月", "文静", "知秋",
  ])
}

///|
fn pad(n : Int) -> String {
  if n < 10 {
    "0" + n.to_string()
  } else {
    n.to_string()
  }
}

///|
pub fn Generator::date(
  self : Generator,
  first_year : Int,
  last_year : Int,
) -> String raise FakeError {
  if first_year < 1 || last_year > 9999 {
    raise Invalid("year bounds")
  }
  let y = self.integer(first_year, last_year)
  let m = self.integer(1, 12)
  let leap = y % 400 == 0 || (y % 4 == 0 && y % 100 != 0)
  let days = if m == 2 {
    if leap {
      29
    } else {
      28
    }
  } else if m == 4 || m == 6 || m == 9 || m == 11 {
    30
  } else {
    31
  }
  (if y < 10 {
    "000"
  } else if y < 100 {
    "00"
  } else if y < 1000 {
    "0"
  } else {
    ""
  }) +
  y.to_string() +
  "-" +
  pad(m) +
  "-" +
  pad(self.integer(1, days))
}

///|
pub fn Generator::profile(
  self : Generator,
  index : Int,
) -> Profile raise FakeError {
  if index < 0 {
    raise Invalid("negative index")
  }
  let name = self.name()
  let place = self.pick([
    "测试省示例市东城区", "测试省示例市西城区", "测试省样本市新区",
  ])
  let road = self.pick(["星河路", "青山路", "明月街", "样本大道"])
  let address = place + road + self.integer(1, 999).to_string() + "号"
  let id = "sample-" + index.to_string() + "-" + self.next().to_string()
  {
    name,
    email: "sample" + index.to_string() + "@example.test",
    address,
    birth_date: self.date(1970, 2005),
    id,
  }
}

///|
pub fn dataset(seed : UInt, count : Int) -> Array[Profile] raise FakeError {
  if count < 0 || count > 10000 {
    raise Invalid("count must be 0..10000")
  }
  let g = Generator::new(seed)
  Array::makei(count, i => g.profile(i))
}