#import "@preview/cetz:0.3.4"

#let draw-strand(polyline, style: (:)) = {
  import cetz.draw: *

  set-style(
    ..cetz.styles.resolve(
      (stroke: (paint: white, thickness: 0.75pt * 8, cap: "butt")),
      base: style,
    ),
  )
  polyline
  set-style(
    ..cetz.styles.resolve(
      style,
      base: (stroke: (paint: black, thickness: 0.75pt, cap: "round")),
    ),
  )
  polyline

  set-style(stroke: (paint: black, thickness: 0.75pt, cap: "round"))
}

#let skein-canvas = body => cetz.canvas(
  length: 0.25cm,
  padding: 0.25,
  {
    import cetz.draw: *
    rect((-1, -1), (1, 1), fill: white, stroke: none)

    body
  },
)

#let arrow-size = 0.35

#let draw-arrow((x, y), angle: 0, ..style) = {
  import cetz.draw: *

  {
    let len = arrow-size / calc.sqrt(2)
    set-style(..style)

    translate(x: x, y: y)
    rotate(z: angle)
    line((0, 0), (-len, +len), ..style)
    line((0, 0), (-len, -len), ..style)
  }
}

#let skein = (
  unit: skein-canvas({
    import cetz.draw: *

    circle((0, 0), radius: 1, stroke: (paint: black, thickness: 0.75pt))
  }),
  over: skein-canvas({
    import cetz.draw: *
    draw-strand({ line((-1, -1), (1, 1)) })
    draw-strand({ line((-1, 1), (1, -1)) })
  }),
  under: skein-canvas({
    import cetz.draw: *
    draw-strand({ line((-1, 1), (1, -1)) })
    draw-strand({ line((-1, -1), (1, 1)) })
  }),
  h: skein-canvas({
    import cetz.draw: *
    draw-strand({ hobby((-1, -1), (0, -0.61), (1, -1), omega: 1) })
    draw-strand({ hobby((-1, 1), (0, +0.61), (1, 1), omega: 1) })
  }),
  v: skein-canvas({
    import cetz.draw: *
    draw-strand({ hobby((-1, -1), (-0.61, 0), (-1, 1), omega: 1) })
    draw-strand({ hobby((1, -1), (+0.61, 0), (1, 1), omega: 1) })
  }),
  strand: skein-canvas({
    import cetz.draw: *
    rect((-1, -1), (1, 1), fill: white, stroke: none)
    draw-strand({ hobby((-1, 0), (0, 0.25), (1, 0), omega: 1) })
  }),
  over-twist: skein-canvas({
    import cetz.draw: *
    draw-strand({ hobby((1.5, +1), (1, +1), (-0.5, 0), (-0.1, -1), (0, -1)) })
    draw-strand({ hobby((-1.5, +1), (-1, +1), (0.5, 0), (0.1, -1), (0, -1)) })
  }),
  under-twist: skein-canvas({
    import cetz.draw: *
    draw-strand({ hobby((-1.5, +1), (-1, +1), (0.5, 0), (0.1, -1), (0, -1)) })
    draw-strand({ hobby((1.5, +1), (1, +1), (-0.5, 0), (-0.1, -1), (0, -1)) })
  }),
)


#let skein-generic(
  kind: "over",
  direction: (+1, +1),
  arrows: (true, true),
  styles: ((:), (:)),
) = {
  skein-canvas({
    import cetz.draw: *
    if kind == "over" {
      draw-strand({ line((-1, -1), (1, 1)) }, style: styles.at(1))
      draw-strand({ line((-1, 1), (1, -1)) }, style: styles.at(0))
    }
    if kind == "under" {
      draw-strand({ line((-1, 1), (1, -1)) }, style: styles.at(0))
      draw-strand({ line((-1, -1), (1, 1)) }, style: styles.at(1))
    }

    if arrows.at(0) {
      if direction.at(0) == +1 {
        line((1 - arrow-size, -1), (1, -1), (1, -1 + arrow-size), ..styles.at(0))
      } else {
        line((-1, 1 - arrow-size), (-1, 1), (-1 + arrow-size, 1), ..styles.at(0))
      }
    }
    if arrows.at(1) {
      if direction.at(1) == +1 {
        line((1 - arrow-size, 1), (1, 1), (1, 1 - arrow-size), ..styles.at(1))
      } else {
        line((-1, -1 + arrow-size), (-1, -1), (-1 + arrow-size, -1), ..styles.at(1))
      }
    }
  })
}
