%---------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%---------------------------------------------------------------------------%
%
% Test RTTI foreign enum types.
%
%---------------------------------------------------------------------------%

:- module foreign_enum_rtti.
:- interface.

:- import_module io.

:- pred main(io::di, io::uo) is det.

%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%

:- implementation.

:- import_module construct.
:- import_module deconstruct.
:- import_module int.
:- import_module list.
:- import_module string.
:- import_module type_desc.
:- import_module univ.

main(!IO) :-
    io.write_string("Checking io.write for foreign enum ...\n", !IO),
    io.write_line(foo, !IO),
    io.write_line(bar, !IO),
    io.write_line(baz, !IO),
    io.write_string("Checking deep copy for foreign enum ...\n", !IO),
    X = [foo, bar, baz],
    copy(X, Y),
    io.write_line(Y, !IO),
    TypeDescForFoo = type_of(foo),
    io.write_string("Number of functors of foo/0: ", !IO),
    NumFunctors = det_num_functors(TypeDescForFoo),
    io.write_int(NumFunctors, !IO),
    io.nl(!IO),
    io.write_string("Checking construct.get_functor for foreign_enum ...\n",
        !IO),
    int.fold_up(test_get_functor(TypeDescForFoo), 0, NumFunctors - 1, !IO),
    io.write_string(
        "Checking construct.get_functor_ordinal for foreign_enum ...\n", !IO),
    list.foldl(check_get_functor_ordinal(TypeDescForFoo), [0, 1, 2], !IO),
    io.write_string(
        "Checking construct.construct for foreign_enum ...\n", !IO),
    list.foldl(check_construct(TypeDescForFoo), [0, 1, 2], !IO),
    io.write_string(
        "Checking deconstruct.deconstruct for foreign_enum ...\n", !IO),
    list.foldl(check_deconstruct, [foo, bar, baz], !IO).

:- pred check_deconstruct(foo::in, io::di, io::uo) is det.

check_deconstruct(Data, !IO) :-
    deconstruct(Data, do_not_allow, Name, Arity, Args),
    io.format("    name = %s\n",  [s(Name)],  !IO),
    io.format("    arity = %d\n", [i(Arity)], !IO),
    (
        Args = [],
        io.write_string("    no args\n", !IO)
    ;
        Args = [_ | _],
        io.write_string("FAILED: unexpected args for foreign enum constant.\n",
            !IO)
    ).

:- pred check_construct(type_desc::in, functor_number_lex::in,
    io::di, io::uo) is det.

check_construct(TypeDesc, LexFunctorNum, !IO) :-
    ( if construct(TypeDesc, LexFunctorNum, [], Univ) then
        io.write_string("    ", !IO),
        io.write_line(Univ, !IO)
    else
        io.write_string("FAILED: construct.construct\n", !IO)
    ).

:- pred check_get_functor_ordinal(type_desc::in, functor_number_lex::in,
        io::di, io::uo) is det.

check_get_functor_ordinal(TypeDesc, Lex, !IO) :-
    ( if Ordinal = get_functor_ordinal(TypeDesc, Lex) then
        io.format("    lex = %d, ordinal = %d\n", [i(Lex), i(Ordinal)], !IO)
    else
        io.write_string("FAILED: get_functor_ordinal\n", !IO)
    ).

:- pred test_get_functor(type_desc::in, functor_number_lex::in,
    io::di, io::uo) is det.

test_get_functor(TypeDesc, LexFunctorNum, !IO) :-
    io.format("functor_number_lex = %d\n", [i(LexFunctorNum)], !IO),
    ( if get_functor(TypeDesc, LexFunctorNum, Name, Arity, ArgTypes) then
        io.format("    name  = %s\n", [s(Name)],  !IO),
        io.format("    arity = %d\n", [i(Arity)], !IO),
        (
            ArgTypes = [],
            io.write_string("    no arguments\n", !IO)
        ;
            ArgTypes = [_ | _],
            io.write_string("FAILED: args for foreign enum functor.\n", !IO)
        )
    else
        io.write_string("FAILED: not a d.u type.\n", !IO)
    ).

:- type foo
    --->    foo
    ;       bar
    ;       baz.

:- pragma foreign_decl("C",
"
    #define CONSTANT1 300
    #define CONSTANT2 400
    #define CONSTANT3 500
").

:- pragma foreign_enum("C", foo/0,
    [
        foo - "CONSTANT1",
        bar - "CONSTANT2",
        baz - "CONSTANT3"
    ]).

:- pragma foreign_enum("C#", foo/0,
    [
        foo - "2",
        bar - "4",
        baz - "6"
    ]).

%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%
