# -*- tcl -*-
# -------------------------------------------------------------------------
# emap.test
# -------------------------------------------------------------------------

source [file join [file dirname [info script]] support testutilities.tcl]

testsNeedTcl     8.4
testsNeedTcltest 2

support {
    useLocal lib/lassign84/lassign.tcl  lassign84
    useLocal lib/dict84/dict.tcl        dict84

    useLocal lib/stubs_container/container.tcl stubs::container
    useLocal lib/stubs_reader/reader.tcl       stubs::reader
    useLocal lib/stubs_genframe/genframe.tcl   stubs::gen
    useLocal lib/critcl/critcl.tcl            critcl
    useLocal lib/critcl-iassoc/iassoc.tcl     critcl::iassoc

    localcache-setup
}
testing {
    useLocal lib/critcl-emap/emap.tcl critcl::emap
}

proc exE {args} {
    list [catch {
        uplevel 1 $args
    } msg] [set msg]
}

# -------------------------------------------------------------------------
##

test emap-mode-tcl-1.0 {critcl-emap, mode: tcl (default)} -setup {
    make-demo TL {
        critcl::ccode {
            #define STATE_INIT 0
            #define STATE_MIX  1
            #define STATE_DONE 2
        }
        critcl::emap::def demo {
            init STATE_INIT
            mix  STATE_MIX
            done STATE_DONE
        }
        critcl::cproc encode {Tcl_Interp* ip Tcl_Obj* state} int {
            int scode;
            if (demo_encode (ip, state, &scode) != TCL_OK) return -1;
            return scode;
        }
        critcl::cproc decode {Tcl_Interp* ip int scode} object {
            Tcl_Obj* res = demo_decode (ip, scode);
            if (res) { Tcl_IncrRefCount (res); }
            return res;
        }
        # Encode hidden in the argtype.
        critcl::cproc xencode {Tcl_Interp* ip demo state} int {
            return state;
        }
        # Decode hidden in the resultype
        critcl::cproc xdecode {Tcl_Interp* ip int state} demo {
            return state;
        }
    }
} -body {
    res!
    res+ [encode  mix]
    res+ [xencode done]
    res+ [decode  0]
    res+ [xdecode 1]
    res+ [encode  foo]
    res+ [exE xencode bar]
    res+ [exE xdecode -2]
    res?
} -result {1 2 init mix -1 {{1 {bad demo "bar": must be done, init, or mix}}} {{1 {Invalid demo state code -2}}}}

test emap-mode-tcl-1.1 {critcl-emap, mode: tcl (default) +nocase} -setup {
    make-demo TL {
        critcl::ccode {
            #define STATE_INIT 0
            #define STATE_MIX  1
            #define STATE_DONE 2
        }
        critcl::emap::def demo {
            init STATE_INIT
            mix  STATE_MIX
            done STATE_DONE
        } -nocase

        critcl::cproc encode {Tcl_Interp* ip Tcl_Obj* state} int {
            int scode;
            if (demo_encode (ip, state, &scode) != TCL_OK) {
                return -1;
            }
            return scode;
        }
        # Encode hidden in the argtype.
        critcl::cproc xencode {Tcl_Interp* ip demo state} int {
            return state;
        }
    }
} -body {
    res!
    res+ [encode  MIX]
    res+ [xencode INIT]
    res?
} -result {1 0}

test emap-mode-tcl-1.2 {critcl-emap, mode: tcl (default), int/direct} -setup {
    make-demo TL {
        critcl::emap::def demo {
            init  0
            mix   1
            done  2
        }
        critcl::cproc encode {Tcl_Interp* ip Tcl_Obj* state} int {
            int scode;
            if (demo_encode (ip, state, &scode) != TCL_OK) return -1;
            return scode;
        }
        critcl::cproc decode {Tcl_Interp* ip int scode} object {
            Tcl_Obj* res = demo_decode (ip, scode);
            if (res) { Tcl_IncrRefCount (res); }
            return res;
        }
        # Encode hidden in the argtype.
        critcl::cproc xencode {Tcl_Interp* ip demo state} int {
            return state;
        }
        # Decode hidden in the resultype
        critcl::cproc xdecode {Tcl_Interp* ip int state} demo {
            return state;
        }
    }
} -body {
    res!
    res+ [encode  mix]
    res+ [xencode done]
    res+ [decode  0]
    res+ [xdecode 1]
    res+ [encode  foo]
    res+ [exE xencode bar]
    res+ [exE xdecode -2]
    res?
} -result {1 2 init mix -1 {{1 {bad demo "bar": must be done, init, or mix}}} {{1 {Invalid demo state code -2}}}}

# -------------------------------------------------------------------------
##

test emap-mode-c-1.0 {critcl-emap, mode: c} -setup {
    make-demo TL {
        critcl::ccode {
            #define STATE_INIT 0
            #define STATE_MIX  1
            #define STATE_DONE 2
        }
        critcl::emap::def demo {
            init STATE_INIT
            mix  STATE_MIX
            done STATE_DONE
        } -mode c
        critcl::cproc encode {Tcl_Interp* ip Tcl_Obj* state} int {
            return demo_encode_cstr (Tcl_GetString(state));
        }
        critcl::cproc decode {Tcl_Interp* ip int scode} object {
            Tcl_Obj* res = Tcl_NewStringObj (demo_decode_cstr (scode), -1);
            if (res) { Tcl_IncrRefCount (res); }
            return res;
        }
    }
} -body {
    res!
    res+ [encode mix]
    res+ [encode foo]
    res+ [decode 0]
    res+ [decode 55]
    res?
} -result {1 -1 init {{}}}

test emap-mode-c-1.2 {critcl-emap, mode: c, int/direct} -setup {
    make-demo TL {
        critcl::emap::def demo {
            init 0
            mix  1
            done 2
        } -mode c
        critcl::cproc encode {Tcl_Interp* ip Tcl_Obj* state} int {
            return demo_encode_cstr (Tcl_GetString(state));
        }
        critcl::cproc decode {Tcl_Interp* ip int scode} object {
            Tcl_Obj* res = Tcl_NewStringObj (demo_decode_cstr (scode), -1);
            if (res) { Tcl_IncrRefCount (res); }
            return res;
        }
    }
} -body {
    res!
    res+ [encode mix]
    res+ [encode foo]
    res+ [decode 0]
    res+ [decode 55]
    res?
} -result {1 -1 init {{}}}

# -------------------------------------------------------------------------
##

test emap-mode-tcl+c-1.0 {critcl-emap, mode: tcl+c} -setup {
    make-demo TL {
        critcl::ccode {
            #define STATE_INIT 0
            #define STATE_MIX  1
            #define STATE_DONE 2
        }
        critcl::emap::def demo {
            init STATE_INIT
            mix  STATE_MIX
            done STATE_DONE
        } -mode {c tcl}
        # Add -nocase as last argument for case-insensitive Tcl strings.
        critcl::cproc encode {Tcl_Interp* ip Tcl_Obj* state} int {
            return demo_encode_cstr (Tcl_GetString(state));
        }
        critcl::cproc decode {Tcl_Interp* ip int scode} object {
            Tcl_Obj* res = Tcl_NewStringObj (demo_decode_cstr (scode), -1);
            if (res) { Tcl_IncrRefCount (res); }
            return res;
        }
        # Encode hidden in the argtype.
        critcl::cproc xencode {Tcl_Interp* ip demo state} int {
            return state;
        }
        # Decode hidden in the resultype
        critcl::cproc xdecode {Tcl_Interp* ip int state} demo {
            return state;
        }
    }
} -body {
    res!
    res+ [encode  mix]  ;# 1
    res+ [xencode done] ;# 2
    res+ [decode  0]    ;# init
    res+ [xdecode 1]    ;# mix
    res+ [encode  foo]  ;# -1
    res+ [decode  55]
    res+ [exE xencode bar]
    res+ [exE xdecode -2]
    res?
} -result {1 2 init mix -1 {{}} {{1 {bad demo "bar": must be done, init, or mix}}} {{1 {Invalid demo state code -2}}}}

test emap-mode-tcl+c-1.2 {critcl-emap, mode: tcl+c, int/direct} -setup {
    make-demo TL {
        critcl::emap::def demo {
            init 0
            mix  1
            done 2
        } -mode {c tcl}
        # Add -nocase as last argument for case-insensitive Tcl strings.
        critcl::cproc encode {Tcl_Interp* ip Tcl_Obj* state} int {
            return demo_encode_cstr (Tcl_GetString(state));
        }
        critcl::cproc decode {Tcl_Interp* ip int scode} object {
            Tcl_Obj* res = Tcl_NewStringObj (demo_decode_cstr (scode), -1);
            if (res) { Tcl_IncrRefCount (res); }
            return res;
        }
        # Encode hidden in the argtype.
        critcl::cproc xencode {Tcl_Interp* ip demo state} int {
            return state;
        }
        # Decode hidden in the resultype
        critcl::cproc xdecode {Tcl_Interp* ip int state} demo {
            return state;
        }
    }
} -body {
    res!
    res+ [encode  mix]  ;# 1
    res+ [xencode done] ;# 2
    res+ [decode  0]    ;# init
    res+ [xdecode 1]    ;# mix
    res+ [encode  foo]  ;# -1
    res+ [decode  55]
    res+ [exE xencode bar]
    res+ [exE xdecode -2]
    res?
} -result {1 2 init mix -1 {{}} {{1 {bad demo "bar": must be done, init, or mix}}} {{1 {Invalid demo state code -2}}}}

# -------------------------------------------------------------------------
## Notes:
# * `+list` is a decoder extension
# * `-nocase` OTOH is an encoder tweak. No need to test here.

test emap-mode-tcl+list-1.0 {critcl-emap, mode: +list (implies tcl)} -setup {
    make-demo TL {
        critcl::ccode {
            #define STATE_INIT 0
            #define STATE_MIX  1
            #define STATE_DONE 2
        }
        critcl::emap::def demo {
            init STATE_INIT
            mix  STATE_MIX
            done STATE_DONE
        } -mode +list
        critcl::cproc decode-list {Tcl_Interp* ip int args} object {
            Tcl_Obj* res = demo_decode_list (ip, args.c, args.v);
            if (res) { Tcl_IncrRefCount (res); }
            return res;
        }
    }
} -body {
    res!
    res+ [decode-list 2 0 1 0 0 1 2]
    res+ [decode-list]
    res+ [exE decode-list 0 3]
    res?
} -result {{{done init mix init init mix done}} {{}} {{1 {Invalid demo state code 3}}}}

# -------------------------------------------------------------------------
rename exE {}
testsuiteCleanup

# Local variables:
# mode: tcl
# indent-tabs-mode: nil
# End:
