///|
/// Metadata stored alongside a generated document.
pub(all) struct DocumentMetadata {
  title : String
  author : String
  seed : UInt
  purpose : String
  tags : Array[String]
} derive(Eq, Debug, ToJson)

///|
pub fn DocumentMetadata::new(
  title : String,
  author : String,
  seed : UInt,
  purpose : String,
) -> DocumentMetadata {
  { title, author, seed, purpose, tags: [] }
}

///|
pub fn DocumentMetadata::tag(
  self : DocumentMetadata,
  value : String,
) -> DocumentMetadata {
  self.tags.push(value)
  self
}

///|
pub fn DocumentMetadata::to_comment(self : DocumentMetadata) -> String {
  "\n"
}

///|
pub(all) struct DesignDocument {
  composition : Composition
  metadata : DocumentMetadata
} derive(Debug)

///|
pub fn DesignDocument::new(
  composition : Composition,
  metadata : DocumentMetadata,
) -> DesignDocument {
  { composition, metadata }
}

///|
pub fn DesignDocument::to_svg(self : DesignDocument) -> String {
  self.metadata.to_comment() + self.composition.render().to_svg_string()
}

///|
pub fn DesignDocument::metrics(self : DesignDocument) -> LayoutMetrics {
  let paths = []
  for layer in self.composition.layers {
    for motif in layer.motifs {
      paths.push(motif.transformed_path())
    }
  }
  measure_paths(
    paths,
    Rect::new(0.0, 0.0, self.composition.width, self.composition.height),
  )
}

///|
pub fn DesignDocument::validate(self : DesignDocument) -> Array[String] {
  let errors = []
  if self.composition.width <= 0.0 {
    errors.push("document width must be positive")
  }
  if self.composition.height <= 0.0 {
    errors.push("document height must be positive")
  }
  if self.composition.layers.length() == 0 {
    errors.push("document has no visible layers")
  }
  for layer in self.composition.layers {
    if layer.name.length() == 0 {
      errors.push("layer name cannot be empty")
    }
  }
  errors
}

///|
pub fn make_design_document(
  seed : UInt,
  width : Double,
  height : Double,
  palette : Palette,
) -> DesignDocument {
  let recipe = SceneRecipe::new(seed, width, height, 12, 12, palette)
  let metadata = DocumentMetadata::new(
      "Procedural Pattern", "MoonBit Generative Patterns", seed, "poster and packaging artwork",
    )
    .tag("procedural")
    .tag("svg")
    .tag("moonbit")
  DesignDocument::new(recipe.build(), metadata)
}

///|
pub fn export_variants(
  seed : UInt,
  sizes : Array[(Double, Double)],
  palette : Palette,
) -> Array[DesignDocument] {
  let result = []
  for size in sizes {
    result.push(make_design_document(seed, size.0, size.1, palette))
  }
  result
}