///|
/// Text alignment relative to the draw point.
pub enum TextAlign {
  Start
  End
  Left
  Right
  Center
} derive(Eq)

///|
pub impl Show for TextAlign with fn output(self, logger) {
  match self {
    Start => logger.write_string("Start")
    End => logger.write_string("End")
    Left => logger.write_string("Left")
    Right => logger.write_string("Right")
    Center => logger.write_string("Center")
  }
}

///|
/// All `TextAlign` variants, in declaration order. Exposed so that later
/// tasks (and external callers) have a stable way to enumerate them; also
/// keeps every constructor reachable from the public API.
pub fn TextAlign::all() -> Array[TextAlign] {
  [
    TextAlign::Start,
    TextAlign::End,
    TextAlign::Left,
    TextAlign::Right,
    TextAlign::Center,
  ]
}

///|
/// Vertical baseline to which text is anchored.
pub enum TextBaseline {
  Alphabetic
  Top
  Middle
  Bottom
} derive(Eq)

///|
pub impl Show for TextBaseline with fn output(self, logger) {
  match self {
    Alphabetic => logger.write_string("Alphabetic")
    Top => logger.write_string("Top")
    Middle => logger.write_string("Middle")
    Bottom => logger.write_string("Bottom")
  }
}

///|
/// All `TextBaseline` variants, in declaration order.
pub fn TextBaseline::all() -> Array[TextBaseline] {
  [
    TextBaseline::Alphabetic,
    TextBaseline::Top,
    TextBaseline::Middle,
    TextBaseline::Bottom,
  ]
}

///|
/// Shape at the endpoints of a stroked path (and each dashed on-segment).
/// Matches HTML Canvas `CanvasRenderingContext2D.lineCap`.
pub(all) enum LineCap {
  Butt
  Round
  Square
} derive(Eq)

///|
pub impl Show for LineCap with fn output(self, logger) {
  match self {
    Butt => logger.write_string("Butt")
    Round => logger.write_string("Round")
    Square => logger.write_string("Square")
  }
}

///|
/// Materializes every LineCap variant once. Used to silence
/// `unused_constructor` warnings under `--deny-warn`.
pub fn LineCap::all() -> Array[LineCap] {
  [LineCap::Butt, LineCap::Round, LineCap::Square]
}

///|
/// Shape at interior vertices (corners) of a stroked path. Matches HTML
/// Canvas `CanvasRenderingContext2D.lineJoin`.
pub(all) enum LineJoin {
  Miter
  Round
  Bevel
} derive(Eq)

///|
pub impl Show for LineJoin with fn output(self, logger) {
  match self {
    Miter => logger.write_string("Miter")
    Round => logger.write_string("Round")
    Bevel => logger.write_string("Bevel")
  }
}

///|
pub fn LineJoin::all() -> Array[LineJoin] {
  [LineJoin::Miter, LineJoin::Round, LineJoin::Bevel]
}

///|
/// Mutable drawing state maintained by `Context`.
/// Snapshots of this struct are pushed onto a stack by `save`/`restore`.
pub struct DrawState {
  mut fill_style : FillStyle
  mut stroke_style : FillStyle
  mut line_width : Double
  mut global_alpha : Double
  mut transform : Matrix2D
  mut font : @font.TTFont?
  mut font_size : Double
  mut text_align : TextAlign
  mut text_baseline : TextBaseline
  mut clip : ClipMask?
  mut line_dash : Array[Double]
  mut line_dash_offset : Double
  mut line_cap : LineCap
  mut line_join : LineJoin
}

///|
pub fn DrawState::default() -> DrawState {
  {
    fill_style: FillStyle::Solid(Color::black()),
    stroke_style: FillStyle::Solid(Color::black()),
    line_width: 1.0,
    global_alpha: 1.0,
    transform: Matrix2D::identity(),
    font: None,
    font_size: 10.0,
    text_align: TextAlign::Start,
    text_baseline: TextBaseline::Alphabetic,
    clip: None,
    line_dash: [],
    line_dash_offset: 0.0,
    line_cap: LineCap::Butt,
    line_join: LineJoin::Miter,
  }
}

///|
/// Returns a value-copy of the state (for the save-stack).
/// `font` is copied as a reference because `@font.TTFont` is a struct.
pub fn DrawState::snapshot(self : DrawState) -> DrawState {
  {
    fill_style: self.fill_style,
    stroke_style: self.stroke_style,
    line_width: self.line_width,
    global_alpha: self.global_alpha,
    transform: self.transform,
    font: self.font,
    font_size: self.font_size,
    text_align: self.text_align,
    text_baseline: self.text_baseline,
    clip: match self.clip {
      None => None
      Some(m) => Some(m.clone())
    },
    line_dash: {
      let copy : Array[Double] = []
      for v in self.line_dash {
        copy.push(v)
      }
      copy
    },
    line_dash_offset: self.line_dash_offset,
    line_cap: self.line_cap,
    line_join: self.line_join,
  }
}