///|
pub struct ThemeProvenance {
  path : String
  layer : Int
  source : String
} derive(Debug, Eq)

///|
pub fn ThemeProvenance::new(
  path : String,
  layer : Int,
  source : String,
) -> ThemeProvenance {
  { path, layer, source }
}

///|
pub fn ThemeDocument::merge_layers(
  layers : Array[ThemeDocument],
) -> ThemeDocument {
  let tokens : Array[ThemeToken] = []
  let provenance : Array[ThemeProvenance] = []
  let aliases : Array[ThemeAlias] = []
  for layer_index, layer in layers {
    for link in layer.aliases {
      if !aliases.any(fn(item) {
          item.name.canonical() == link.name.canonical()
        }) {
        aliases.push(link)
      }
    }
    for token in layer.tokens {
      let path = token.path.canonical()
      let existing_index = find_token_index(tokens, path)
      match existing_index {
        Some(index) => {
          tokens[index] = token
          provenance[index] = ThemeProvenance::new(
            path,
            layer_index,
            "layer " + layer_index.to_string(),
          )
        }
        None => {
          tokens.push(token)
          provenance.push(
            ThemeProvenance::new(
              path,
              layer_index,
              "layer " + layer_index.to_string(),
            ),
          )
        }
      }
    }
  }
  { tokens, aliases, provenance }
}

///|
pub fn ThemeDocument::provenance(
  self : ThemeDocument,
  path : String,
) -> String? {
  let wanted = TokenPath::from_string(path).canonical()
  for item in self.provenance {
    if item.path == wanted {
      return Some(item.source)
    }
  }
  None
}

///|
pub fn ThemeDocument::layer_for(self : ThemeDocument, path : String) -> Int? {
  let wanted = TokenPath::from_string(path).canonical()
  for item in self.provenance {
    if item.path == wanted {
      return Some(item.layer)
    }
  }
  None
}

///|
pub fn ThemeDocument::with_source(
  self : ThemeDocument,
  source : String,
) -> ThemeDocument {
  let provenance : Array[ThemeProvenance] = []
  for token in self.tokens {
    provenance.push(ThemeProvenance::new(token.path.canonical(), 0, source))
  }
  { ..self, provenance, }
}

///|
pub fn ThemeDocument::override_token(
  self : ThemeDocument,
  token : ThemeToken,
  source : String,
) -> ThemeDocument {
  ThemeDocument::merge_layers([
    self,
    ThemeDocument::empty().add(token).with_source(source),
  ])
}

///|
fn find_token_index(tokens : Array[ThemeToken], path : String) -> Int? {
  for i, token in tokens {
    if token.path.canonical() == path {
      return Some(i)
    }
  }
  None
}