///|
pub(all) enum MaskBox {
  MaskBorderBox
  MaskPaddingBox
  MaskContentBox
  MaskFillBox
  MaskStrokeBox
  MaskViewBox
} derive(Eq, Debug)

///|
pub extend MaskBox with Eq::{equal, not_equal}

///|
pub extend MaskBox with @debug.Debug::{to_repr}

///|
pub fn MaskBox::to_css(self : MaskBox) -> String {
  match self {
    MaskBorderBox => "border-box"
    MaskPaddingBox => "padding-box"
    MaskContentBox => "content-box"
    MaskFillBox => "fill-box"
    MaskStrokeBox => "stroke-box"
    MaskViewBox => "view-box"
  }
}

///|
pub(all) enum MaskClip {
  ClipBox(MaskBox)
  NoClip
} derive(Eq, Debug)

///|
pub extend MaskClip with Eq::{equal, not_equal}

///|
pub extend MaskClip with @debug.Debug::{to_repr}

///|
pub fn MaskClip::to_css(self : MaskClip) -> String {
  match self {
    ClipBox(box) => box.to_css()
    NoClip => "no-clip"
  }
}

///|
pub(all) enum MaskMode {
  MatchSource
  Alpha
  Luminance
} derive(Eq, Debug)

///|
pub extend MaskMode with Eq::{equal, not_equal}

///|
pub extend MaskMode with @debug.Debug::{to_repr}

///|
pub fn MaskMode::to_css(self : MaskMode) -> String {
  match self {
    MatchSource => "match-source"
    Alpha => "alpha"
    Luminance => "luminance"
  }
}

///|
pub(all) enum MaskComposite {
  MaskAdd
  MaskSubtract
  MaskIntersect
  MaskExclude
} derive(Eq, Debug)

///|
pub extend MaskComposite with Eq::{equal, not_equal}

///|
pub extend MaskComposite with @debug.Debug::{to_repr}

///|
pub fn MaskComposite::to_css(self : MaskComposite) -> String {
  match self {
    MaskAdd => "add"
    MaskSubtract => "subtract"
    MaskIntersect => "intersect"
    MaskExclude => "exclude"
  }
}

///|
pub struct MaskLayer {
  image : Expression
  position : GradientPosition
  size : LayerSize
  repeat : RepeatStyle
  origin : MaskBox
  clip : MaskClip
  mode : MaskMode
  composite : MaskComposite
}

///|
pub fn[I : ImageInput] mask_layer(
  image : I,
  position~ : GradientPosition = {
    x: literal_expression("0%", "length-percentage"),
    y: literal_expression("0%", "length-percentage"),
  },
  size~ : LayerSize = {
    expression: literal_expression("auto", "background-size"),
  },
  repeat~ : RepeatStyle = Repeat,
  origin~ : MaskBox = MaskBorderBox,
  clip~ : MaskClip = ClipBox(MaskBorderBox),
  mode~ : MaskMode = MatchSource,
  composite~ : MaskComposite = MaskAdd,
) -> MaskLayer {
  let expression = image.image_expression()
  {
    image: if expression.category == "image-list" {
      invalid_expression(
        "STYLE_MASK_IMAGE", "A mask layer requires one image; use separate mask layers",
      )
    } else {
      expression
    },
    position,
    size,
    repeat,
    origin,
    clip,
    mode,
    composite,
  }
}

///|
pub struct MaskLayers {
  layers : Array[MaskLayer]
}

///|
pub fn mask_layers(layers : Array[MaskLayer]) -> MaskLayers {
  { layers: layers.copy(), }
}

///|
fn MaskLayer::longhand(self : MaskLayer, property : String) -> Expression {
  match property {
    "maskImage" => self.image
    "maskPosition" => self.position.expression()
    "maskSize" => self.size.expression
    "maskRepeat" => literal_expression(self.repeat.to_css(), "mask-repeat")
    "maskOrigin" => literal_expression(self.origin.to_css(), "mask-origin")
    "maskClip" => literal_expression(self.clip.to_css(), "mask-clip")
    "maskMode" => literal_expression(self.mode.to_css(), "mask-mode")
    _ => literal_expression(self.composite.to_css(), "mask-composite")
  }
}

///|
pub fn MaskLayers::longhands(self : MaskLayers) -> Array[(String, Expression)] {
  [
    "maskImage", "maskPosition", "maskSize", "maskRepeat", "maskOrigin", "maskClip",
    "maskMode", "maskComposite",
  ].map(property => {
    let values = self.layers.map(layer => layer.longhand(property))
    if values.is_empty() {
      values.push(
        invalid_expression("STYLE_MASK_EMPTY", "Mask layers must not be empty"),
      )
    }
    (property, group_expression(property, "", values, ", ", ""))
  })
}