(import /src/base :as base)
(import /src/prelude :as prelude)

(defn map-values [f dict] 
 (from-pairs (map (fn [[k v]] [k (f v)]) (pairs dict))))

(def with-docs (map-values base/doc prelude/ctx))

(def sorted-names (-> with-docs keys sort))

(defn escape-underscores [str] (string/replace  "_" "\\_" str))

(defn escape-punctuation [str] (->> str
 (string/replace "?" "")
 (string/replace "!" "")
 (string/replace "/" "")))

(defn toc-entry [name] 
 (def escaped (escape-underscores name)) 
 (string "[" escaped "](#" (escape-punctuation escaped) ")"))

(def alphabetical-list
 (string/join (map toc-entry sorted-names) "&nbsp;&nbsp;&nbsp; "))

(def topics {
 "math" ["abs" "add" "angle" "atan/2" "between?" "ceil" "cos" "dec" "deg/rad" "deg/turn" "dist" "div" "div/0" "div/safe" "even?" "floor" "gt?" "gte?" "heading/vector" "inc" "inv" "inv/0" "inv/safe" "lt?" "lte?" "max" "min" "mod" "mod/0" "mod/safe" "mult" "neg" "neg?" "odd?" "pi" "pos?" "rad/deg" "rad/turn" "random" "random_int" "range" "round" "sin" "sqrt" "sqrt/safe" "square" "sub" "sum_of_squares" "tan" "tau" "to_number" "turn/deg" "turn/rad" "zero?"]
 "boolean" ["and" "bool" "bool?" "false?" "not" "or" "true?"]
 "dicts" ["any?" "assoc" "assoc?" "coll?" "count" "dict" "dict?" "diff" "dissoc" "empty?" "get" "keys" "random" "update" "values"]
 "lists" ["any?" "append" "at" "butlast" "coll?" "concat" "count" "each!" "empty?" "filter" "first" "fold" "join" "keep" "last" "list" "list?" "map" "ordered?" "random" "range" "rest" "second" "sentence" "slice"]
 "sets" ["any?" "append" "coll?" "concat" "contains?" "count" "empty?" "omit" "random" "set" "set?"]
 "tuples" ["any?" "at" "coll?" "count" "empty?" "first" "last" "ordered?" "rest" "second" "tuple?"]
 "strings" ["any?" "chars" "chars/safe" "concat" "count" "downcase" "empty?" "join" "sentence" "show" "slice" "split" "string" "string?" "strip" "to_number" "trim" "upcase" "words"]
 "types and values" ["assoc?" "bool?" "box?" "coll?" "dict?" "eq?" "fn?" "keyword?" "list?" "neq?" "nil?" "number?" "ordered?" "set?" "show" "some" "some?" "string?" "tuple?" "type"]
 "boxes and state" ["box?" "unbox" "store!" "update!"]
 "results" ["err" "err?" "ok" "ok?" "unwrap!" "unwrap_or"]
 "errors" ["assert!"]
 "turtle graphics" ["back!" "background!" "bk!" "clear!" "colors" "fd!" "forward!" "goto!" "heading" "heading/vector" "hideturtle!" "home!" "left!" "loadstate!" "lt!" "pc!" "pd!" "pencolor" "pencolor!" "pendown!" "pendown?" "penup!" "penwidth" "penwidth!" "position" "pu!" "pw!" "render_turtle!" "reset_turtle!" "right!" "rt!" "setheading!" "showturtle!" "turtle_state"]
 "environment and i/o" ["doc!" "print!" "report!" "state"]
 })

(defn capitalize [str]
 (def fst (slice str 0 1))
 (def rest (slice str 1))
 (def init_cap (string/ascii-upper fst))
 (def lower_rest (string/ascii-lower rest))
 (string init_cap lower_rest))

(defn topic-entry [topic] 
 (string "### " (capitalize topic) "\n" 
  (as-> topic _ (topics _) (array/slice _) (sort _) (map toc-entry _) 
   (string/join _ "&nbsp;&nbsp;&nbsp; "))
  "\n"))

(def by-topic (let [the-topics (-> topics keys sort)
                    topics-entries (map topic-entry the-topics)]
                (string/join topics-entries "\n")))

(defn compose-entry [name]
 	(def header (string "\n### " name "\n"))   	
 	(def the-doc (get with-docs name))
  (when (= "No documentation available." the-doc)
   (break (string header the-doc "\n")))
  (def lines (string/split "\n" the-doc))
	(def description (last lines))
  (def patterns (string/join (slice lines 1 (-> lines length dec)) "\n"))
  (def backto "[Back to top.](#ludus-prelude-documentation)\n")
 	(string header description "\n```\n" patterns "\n```\n" backto))

(compose-entry "update")

(def entries (string/join (map compose-entry sorted-names) "\n---"))

(def doc-file (string 
```
# Ludus prelude documentation
These functions are available in every Ludus script.
The documentation for any function can be found within Ludus by passing the function to `doc!`, 
e.g., running `doc! (add)` will send the documentation for `add` to the console.

For more information on the syntax & semantics of the Ludus language, see [language.md](./language.md).

The prelude itself is just a Ludus file, which you can see at [prelude.ld](./prelude.ld).

## A few notes
**Naming conventions.** Functions whose name ends with a question mark, e.g., `eq?`, return booleans.
Functions whose name ends with an exclamation point, e.g., `make!`, change state in some way.
In other words, they _do things_ rather than _calculating values_. 
Functions whose name includes a slash either convert from one value to another, e.g. `deg/rad`,
or they are variations on a function, e.g. `div/0` as a variation on `div`.

**How entries are formatted.** Each entry has a brief (sometimes too brief!) description of what it does.
It is followed by the patterns for each of its function clauses.
This should be enough to indicate order of arguments, types, and so on.

**Patterns often, but do not always, indicate types.** Typed patterns are written as `foo as :bar`,
where the type is indicated by the keyword. 
Possible ludus types are: `:nil`, `:boolean`, `:number`, `:keyword` (atomic values);
`:string` (strings are their own beast); `:tuple` and `:list` (ordered collections), `:set`s, and `:dict`ionaries (the other collection types); `:pkg` (packages, which are quasi-collections); `:fn` (functions); and `:box`es.

**Conventional types.** Ludus has two types based on conventions.
* _Result tuples._ Results are a way of modeling the result of a calculation that might fail.
The two possible values are `(:ok, value)` and `(:err, msg)`.
`msg` is usually a string describing what went wrong.
To work with result tuples, see [`unwrap!`](#unwrap) and [`unwrap_or`](#unwrap_or).
That said, usually you work with these using pattern matching.

* _Vectors._ Vectors are 2-element tuples of x and y coordinates.
The origin is `(0, 0)`. 
Many math functions take vectors as well as numbers, e.g., `add` and `mult`.
You will see vectors indicated in patterns by an `(x, y)` tuple.
You can see what this looks like in the last clause of `add`: `((x1, y1), (x2, y2))`.

## Functions by topic

```
by-topic
```
    
## All functions, alphabetically

```
alphabetical-list
```

## Function documentation

```
entries
))

(spit "prelude.md" doc-file)
