///|
/// Enhanced CSS Properties and Builders for respo_css

///|
/// Border Radius Properties
pub(all) struct CssBorderRadius {
  top_left : CssSize?
  top_right : CssSize?
  bottom_left : CssSize?
  bottom_right : CssSize?
}

///|
pub fn CssBorderRadius::new(
  top_left? : CssSize,
  top_right? : CssSize,
  bottom_left? : CssSize,
  bottom_right? : CssSize,
) -> CssBorderRadius {
  CssBorderRadius::{ top_left, top_right, bottom_left, bottom_right }
}

///|
pub fn CssBorderRadius::to_string(self : CssBorderRadius) -> String {
  let parts = []
  if self.top_left is Some(v) {
    parts.push("border-top-left-radius: " + v.to_string())
  }
  if self.top_right is Some(v) {
    parts.push("border-top-right-radius: " + v.to_string())
  }
  if self.bottom_left is Some(v) {
    parts.push("border-bottom-left-radius: " + v.to_string())
  }
  if self.bottom_right is Some(v) {
    parts.push("border-bottom-right-radius: " + v.to_string())
  }
  parts.join(";")
}

///|
pub fn CssBorderRadius::to_styles(
  self : CssBorderRadius,
) -> Array[(String, String)] {
  let styles = []
  if self.top_left is Some(v) {
    styles.push(("border-top-left-radius", v.to_string()))
  }
  if self.top_right is Some(v) {
    styles.push(("border-top-right-radius", v.to_string()))
  }
  if self.bottom_left is Some(v) {
    styles.push(("border-bottom-left-radius", v.to_string()))
  }
  if self.bottom_right is Some(v) {
    styles.push(("border-bottom-right-radius", v.to_string()))
  }
  styles
}

///|
/// Box Shadow Builder
pub(all) enum CssBoxShadowType {
  Outer
  Inset
} derive(Eq)

///|
pub(all) struct CssBoxShadow {
  x : CssSize
  y : CssSize
  blur : CssSize
  spread : CssSize?
  color : CssColor
  shadow_type : CssBoxShadowType
}

///|
pub fn CssBoxShadow::new(
  x? : CssSize = Px(0.0),
  y? : CssSize = Px(0.0),
  blur? : CssSize = Px(0.0),
  spread? : CssSize,
  color? : CssColor = Black,
  shadow_type? : CssBoxShadowType = Outer,
) -> CssBoxShadow {
  CssBoxShadow::{ x, y, blur, spread, color, shadow_type }
}

///|
pub impl Show for CssBoxShadow with output(self, logger) {
  let mut ret = ""
  if self.shadow_type == Inset {
    ret = ret + "inset "
  }
  ret = ret +
    self.x.to_string() +
    " " +
    self.y.to_string() +
    " " +
    self.blur.to_string() +
    " "
  if self.spread is Some(v) {
    ret = ret + v.to_string() + " "
  }
  ret = ret + self.color.to_string()
  logger.write_string(ret)
}

///|
/// Linear Gradient Builder
pub(all) enum CssGradientAngle {
  Deg(Int)
  Direction(String)
} derive(Eq)

///|
pub(all) struct CssGradientStop {
  color : CssColor
  position : CssSize?
} derive(Eq)

///|
pub fn CssGradientStop::new(
  color : CssColor,
  position? : CssSize,
) -> CssGradientStop {
  CssGradientStop::{ color, position }
}

///|
pub(all) enum CssGradient {
  Linear(CssGradientAngle?, Array[CssGradientStop])
  Radial(Array[CssGradientStop])
  Conic(CssGradientAngle?, Array[CssGradientStop])
} derive(Eq)

///|
pub impl Show for CssGradient with output(self, logger) {
  let ret = match self {
    Linear(angle, stops) => {
      let mut result = "linear-gradient("
      match angle {
        Some(Deg(d)) => result = result + d.to_string() + "deg, "
        Some(Direction(dir)) => result = result + dir + ", "
        None => ()
      }
      let stop_strs = stops.map(fn(stop) {
        let color_str = stop.color.to_string()
        match stop.position {
          Some(pos) => color_str + " " + pos.to_string()
          None => color_str
        }
      })
      result = result + stop_strs.join(", ")
      result + ")"
    }
    Radial(stops) => {
      let mut result = "radial-gradient("
      let stop_strs = stops.map(fn(stop) {
        let color_str = stop.color.to_string()
        match stop.position {
          Some(pos) => color_str + " " + pos.to_string()
          None => color_str
        }
      })
      result = result + stop_strs.join(", ")
      result + ")"
    }
    Conic(angle, stops) => {
      let mut result = "conic-gradient("
      match angle {
        Some(Deg(d)) => result = result + "from " + d.to_string() + "deg, "
        Some(Direction(dir)) => result = result + "from " + dir + ", "
        None => ()
      }
      let stop_strs = stops.map(fn(stop) {
        let color_str = stop.color.to_string()
        match stop.position {
          Some(pos) => color_str + " " + pos.to_string()
          None => color_str
        }
      })
      result = result + stop_strs.join(", ")
      result + ")"
    }
  }
  logger.write_string(ret)
}

///|
/// Transition Builder
pub(all) struct CssTransitionProperty {
  property : String
  duration : CssDuration
  timing_function : CssTimingFunction?
  delay : CssDuration?
}

///|
pub fn CssTransitionProperty::new(
  property : String,
  duration : CssDuration,
  timing_function? : CssTimingFunction,
  delay? : CssDuration,
) -> CssTransitionProperty {
  CssTransitionProperty::{ property, duration, timing_function, delay }
}

///|
pub impl Show for CssTransitionProperty with output(self, logger) {
  let mut ret = self.property + " " + self.duration.to_string()
  if self.timing_function is Some(tf) {
    ret = ret + " " + tf.to_string()
  }
  if self.delay is Some(d) {
    ret = ret + " " + d.to_string()
  }
  logger.write_string(ret)
}

///|
/// Keyframes Definition
pub(all) struct CssKeyframe {
  position : Int
  styles : Array[(String, String)]
}

///|
pub fn CssKeyframe::new(
  position : Int,
  styles : Array[(String, String)],
) -> CssKeyframe {
  CssKeyframe::{ position, styles }
}

///|
pub(all) struct CssKeyframes {
  name : String
  keyframes : Array[CssKeyframe]
}

///|
pub fn CssKeyframes::new(
  name : String,
  keyframes : Array[CssKeyframe],
) -> CssKeyframes {
  CssKeyframes::{ name, keyframes }
}

///|
pub fn CssKeyframes::to_css_string(self : CssKeyframes) -> String {
  let mut css = "@keyframes " + self.name + " {\n"
  for keyframe in self.keyframes {
    css = css + "  " + keyframe.position.to_string() + "% {\n"
    for pair in keyframe.styles {
      let (prop, value) = pair
      css = css + "    " + prop + ": " + value + ";\n"
    }
    css = css + "  }\n"
  }
  css = css + "}\n"
  css
}

///|
/// Pointer Events
pub(all) enum CssPointerEvents {
  Auto
  None
  Visible
  Painted
  Fill
  Stroke
  All
  Inherit
} derive(Eq)

///|
pub impl Show for CssPointerEvents with output(self, logger) {
  let ret = match self {
    Auto => "auto"
    None => "none"
    Visible => "visible"
    Painted => "painted"
    Fill => "fill"
    Stroke => "stroke"
    All => "all"
    Inherit => "inherit"
  }
  logger.write_string(ret)
}

///|
/// Letter Spacing
pub(all) enum CssLetterSpacing {
  Normal
  Value(CssSize)
  Custom(String)
} derive(Eq)

///|
pub impl Show for CssLetterSpacing with output(self, logger) {
  let ret = match self {
    Normal => "normal"
    Value(size) => size.to_string()
    Custom(value) => value
  }
  logger.write_string(ret)
}

///|
/// Text Shadow
pub(all) struct CssTextShadow {
  x : CssSize
  y : CssSize
  blur : CssSize
  color : CssColor
}

///|
pub fn CssTextShadow::new(
  x? : CssSize = Px(0.0),
  y? : CssSize = Px(0.0),
  blur? : CssSize = Px(0.0),
  color? : CssColor = Black,
) -> CssTextShadow {
  CssTextShadow::{ x, y, blur, color }
}

///|
pub impl Show for CssTextShadow with output(self, logger) {
  let ret = self.x.to_string() +
    " " +
    self.y.to_string() +
    " " +
    self.blur.to_string() +
    " " +
    self.color.to_string()
  logger.write_string(ret)
}

///|
/// Clip Path
pub(all) enum CssClipPath {
  Circle(CssSize, CssSize, CssSize)
  Ellipse(CssSize, CssSize, CssSize, CssSize)
  Inset(CssSize, CssSize, CssSize, CssSize)
  Polygon(Array[String])
  Url(String)
  Custom(String)
} derive(Eq)

///|
pub impl Show for CssClipPath with output(self, logger) {
  let ret = match self {
    Circle(radius, x, y) => {
      let r = radius.to_string()
      let px = x.to_string()
      let py = y.to_string()
      "circle(" + r + " at " + px + " " + py + ")"
    }
    Ellipse(rx, ry, x, y) => {
      let rx_str = rx.to_string()
      let ry_str = ry.to_string()
      let px = x.to_string()
      let py = y.to_string()
      "ellipse(" + rx_str + " " + ry_str + " at " + px + " " + py + ")"
    }
    Inset(top, right, bottom, left) => {
      let t = top.to_string()
      let r = right.to_string()
      let b = bottom.to_string()
      let l = left.to_string()
      "inset(" + t + " " + r + " " + b + " " + l + ")"
    }
    Polygon(points) => "polygon(" + points.join(", ") + ")"
    Url(url) => "url(" + url + ")"
    Custom(value) => value
  }
  logger.write_string(ret)
}

///|
/// Background Image
pub(all) enum CssBackgroundImage {
  LinearGradient(CssGradient)
  RadialGradient(CssGradient)
  ConicGradient(CssGradient)
  Url(String)
  Multiple(Array[CssBackgroundImage])
  None
  Custom(String)
} derive(Eq)

///|
pub impl Show for CssBackgroundImage with output(self, logger) {
  let ret = match self {
    LinearGradient(gradient) => gradient.to_string()
    RadialGradient(gradient) => gradient.to_string()
    ConicGradient(gradient) => gradient.to_string()
    Url(url) => "url(" + url + ")"
    Multiple(images) => images.map(fn(img) { img.to_string() }).join(", ")
    None => "none"
    Custom(value) => value
  }
  logger.write_string(ret)
}