#|
list item:
  (name default-value props)

stored item:
  (value . props)
|#


define new-options-list()
  ;assert {{symbol? name} or {null? name}} "option list name should be a symbol or #nil"
  dict:new()


define add-option
  case-lambda add-option
    (dict name value props)
      define rawsym(sym)
        cond
          {keyword? sym} sym
          {{pair? sym} and {car(sym) eq? 'quote}} rawsym(cadr(sym))
          else error "invalid property list"
      ;
      define prepare-props(props alist)
        cond
          {null? props}
            reverse! alist
          else ;; car is quoted symbol; cadr is value; cddr is rest
            prepare-props
              cddr(props)
              cons(cons(rawsym(car(props)) cadr(props)) alist)
      ;;
      assert {symbol? name} "option name should be a symbol"
      assert {not dict:has?(dict name)} string-append("duplicate option: " symbol->string(name))
      dict:put! dict name (cons value prepare-props(props #nil))
      dict
    (dict name value)
      add-option dict name value #nil

define has-option?(dict name)
  assert {symbol? name} "option name should be a symbol"
  {pair? dict:find(dict name)}

;; do not use!
define option-set!(dict name value)
  assert {symbol? name} "option name should be a symbol"
  define xx dict:find(dict name)
  if {pair? xx}
    car-set! xx value
    error string-append("unknown option: " symbol->string(name))

;; do not use!
define option-remove!(dict name)
  assert {symbol? name} "option name should be a symbol"
  define xx dict:find(dict name)
  if {pair? xx}
    dict:remove! dict name
    error string-append("unknown option: " symbol->string(name))

define option-lax-remove!(dict name)
  assert {symbol? name} "option name should be a symbol"
  dict:remove! dict name

define option-ref(dict name)
  assert {symbol? name} "option name should be a symbol"
  define xx dict:find(dict name)
  if {pair? xx}
    car xx
    error string-append("unknown option: " symbol->string(name))

define option-ref-over(dict dict-over name)
  assert {symbol? name} "option name should be a symbol"
  define xx (if {null? dict-over} #nil dict:find(dict-over name))
  if {not {pair? xx}} {xx := dict:find(dict name)}
  if {pair? xx}
    car xx
    error string-append("unknown option: " symbol->string(name))

define option-props-ref(dict name)
  assert {symbol? name} "option name should be a symbol"
  define xx dict:find(dict name)
  if {pair? xx}
    cdr xx
    error string-append("unknown option: " symbol->string(name))

define option-props-ref-over(dict dict-over name)
  assert {symbol? name} "option name should be a symbol"
  define xx (if {null? dict-over} #nil dict:find(dict-over name))
  if {not {pair? xx}} {xx := dict:find(dict name)}
  if {pair? xx}
    cdr xx
    error string-append("unknown option: " symbol->string(name))

;; check-proc: (key default-value props)
define options-foreach(dict proc)
  define val
  define it ::dict:iter:new(dict)
  while ::dict:iter:next(it)
    {val := ::dict:iter:value(it)}
    proc(::dict:iter:key(it) car(val) cdr(val))
