; [ʞ] ansi.b
;  ~ lexi hale <lexi@hale.su>
;  🄯 GNU AGPL v3
;  ? a terminal text formatter for boron
;  > do read/text %ansi.b
;  @ vim: ft=rebol

stdin:  open 0
stdout: open 1
stderr: open 2

fmt-code-stack: []

in-list: func [i lst] [ either (find lst i) [true] [false] ]

ansi: context [
	escape: to-string #{1B5B}
	fmt: "m" with: ";"
	style: context [
		hl: "1" ul: "4" rv: "7" sl: "3" dim: "2" 
	]
	color: context [
		red: "1"    green: "2"
		yellow: "3" blue: "4"
		purple: "5" cyan: "6"
		white: "7" black: "0"
	]
	fg: color   bg: color

	frame: context [
		kind: none
		value: none
	]
]

fmt-push: func[k v] [ any [
	all [
		f-kind: in ansi k
		f-val: in (do f-kind) v
		
		frame: make ansi/frame [
			kind: f-kind
			value: f-val
		]

		append fmt-code-stack frame
	] throw 'bad-argument
]]

fmt-pop: does [ pop fmt-code-stack ]

fmt-seq: func[/local] [
	active-styles: []
	active-colors: context [
		fg: none
		bg: none
	]

	foreach frame fmt-code-stack [
		either eq? frame/kind 'style [
			ifn in-list frame/value active-styles [
				append active-styles frame/value
			] 
		] /* or */ [
			ck: in active-colors (unbind frame/kind)
			set ck frame/value
		]
	]

	style-string: rejoin (map i active-styles [join ";" (do i)])

	key: context [
		plain: context [ fg: "3" bg: "4" ]
		bright: context [ fg: "9" bg: "10" ]
	]
	foreach i [fg bg] [
		code: do (in key/plain i)
		color: do (in active-colors i)
		if color [
			style-string: join style-string rejoin
				[";" code do color]
		]
	]

	free active-styles

	rejoin [ ansi/escape style-string ansi/fmt ]
]

fmt: func [lst] [
	depth: 0
	evaluated: map term (join lst fmt-seq) [
		case [
			paren? term [
				fmt-push (first term) (second term)
				++ depth fmt-seq
			]
			word? term [ do term ]
			block? term [ fmt term ]
			string? term [ term ]
		]
	]
	loop depth [fmt-pop]
	rejoin evaluated
]
