/// The KCL standard library
///
/// Contains frequently used constants, functions for interacting with the KittyCAD servers to
/// create sketches and geometry, and utility functions.
///
/// The standard library is organised into modules (listed below), but most things are always available
/// in KCL programs.
///
/// You might also want the [KCL language reference](/docs/kcl-lang) or the [KCL guide](https://zoo.dev/docs/kcl-book/intro.html).

@no_std
@settings(defaultLengthUnit = mm, kclVersion = 1.0, experimentalFeatures = allow)

// Note that everything in the prelude is treated as exported.

export import * from "std::types"
export import "std::gdt"
export import "std::units"
export import * from "std::array"
export import * from "std::math"
export import * from "std::sketch"
export import "std::solver"
export import * from "std::solid"
export import * from "std::transform"
export import "std::turns"
export import "std::sweep"
export import "std::appearance"
export import "std::vector"
export import "std::hole"
@(experimental = true)
export import * from "std::runtime"

/// An abstract 3d plane aligned with the X and Y axes. Its normal is the positive Z axis.
export XY = {
  origin = { x = 0, y = 0, z = 0 },
  xAxis = { x = 1, y = 0, z = 0 },
  yAxis = { x = 0, y = 1, z = 0 },
}: Plane

/// An abstract 3d plane aligned with the X and Z axes. Its normal is the negative Y axis.
export XZ = {
  origin = { x = 0, y = 0, z = 0 },
  xAxis = { x = 1, y = 0, z = 0 },
  yAxis = { x = 0, y = 0, z = 1 },
}: Plane

/// An abstract 3d plane aligned with the Y and Z axes. Its normal is the positive X axis.
export YZ = {
  origin = { x = 0, y = 0, z = 0 },
  xAxis = { x = 0, y = 1, z = 0 },
  yAxis = { x = 0, y = 0, z = 1 },
}: Plane

/// The X-axis (can be used in both 2d and 3d contexts).
export X = {
  origin = [0, 0, 0],
  direction = [1, 0, 0],
}: Axis3d

/// The Y-axis (can be used in both 2d and 3d contexts).
export Y = {
  origin = [0, 0, 0],
  direction = [0, 1, 0],
}: Axis3d

/// The 3D Z-axis.
export Z = {
  origin = [0, 0, 0],
  direction = [0, 0, 1],
}: Axis3d

/// Identifies the starting face of an extrusion. I.e., the face which is extruded.
export START = 'start': TaggedFace

/// Identifies the ending face of an extrusion. I.e., the new face created by an extrusion.
export END = 'end': TaggedFace

/// Specifies that a new object is created during extrusion.
export NEW = "new": string

/// Specifies that the extrusion will be pulled into or pushed out of the existing object,
/// modifying it without creating a new object.
export MERGE = "merge": string

/// When bodies are solid, they have a top and bottom, and enclose all space between them.
export SOLID = "solid": string

/// When bodies are surfaces, they have zero thickness. They are perfectly flat and do not
/// enclose any space.
export SURFACE = "surface": string

/// Counterclockwise circular direction, currently used by `region()`.
export CCW = "ccw": string

/// Clockwise circular direction, currently used by `region()`.
export CW = "cw": string

/// Create a helix.
///
/// ```kcl,no3d,legacySketch
/// // Create a helix around the Z axis.
/// helixPath = helix(
///     angleStart = 0,
///     ccw = true,
///     revolutions = 5,
///     length = 10,
///     radius = 5,
///     axis = Z,
///  )
///
/// // Create a spring by sweeping around the helix path.
/// springSketch = startSketchOn(XZ)
///     |> circle( center = [5, 0], radius = 0.5)
///     |> sweep(path = helixPath)
/// ```
///
/// ```kcl,no3d,legacySketch
/// // Create a helix around an edge.
/// helper001 = startSketchOn(XZ)
///  |> startProfile(at = [0, 0])
///  |> line(end = [0, 10], tag = $edge001)
///
/// helixPath = helix(
///     angleStart = 0,
///     ccw = true,
///     revolutions = 5,
///     length = 10,
///     radius = 5,
///     axis = edge001,
///  )
///
/// // Create a spring by sweeping around the helix path.
/// springSketch = startSketchOn(XZ)
///     |> circle( center = [5, 0], radius = 0.5 )
///     |> sweep(path = helixPath)
/// ```
///
/// ```kcl,no3d,legacySketch
/// // Create a helix around a custom axis.
/// helixPath = helix(
///     angleStart = 0,
///     ccw = true,
///     revolutions = 5,
///     length = 10,
///     radius = 5,
///     axis = {
///         direction = [0, 0, 1.0],
///         origin = [0, 0.25, 0]
///     }
///  )
///
/// // Create a spring by sweeping around the helix path.
/// springSketch = startSketchOn(XZ)
///     |> circle( center = [5, 0], radius = 1 )
///     |> sweep(path = helixPath)
/// ```
///
/// ```kcl,no3d,legacySketch
/// // Create a helix on a cylinder.
///
/// part001 = startSketchOn(XY)
///   |> circle( center = [5, 5], radius= 10 )
///   |> extrude(length = 10)
///
/// helix(
///     angleStart = 0,
///     ccw = true,
///     revolutions = 16,
///     cylinder = part001,
///  )
/// ```
/// ```kcl,sketchSolve
/// // Demonstrate building a helix where the central axis is a line defined in a sketch block.
/// // First, here's the sketch block with a line:
/// sketch002 = sketch(on = XZ) {
///   line1 = line(start = [var -0.82mm, var 3.4mm], end = [var -1.58mm, var -5.24mm])
/// }
///
/// // Create a helix around the line in the sketch above.
/// helixPath = helix(
///   angleStart = 0,
///   ccw = true,
///   revolutions = 5,
///   length = 10,
///   radius = 5,
///   axis = sketch002.line1,
/// )
///
/// // Create a spring by sweeping around the helix path.
/// springSketch = startSketchOn(XZ)
///   |> circle(center = [5, 0], radius = 0.5)
///   |> sweep(path = helixPath)
/// ```
@(impl = std_rust, feature_tree = true)
export fn helix(
  /// Number of revolutions.
  revolutions: number(_),
  /// Start angle.
  angleStart: number(Angle),
  /// Is the helix rotation counter clockwise? The default is `false`.
  ccw?: bool,
  /// Radius of the helix.
  @(includeInSnippet = true)
  radius?: number(Length),
  /// Axis to use for the helix.
  /// The center of the helix's base will be at this axis's origin point.
  @(includeInSnippet = true)
  axis?: Axis3d | Edge | Segment,
  /// Length of the helix. This is not necessary if the helix is created around an edge. If not given the length of the edge is used.
  @(includeInSnippet = true)
  length?: number(Length),
  /// Cylinder to create the helix on.
  cylinder?: Solid,
): Helix {}

/// Offset a plane by a distance along its normal.
///
/// For example, if you offset the `XZ` plane by 10, the new plane will be parallel to the `XZ`
/// plane and 10 units away from it.
///
/// ```kcl,legacySketch
/// // Loft a square and a circle on the `XY` plane using offset.
/// squareSketch = startSketchOn(XY)
///     |> startProfile(at = [-100, 200])
///     |> line(end = [200, 0])
///     |> line(end = [0, -200])
///     |> line(end = [-200, 0])
///     |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
///     |> close()
///
/// circleSketch = startSketchOn(offsetPlane(XY, offset = 150))
///     |> circle( center = [0, 100], radius = 50 )
///
/// loft([squareSketch, circleSketch])
/// ```
///
/// ```kcl,legacySketch
/// // Loft a square and a circle on the `XZ` plane using offset.
/// squareSketch = startSketchOn(XZ)
///     |> startProfile(at = [-100, 200])
///     |> line(end = [200, 0])
///     |> line(end = [0, -200])
///     |> line(end = [-200, 0])
///     |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
///     |> close()
///
/// circleSketch = startSketchOn(offsetPlane(XZ, offset = 150))
///     |> circle( center = [0, 100], radius = 50 )
///
/// loft([squareSketch, circleSketch])
/// ```
///
/// ```kcl,legacySketch
/// // Loft a square and a circle on the `YZ` plane using offset.
/// squareSketch = startSketchOn(YZ)
///     |> startProfile(at = [-100, 200])
///     |> line(end = [200, 0])
///     |> line(end = [0, -200])
///     |> line(end = [-200, 0])
///     |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
///     |> close()
///
/// circleSketch = startSketchOn(offsetPlane(YZ, offset = 150))
///     |> circle( center = [0, 100], radius = 50 )
///
/// loft([squareSketch, circleSketch])
/// ```
///
/// ```kcl,legacySketch
/// // Loft a square and a circle on the `-XZ` plane using offset.
/// squareSketch = startSketchOn(-XZ)
///     |> startProfile(at = [-100, 200])
///     |> line(end = [200, 0])
///     |> line(end = [0, -200])
///     |> line(end = [-200, 0])
///     |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
///     |> close()
///
/// circleSketch = startSketchOn(offsetPlane(-XZ, offset = 150))
///     |> circle(center = [0, 100], radius = 50)
///
/// loft([squareSketch, circleSketch])
/// ```
///
/// ```kcl,legacySketch
/// // A circle on the XY plane
/// startSketchOn(XY)
///   |> startProfile(at = [0, 0])
///   |> circle( radius = 10, center = [0, 0] )
///
/// // Triangle on the plane 4 units above
/// startSketchOn(offsetPlane(XY, offset = 4))
///   |> startProfile(at = [0, 0])
///   |> line(end = [10, 0])
///   |> line(end = [0, 10])
///   |> close()
/// ```
@(impl = std_rust, feature_tree = true)
export fn offsetPlane(
  /// The plane (e.g. `XY`) which this new plane is created from.
  @plane: Plane,
  /// Distance from the standard plane this new plane will be created at.
  offset: number(Length),
): Plane {}

/// Clone a sketch or solid.
///
/// This works essentially like a copy-paste operation. It creates a perfect replica
/// at that point in time that you can manipulate individually afterwards.
///
/// This doesn't really have much utility unless you need the equivalent of a double
/// instance pattern with zero transformations.
///
/// Really only use this function if YOU ARE SURE you need it. In most cases you
/// do not need clone and using a pattern with `instance = 2` is more appropriate.
///
/// ```kcl,legacySketch
/// // Clone a basic sketch and move it and extrude it.
/// exampleSketch = startSketchOn(XY)
///   |> startProfile(at = [0, 0])
///   |> line(end = [10, 0])
///   |> line(end = [0, 10])
///   |> line(end = [-10, 0])
///   |> close()
///
/// clonedSketch = clone(exampleSketch)
///     |> scale(
///     x = 1.0,
///     y = 1.0,
///     z = 2.5,
///     )
///     |> translate(
///         x = 15.0,
///         y = 0,
///         z = 0,
///     )
///
/// extrude(clonedSketch, length = 5)
/// ```
///
/// ```kcl,legacySketch
/// // Clone a basic solid and move it.
///
/// exampleSketch = startSketchOn(XY)
///   |> startProfile(at = [0, 0])
///   |> line(end = [10, 0])
///   |> line(end = [0, 10])
///   |> line(end = [-10, 0])
///   |> close()
///
/// myPart = extrude(exampleSketch, length = 5)
/// clonedPart = clone(myPart)
///     |> translate(
///         x = 25.0,
///     )
/// ```
///
/// ```kcl,legacySketch
/// // Translate and rotate a cloned sketch to create a loft.
///
/// sketch001 = startSketchOn(XY)
///         |> startProfile(at = [-10, 10])
///         |> xLine(length = 20)
///         |> yLine(length = -20)
///         |> xLine(length = -20)
///         |> close()
///
/// sketch002 = clone(sketch001)
///     |> translate(x = 0, y = 0, z = 20)
///     |> rotate(axis = [0, 0, 1.0], angle = 45)
///
/// loft([sketch001, sketch002])
/// ```
///
/// ```kcl,legacySketch
/// // Translate a cloned solid. Fillet only the clone.
///
/// sketch001 = startSketchOn(XY)
/// |> startProfile(at = [-10, 10])
/// |> xLine(length = 20)
/// |> yLine(length = -20)
/// |> xLine(length = -20, tag = $face0)
/// |> line(endAbsolute = [profileStart()], tag=$face1)
/// |> extrude(length = 5, tagEnd=$end1)
///
/// sketch002 = clone(sketch001)
/// |> translate(x = 0, y = 0, z = 20)
///
/// fillet(
/// sketch002,
/// radius = 2,
/// tags = [getCommonEdge(faces = [sketch002.sketch.tags.face0, sketch002.sketch.tags.face1])],
/// )
/// ```
///
/// ```kcl,legacySketch
/// // You can reuse the tags from the original geometry with the cloned geometry.
///
/// sketch001 = startSketchOn(XY)
///   |> startProfile(at = [0, 0])
///   |> line(end = [10, 0])
///   |> line(end = [0, 10], tag = $sketchingFace)
///   |> line(end = [-10, 0])
///   |> close()
///
/// sketch002 = clone(sketch001)
///   |> translate(x = 15, y = 0, z = 5)
///   |> extrude(length = 5)
///
/// startSketchOn(sketch002, face = sketch002.sketch.tags.sketchingFace)
///   |> startProfile(at = [4, 6])
///   |> line(end = [3, 0])
///   |> line(end = [0, 3])
///   |> line(end = [-3, 0])
///   |> close()
///   |> extrude(length = 5)
/// ```
///
/// ```kcl,legacySketch
/// // You can also use the tags from the original geometry to fillet the cloned geometry.
///
/// width = 20
/// length = 10
/// thickness = 1
/// filletRadius = 2
///
/// mountingPlateSketch = startSketchOn(XY)
///   |> startProfile(at = [-width/2, -length/2])
///   |> line(endAbsolute = [width/2, -length/2], tag = $edge1)
///   |> line(endAbsolute = [width/2, length/2], tag = $edge2)
///   |> line(endAbsolute = [-width/2, length/2], tag = $edge3)
///   |> close(tag = $edge4)
///
/// mountingPlate = extrude(mountingPlateSketch, length = thickness)
///
/// clonedMountingPlate = clone(mountingPlate)
///
/// fillet(
///       clonedMountingPlate,
///        radius = filletRadius,
///        tags = [
///          getNextAdjacentEdge(clonedMountingPlate.sketch.tags.edge1),
///          getNextAdjacentEdge(clonedMountingPlate.sketch.tags.edge2),
///          getNextAdjacentEdge(clonedMountingPlate.sketch.tags.edge3),
///          getNextAdjacentEdge(clonedMountingPlate.sketch.tags.edge4)
///        ],
///      )
///   |> translate(x = 0, y = 50, z = 0)
/// ```
///
/// ```kcl,legacySketch
/// // Create a spring by sweeping around a helix path from a cloned sketch.
///
/// // Create a helix around the Z axis.
/// helixPath = helix(
///     angleStart = 0,
///     ccw = true,
///     revolutions = 4,
///     length = 10,
///     radius = 5,
///     axis = Z,
///  )
///
///
/// springSketch = startSketchOn(XZ)
///     |> circle( center = [0, 0], radius = 1)
///
/// // Create a spring by sweeping around the helix path.
/// sweepedSpring = clone(springSketch)
///     |> translate(x=5)
///     |> sweep(path = helixPath)
/// ```
///
/// ```kcl,legacySketch
/// // A donut shape from a cloned sketch.
/// sketch001 = startSketchOn(XY)
///     |> circle( center = [15, 0], radius = 5 )
///
/// sketch002 = clone(sketch001)
///    |> translate( z = 30)
///     |> revolve(
///         angle = 360,
///         axis = Y,
///     )
/// ```
///
/// ```kcl,legacySketch
/// // Sketch on the end of a revolved face by tagging the end face.
/// // This shows the cloned geometry will have the same tags as the original geometry.
///
/// exampleSketch = startSketchOn(XY)
///   |> startProfile(at = [4, 12])
///   |> line(end = [2, 0])
///   |> line(end = [0, -6])
///   |> line(end = [4, -6])
///   |> line(end = [0, -6])
///   |> line(end = [-3.75, -4.5])
///   |> line(end = [0, -5.5])
///   |> line(end = [-2, 0])
///   |> close()
///
/// example001 = revolve(exampleSketch, axis = Y, angle = 180, tagEnd = $end01)
///
/// // example002 = clone(example001)
/// // |> translate(x = 0, y = 20, z = 0)
///
/// // Sketch on the cloned face.
/// // exampleSketch002 = startSketchOn(example002, face = example002.sketch.tags.end01)
/// //  |> startProfile(at = [4.5, -5])
/// //  |> line(end = [0, 5])
/// //  |> line(end = [5, 0])
/// //  |> line(end = [0, -5])
/// //  |> close()
///
/// // example003 = extrude(exampleSketch002, length = 5)
/// ```
///
/// ```kcl,sketchSyntaxAgnostic
/// // Clone an imported model.
///
/// import "tests/inputs/cube.sldprt" as cube
///
/// myCube = cube
///
/// clonedCube = clone(myCube)
///    |> translate(
///    x = 1020,
///    )
///    |> appearance(
///        color = "#ff0000",
///        metalness = 50,
///        roughness = 50
///    )
/// ```
///
/// ```kcl,legacySketch
/// // Clone an array of sketches and solids
/// exampleSketch = startSketchOn(XY)
///  |> startProfile(at = [0, 0])
///  |> line(end = [10, 0])
///  |> line(end = [0, 10])
///  |> line(end = [-10, 0])
///  |> close()
///
/// exampleSolid = startSketchOn(XY)
///   |> startProfile(at = [-15, 0])
///   |> line(end = [10, 0])
///   |> line(end = [0, 10])
///   |> line(end = [-10, 0])
///   |> close()
///   |> extrude(length = 5)
///
///
/// inputArray = [ exampleSketch, exampleSolid]
/// outputArray = clone(inputArray)
///
/// outputArray[0]
/// |> translate(xyz = [10, 10, 10])
/// |> extrude(length = 5)
///
/// clonedSolid = outputArray[1]
/// |> translate(xyz = [-10, -10, -10])
///
/// ```
///
/// // Clone an array that includes an imported geometry, solid and sketch.
/// ```kcl,legacySketch
/// import "tests/inputs/cube.sldprt" as cube
///
/// geom = cube
/// |> translate(xyz = [1000, 50 , 50])
///
/// // Clone a basic sketch and move it and extrude it.
/// exampleSketch = startSketchOn(XY)
///   |> startProfile(at = [0, 0])
///   |> line(end = [10, 0])
///   |> line(end = [0, 10])
///   |> line(end = [-10, 0])
///   |> close()
///
/// exampleSolid = startSketchOn(XY)
///   |> startProfile(at = [-15, 0])
///   |> line(end = [10, 0])
///   |> line(end = [0, 10])
///   |> line(end = [-10, 0])
///   |> close()
///   |> extrude(length = 5)
///
///
/// inputArray = [ exampleSketch, exampleSolid, geom ]
/// outputArray = clone(inputArray)
///
/// outputArray[0]
/// |> translate(xyz = [10, 10, 10])
/// |> extrude(length = 5)
/// ```
@(impl = std_rust, feature_tree = true)
export fn clone(
  /// The sketch, solid, or imported geometry to be cloned.
  @geometries: [Sketch | Solid | ImportedGeometry; 1+],
): [Sketch | Solid | ImportedGeometry; 1+] {}

/// Asserts that a value is the boolean value true.
///
/// ```kcl,norun,sketchSyntaxAgnostic
/// kclIsFun = true
/// assertIs(kclIsFun)
/// ```
@(impl = std_rust, feature_tree = false)
export fn assertIs(
  /// Value to check. If this is the boolean value true, assert passes. Otherwise it fails..
  @actual: bool,
  /// If the value was false, the program will terminate with this error message
  error?: string,
) {}

/// Check a value meets some expected conditions at runtime. Program terminates with an error if conditions aren't met.
/// If you provide multiple conditions, they will all be checked and all must be met.
///
/// ```kcl,norun,sketchSyntaxAgnostic
/// n = 10
/// assert(n, isEqualTo = 10)
/// assert(n, isGreaterThanOrEqual = 0, isLessThan = 100, error = "number should be between 0 and 100")
/// assert(1.0000000000012, isEqualTo = 1, tolerance = 0.0001, error = "number should be almost exactly 1")
/// assert(PI, isNotEqualTo = 3, tolerance = 0.0001, error = "PI should not be exactly 3")
/// ```
@(impl = std_rust, feature_tree = false)
export fn assert(
  /// Value to check. If this is the boolean value true, assert passes. Otherwise it fails..
  @actual: number,
  /// Comparison argument. If given, checks the `actual` value is greater than this.
  isGreaterThan?: number,
  /// Comparison argument. If given, checks the `actual` value is less than this.
  isLessThan?: number,
  /// Comparison argument. If given, checks the `actual` value is greater than or equal to this.
  isGreaterThanOrEqual?: number,
  /// Comparison argument. If given, checks the `actual` value is less than or equal to this.
  isLessThanOrEqual?: number,
  /// Comparison argument. If given, checks the `actual` value is equal to this.
  @(includeInSnippet = true)
  isEqualTo?: number,
  /// Comparison argument. If given, checks the `actual` value is not equal to this.
  isNotEqualTo?: number,
  /// If `isEqualTo` or `isNotEqualTo` is used, this is the tolerance to allow for the comparison. This tolerance is used because KCL's number system has some floating-point imprecision when used with very large decimal places.
  tolerance?: number,
  /// If the value was false, the program will terminate with this error message
  error?: string,
) {}


/// Given a face index, find its ID.
/// In general, you should prefer tagging faces to using this function. Use this function if
/// you can't tag a face. For example, if the face comes from imported geometry in a .STEP file,
/// or if it's from an operation that doesn't yet support tagging the faces it creates, like `subtract`.
/// ```kcl,legacySketch
/// // Cylinder
/// cylinder = startSketchOn(XY)
///   |> circle(center = [0, 0], radius = 4.09, tag = $seg01)
///   |> extrude(length = 5)
///
/// // Delete the face at index 2
/// // (the top face)
/// deleteFace(cylinder, faces = [faceId(cylinder, index = 2)])
/// ```
@(impl = std_rust, feature_tree = false)
export fn faceId(
  /// The solid whose faces we're trying to find
  @body: Solid,
  /// Face to identify.
  /// The index is a stable ordering of faces, used when you can't get the
  /// usual ID of a face.
  index: number(Count),
): TaggedFace {}

/// Given an edge index, find its ID.
/// In general, you should prefer tagging edges to using this function. Use this function if
/// you can't tag an edge. For example, if the edge comes from imported geometry in a .STEP file,
/// or if it's from an operation that doesn't yet support tagging the edges it creates, like `subtract`.
/// ```kcl,legacySketch
/// // Cylinder
/// cylinder = startSketchOn(XY)
///   |> circle(center = [0, 0], radius = 4.09, tag = $seg01)
///   |> extrude(length = 5)
///   // Fillet the edge at index 2, i.e. the top edge.
///   |> fillet(radius = 1, tags = [edgeId(index = 2)])
/// ```
/// ```kcl,legacySketch
/// startSketchOn(XY)
///   |> rectangle(width = 10, height = 10, center = [0, 0])
///   |> extrude(length = 2)
///   |> fillet(radius = 0.5, tags = [edgeId(closestTo = [5, 0, 2])])
/// ```
@(impl = std_rust, feature_tree = false)
export fn edgeId(
  /// The solid whose edges we're trying to find
  @body: Solid,
  /// Edge to identify.
  /// The index is a stable ordering of edges, used when you can't get the
  /// usual ID of an edge.
  index?: number(Count),
  /// Query the edge closest to this point. Uses absolute global coordinates.
  closestTo?: Point3d,
): Edge {}
