#lang racket
(require "tfield.rkt")
(require web-server/servlet
web-server/servlet-env
web-server/templates
xml)
(define (render-full/string tf cont-url)
(define title (tfield-label tf))
(define text
(match (tfield/function-text tf)
[(? string? s) s]
[xs (apply string-append (map xexpr->string xs))]))
(include-template "htdocs/template.html"))
(define (render*/edit tfs parent)
(map (λ(t) (render/edit t parent)) tfs))
(define (render/edit tf parent)
(define parent-not-oneof? (not (tfield/oneof? parent)))
(define parent-not-oneof/listof? (and (not (tfield/oneof? parent))
(not (tfield/listof? parent))))
(define (input-text-of name value [disabled? #f])
`(input ([type "text"] [name ,name] [id ,name]
[value ,value] ,@(if disabled? `([disabled "disabled"]) '()))))
(match tf
[(tfield/const label name error value)
(define label? parent-not-oneof/listof?)
(render-basic/edit name '(tfield-constant) (and label? label)
(input-text-of name (format "~a" value) #t) error)]
[(tfield/boolean label name error value)
(define label? parent-not-oneof/listof?)
(render-basic/edit name '(tfield-boolean)
(and label? `(label ([for ,name]) ,label))
`(input ([type "checkbox"] [name ,name]
[id ,name] ,@(if value `([checked "checked"]) '())))
error)]
[(tfield/number label name error value raw-value)
(define label? parent-not-oneof/listof?)
(render-basic/edit name '(tfield-number) (and label? label)
(input-text-of name (or raw-value "")) error)]
[(tfield/symbol label name error value)
(define label? parent-not-oneof/listof?)
(render-basic/edit name '(tfield-symbol) (and label? label)
(input-text-of name (if value (symbol->string value) ""))
error)]
[(tfield/string label name error value non-empty?)
(define label? parent-not-oneof/listof?)
(render-basic/edit name '(tfield-string) (and label? label)
(input-text-of name (or value "")) error)]
[(? tfield/struct? _) (render-struct/edit tf parent)]
[(? tfield/oneof? _) (render-oneof/edit tf parent)]
[(? tfield/listof? _) (render-listof/edit tf parent)]
[(tfield/function title name error text func args result)
"blah"]
[_ (error (object-name render/edit)
(format "somehow got an unknown field type: ~a" tf))]))
(define ((div-wrapper name classes) inner)
`(div ([id ,(format "~a-div" name)]
[class ,(string-join (map symbol->string classes) " ")])
,@(if (xexpr? inner) (list inner) inner)))
(define (nest-level tf/name)
(format "nest~a" (add1 (remainder (sub1 (depth-of tf/name)) 3))))
(define (render-basic/edit name classes label input-elt error)
((div-wrapper name `(tfield tfield-basic ,@classes))
(list (if label `(span ([class "label"]) ,label) "")
input-elt
(if error `(span ([class "error"]) ,error) ""))))
(define (render-listof/edit tf parent)
(match tf
[(tfield/listof label name error base elts non-empty?)
((div-wrapper name `(tfield tfield-listof))
`(fieldset ([class ,(nest-level tf)])
(legend ,label)
,(if error `(div ([class "error error-listof"]) ,error) "")
(input ([type "hidden"] [name ,name] [id ,name]
[value ,(format "~a" (length elts))]))
(ol ([id ,(string-append name "-ol")] [class "tfield-listof sortable"])
,@(map (curry render-listof-item/edit tf) elts)
(li ([class "nosort"])
(button ([id ,(string-append name "-addbtn")]
[class "addbtn"] [type "button"])
"Add " ,(tfield-label base))))))]))
(define (render-listof-item/edit tf/listof e)
(define elt-name (tfield-name e))
`(li ([id ,(string-append elt-name "-li")])
(div ([class "listof-item"])
(div ([class "li-handle"])
(span ([class "ui-icon ui-icon-arrowthick-2-n-s"]) ""))
(button ([id ,(string-append elt-name "-delbtn")]
[class "delbtn"] [type "button"]) "")
(div ([class "listof-data"])
,(render/edit e tf/listof))
(div ([style "clear: both;"]) ""))))
(define (render-oneof/edit tf parent)
(match tf
[(tfield/oneof label name error options chosen)
(define label? (and (not (tfield/oneof? parent))
(not (tfield/listof? parent))))
(define selected-tf (and chosen (list-ref options chosen)))
(define fieldset? (and selected-tf (or (tfield/struct? selected-tf)
(tfield/listof? selected-tf))))
(define select-elt (render-select-element/edit tf))
(define label-span
`(span ,(if (and label? label)
`(label ([for ,name]) ,label " ") "") ,select-elt))
(cond
[(not selected-tf)
(render-basic/edit name '(tfield-oneof) label-span empty error)]
[fieldset?
((div-wrapper name `(tfield tfield-oneof))
`(fieldset ([class ,(nest-level tf)])
(legend ,label-span)
,(if error `(div ([class "error"]) ,error) "")
,(render/edit selected-tf tf)))]
[else
(render-basic/edit name '(tfield-oneof) label-span
(render/edit selected-tf tf) error)])]))
(define (render-select-element/edit tf/o)
(define name (tfield-name tf/o))
(define opts (tfield/oneof-options tf/o))
(define chosen (tfield/oneof-chosen tf/o))
`(select ([name ,name] [id ,name] [class "tfield-oneof"])
(option ([value "-"]
,@(if chosen '() '([selected "selected"]))) "-")
,@(map (λ(tflabel i)
`(option ([value ,(number->string i)]
,@(if (and chosen (= i chosen))
'([selected "selected"]) '())) ,tflabel))
(map tfield-label opts)
(build-list (length opts) values))))
(define (render-struct/edit tf parent)
(match tf
[(tfield/struct label name error constr args)
(define arg/content
`(ul ([class "tfield-structure"])
,@(map (λ(a) `(li ,(render/edit a tf))) args)))
(define wrapper
(if (tfield/oneof? parent)
((div-wrapper name '(tfield tfield-structure))
(list (if error `(div ([class "error"]) ,error) "")
arg/content))
((div-wrapper name '(tfield tfield-structure))
`(fieldset ([class ,(nest-level name)])
,(if label `(legend ,label) "")
,(if error `(div ([class "error"]) ,error) "")
,arg/content)) ))
wrapper]))
(define (colonize str)
(if (equal? str "") str (string-append str ": ")))
(define (render*/disp tfs parent)
(map (λ(t) (render/disp t parent)) tfs))
(define (render-basic/disp name classes label content)
((div-wrapper name `(tfield tfield-basic ,@classes))
(list (if label `(span ([class "label"]) ,(colonize label)) "")
`(span ,@(if (xexpr? content) (list content) content)))))
(define (render/disp tf parent)
(define parent-not-oneof? (not (tfield/oneof? parent)))
(define parent-not-oneof/listof? (and (not (tfield/oneof? parent))
(not (tfield/listof? parent))))
(define parent-not-listof? (not (tfield/listof? parent)))
(match tf
[(tfield/const label name error value)
(render-basic/disp name '(tfield-constant) #f label)]
[(tfield/boolean label name error value)
(render-basic/disp name '(tfield-boolean) (and parent-not-listof? label)
(if value "YES" "NO"))]
[(tfield/number label name error value raw-value)
(render-basic/disp name '(tfield-number) (and parent-not-listof? label)
(or raw-value "-"))]
[(tfield/symbol label name error value)
(render-basic/disp name '(tfield-symbol) (and parent-not-listof? label)
(or (and value (symbol->string value)) "-"))]
[(tfield/string label name error value non-empty?)
(render-basic/disp name '(tfield-string) (and parent-not-listof? label)
(if (equal? value "") "-"
(or value "-")))]
[(tfield/struct label name error constr args)
((div-wrapper name '(tfield tfield-structure))
`(fieldset (legend ,(colonize label))
(ul ,@(map (λ(a) `(li ,a)) (render*/disp args tf)))))]
[(tfield/oneof label name error options chosen)
(define selected-tf (and chosen (list-ref options chosen)))
(cond
[(not selected-tf)
((div-wrapper name `(tfield tfield-oneof))
`(span "(" ,label " not selected)"))]
[else
((div-wrapper name `(tfield tfield-oneof))
(render/disp selected-tf tf))])]
[(tfield/listof label name error base elts non-empty?)
((div-wrapper name `(tfield tfield-listof))
`(fieldset (legend ,(colonize label))
,(if (empty? elts)
"(empty)"
`(ol ,@(map (λ(a) `(li ,a)) (render*/disp elts tf))))))]
[(tfield/function title name error text func args result)
`(span ([id ,name]) "")]
[_ (error (object-name render/disp)
(format "somehow got an unknown field type: ~a" tf))]))
(provide render-full/string
render*/edit
render/edit
render-listof-item/edit
render*/disp
render/disp)