<template name="file-header">
    /*
        Generated by sbus code generator

        Copyright (C) 2017 Red Hat

        This program is free software; you can redistribute it and/or modify
        it under the terms of the GNU General Public License as published by
        the Free Software Foundation; either version 3 of the License, or
        (at your option) any later version.

        This program is distributed in the hope that it will be useful,
        but WITHOUT ANY WARRANTY; without even the implied warranty of
        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
        GNU General Public License for more details.

        You should have received a copy of the GNU General Public License
        along with this program.  If not, see <http://www.gnu.org/licenses/>.
    */

    #include <errno.h>
    #include <talloc.h>
    #include <tevent.h>
    #include <dbus/dbus.h>

    #include "${sbus-path}/sbus_private.h"
    #include "${sbus-path}/interface/sbus_iterator_readers.h"
    #include "${sbus-path}/interface_dbus/sbus_dbus_client_async.h"
    #include "${header:arguments}"
    #include "${header:keygens}"
    #include "${header:client_properties}"

</template>

<template name="method-invoker">
    struct sbus_method_in_${input-signature}_out_${output-signature}_state {
        <toggle name="if-input-arguments">
        struct _sbus_invoker_args_${input-signature} in;
        </toggle>
        <toggle name="if-output-arguments">
        struct _sbus_invoker_args_${output-signature} *out;
        </toggle>
        <toggle name="if-raw-output">
        DBusMessage *reply;
        </toggle>
        <toggle name="if-show-dummy">
        int dummy;
        </toggle>
    };

    static void sbus_method_in_${input-signature}_out_${output-signature}_done(struct tevent_req *subreq);

    static struct tevent_req *
    sbus_method_in_${input-signature}_out_${output-signature}_send
        (TALLOC_CTX *mem_ctx,
         struct sbus_connection *conn,
         <toggle name="if-raw-input">
         DBusMessage *raw_message)
         <or>
         sbus_invoker_keygen keygen,
         const char *bus,
         const char *path,
         const char *iface,
         const char *method<loop name="in">,
         ${type} arg${index}</loop>)
         </toggle>
    {
        struct sbus_method_in_${input-signature}_out_${output-signature}_state *state;
        struct tevent_req *subreq;
        struct tevent_req *req;
        errno_t ret;

        req = tevent_req_create(mem_ctx, &state, struct sbus_method_in_${input-signature}_out_${output-signature}_state);
        if (req == NULL) {
            DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
            return NULL;
        }

        <toggle name="if-output-arguments">
        state->out = talloc_zero(state, struct _sbus_invoker_args_${output-signature});
        if (state->out == NULL) {
            DEBUG(SSSDBG_CRIT_FAILURE,
                  "Unable to allocate space for output parameters!\n");
            ret = ENOMEM;
            goto done;
        }

        </toggle>
        <loop name="in">
        state->in.arg${index} = arg${index};
        </loop>

        <toggle name="if-raw-input">
        subreq = sbus_call_method_send(state, conn, raw_message, NULL, NULL, NULL,
                                       dbus_message_get_path(raw_message),
                                       dbus_message_get_interface(raw_message),
                                       dbus_message_get_member(raw_message), NULL);
        </toggle>
        <toggle name="if-input-arguments">
        subreq = sbus_call_method_send(state, conn, NULL, keygen,
                                       (sbus_invoker_writer_fn)_sbus_invoker_write_${input-signature},
                                       bus, path, iface, method, &state->in);
        </toggle>
        <toggle name="if-empty-input">
        subreq = sbus_call_method_send(state, conn, NULL, keygen, NULL,
                                       bus, path, iface, method, NULL);
        </toggle>
        if (subreq == NULL) {
            DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
            ret = ENOMEM;
            goto done;
        }

        tevent_req_set_callback(subreq, sbus_method_in_${input-signature}_out_${output-signature}_done, req);

        ret = EAGAIN;

    done:
        if (ret != EAGAIN) {
            tevent_req_error(req, ret);
            tevent_req_post(req, conn->ev);
        }

        return req;
    }

    static void sbus_method_in_${input-signature}_out_${output-signature}_done(struct tevent_req *subreq)
    {
        struct sbus_method_in_${input-signature}_out_${output-signature}_state *state;
        struct tevent_req *req;
        DBusMessage *reply;
        errno_t ret;

        req = tevent_req_callback_data(subreq, struct tevent_req);
        state = tevent_req_data(req, struct sbus_method_in_${input-signature}_out_${output-signature}_state);

        ret = sbus_call_method_recv(state, subreq, &reply);
        talloc_zfree(subreq);
        if (ret != EOK) {
            tevent_req_error(req, ret);
            return;
        }

        <toggle name="if-raw-output">
        state->reply = reply;

        </toggle>
        <toggle name="if-output-arguments">
        ret = sbus_read_output(state->out, reply, (sbus_invoker_reader_fn)_sbus_invoker_read_${output-signature}, state->out);
        if (ret != EOK) {
            tevent_req_error(req, ret);
            return;
        }

        </toggle>
        tevent_req_done(req);
        return;
    }

    static errno_t
    sbus_method_in_${input-signature}_out_${output-signature}_recv
        (<toggle name="if-use-talloc">TALLOC_CTX *mem_ctx,
         struct tevent_req *req<or>struct tevent_req *req</toggle><toggle name="if-raw-output">,
         DBusMessage **_reply)
         <or><loop name="out">,
         ${type} _arg${index}</loop>)
        </toggle>
    {
        <toggle name="if-raw-output">
        errno_t ret;
        </toggle>
        <toggle name="if-has-output">
        struct sbus_method_in_${input-signature}_out_${output-signature}_state *state;
        state = tevent_req_data(req, struct sbus_method_in_${input-signature}_out_${output-signature}_state);

        </toggle>
        TEVENT_REQ_RETURN_ON_ERROR(req);

        <toggle name="if-output-arguments">
        <loop name="out-static">
        *_arg${index} = state->out->arg${index};
        </loop>
        <loop name="out-talloc">
        *_arg${index} = talloc_steal(mem_ctx, state->out->arg${index});
        </loop>

        </toggle>
        <toggle name="if-raw-output">
        /* Bounded reference cannot be unreferenced with dbus_message_unref.
         * For that reason we do not allow NULL memory context as it would
         * result in leaking the message memory. */
        if (mem_ctx == NULL) {
            return EINVAL;
        }

        ret = sbus_message_bound_steal(mem_ctx, state->reply);
        if (ret != EOK) {
            DEBUG(SSSDBG_CRIT_FAILURE, "Unable to steal message [%d]: %s\n",
                  ret, sss_strerror(ret));
            return ret;
        }

        *_reply = state->reply;

        </toggle>
        return EOK;
    }

</template>

<template name="method-caller">
    struct tevent_req *
    sbus_call_${token}_send
        (TALLOC_CTX *mem_ctx,
         struct sbus_connection *conn,
         <toggle name="if-raw-input">
         DBusMessage *raw_message)
         <or>
         const char *busname,
         const char *object_path<loop name="in">,
         ${type} arg_${name}</loop>)
         </toggle>
    {
        <toggle name="if-raw-input">
        return sbus_method_in_${input-signature}_out_${output-signature}_send(mem_ctx, conn, raw_message);
        <or>
        return sbus_method_in_${input-signature}_out_${output-signature}_send(mem_ctx, conn, ${keygen},
            busname, object_path, "${iface}", "${method}"<loop name="in">, arg_${name}</loop>);
        </toggle>
    }

    errno_t
    sbus_call_${token}_recv
        (<toggle name="if-use-talloc">TALLOC_CTX *mem_ctx,
         struct tevent_req *req<or>struct tevent_req *req</toggle><toggle name="if-raw-output">,
         DBusMessage **_reply)
         <or><loop name="out">,
         ${type} _${name}</loop>)
         </toggle>
    {
        return sbus_method_in_${input-signature}_out_${output-signature}_recv(<toggle name="if-use-talloc">mem_ctx, </toggle>req<toggle line name="if-raw-output">, _reply<or><loop name="out">, _${name}</loop></toggle>);
    }

</template>

<template name="signal-invoker">
    static void
    sbus_emit_signal_${input-signature}
        (struct sbus_connection *conn,
        <toggle name="if-raw-input">
         DBusMessage *raw_message)
         <or>
         const char *path,
         const char *iface,
         const char *signal_name<loop name="in">,
         ${type} arg${index}</loop>)
         </toggle>
    {
        <toggle name="if-input-arguments">
        struct _sbus_invoker_args_${input-signature} args;

        <loop name="in">
        args.arg${index} = arg${index};
        </loop>

        </toggle>
        <toggle name="if-raw-input">
        sbus_call_signal_send(conn, raw_message, NULL,
                              dbus_message_get_path(raw_message),
                              dbus_message_get_interface(raw_message),
                              dbus_message_get_member(raw_message), NULL);
        </toggle>
        <toggle name="if-input-arguments">
        sbus_call_signal_send(conn, NULL, (sbus_invoker_writer_fn)_sbus_invoker_write_${input-signature},
                              path, iface, signal_name, &args);
        </toggle>
        <toggle name="if-empty-input">
        sbus_call_signal_send(conn, NULL, NULL, path, iface, signal_name, NULL);
        </toggle>
    }

</template>

<template name="signal-caller">
    void
    sbus_emit_${token}
        (struct sbus_connection *conn,
         <toggle name="if-raw-input">
         DBusMessage *raw_message)
         <or>
         const char *object_path<loop name="in">,
         ${type} arg_${name}</loop>)
         </toggle>
    {
        <toggle name="if-raw-input">
        sbus_emit_signal_${input-signature}(conn, raw_message);
        <or>
        sbus_emit_signal_${input-signature}(conn, object_path,
            "${iface}", "${signal}"<loop name="in">, arg_${name}</loop>);
        </toggle>
    }

</template>

<template name="get-invoker">
    struct sbus_get_${output-signature}_state {
        struct _sbus_invoker_args_${output-signature} *out;
    };

    static void sbus_get_${output-signature}_done(struct tevent_req *subreq);

    static struct tevent_req *
    sbus_get_${output-signature}_send
        (TALLOC_CTX *mem_ctx,
         struct sbus_connection *conn,
         const char *busname,
         const char *object_path,
         const char *interface,
         const char *property)
    {
        struct sbus_get_${output-signature}_state *state;
        struct tevent_req *subreq;
        struct tevent_req *req;
        errno_t ret;

        req = tevent_req_create(mem_ctx, &state, struct sbus_get_${output-signature}_state);
        if (req == NULL) {
            DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
            return NULL;
        }

        state->out = talloc_zero(state, struct _sbus_invoker_args_${output-signature});
        if (state->out == NULL) {
            DEBUG(SSSDBG_CRIT_FAILURE,
                  "Unable to allocate space for output parameters!\n");
            ret = ENOMEM;
            goto done;
        }

        subreq = sbus_call_DBusProperties_Get_send(mem_ctx, conn,
                    busname, object_path, interface, property);
        if (subreq == NULL) {
            DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
            ret = ENOMEM;
            goto done;
        }

        tevent_req_set_callback(subreq, sbus_get_${output-signature}_done, req);

        ret = EAGAIN;

    done:
        if (ret == EOK) {
            tevent_req_done(req);
            tevent_req_post(req, conn->ev);
        } else if (ret != EAGAIN) {
            tevent_req_error(req, ret);
            tevent_req_post(req, conn->ev);
        }

        return req;
    }

    static void sbus_get_${output-signature}_done(struct tevent_req *subreq)
    {
        struct sbus_get_${output-signature}_state *state;
        sbus_value_reader_fn reader;
        sbus_value_reader_talloc_fn reader_talloc;
        struct tevent_req *req;
        DBusMessage *reply;
        errno_t ret;

        req = tevent_req_callback_data(subreq, struct tevent_req);
        state = tevent_req_data(req, struct sbus_get_${output-signature}_state);

        ret = sbus_call_DBusProperties_Get_recv(state, subreq, &reply);
        talloc_zfree(subreq);
        if (ret != EOK) {
            tevent_req_error(req, ret);
            return;
        }

        reader = <toggle line name="if-use-talloc">NULL<or>(sbus_value_reader_fn)sbus_iterator_read_${output-signature}</toggle>;
        reader_talloc = <toggle line name="if-use-talloc">(sbus_value_reader_talloc_fn)sbus_iterator_read_${output-signature}<or>NULL</toggle>;
        ret = sbus_parse_get_message(state->out, reader, reader_talloc, reply, &state->out->arg0);
        if (ret != EOK) {
            tevent_req_error(req, ret);
            return;
        }

        tevent_req_done(req);
        return;
    }

    static errno_t
    sbus_get_${output-signature}_recv
        (<toggle name="if-use-talloc">TALLOC_CTX *mem_ctx,
         struct tevent_req *req,
         <or>struct tevent_req *req,
         </toggle>
         ${output-type} _value)
    {
        struct sbus_get_${output-signature}_state *state;
        state = tevent_req_data(req, struct sbus_get_${output-signature}_state);

        TEVENT_REQ_RETURN_ON_ERROR(req);

        *_value = <toggle line name="if-use-talloc">talloc_steal(mem_ctx, state->out->arg0);<or>state->out->arg0</toggle>;

        return EOK;
    }

</template>

<template name="set-invoker">
    struct sbus_set_${input-signature}_state {
        struct _sbus_invoker_args_${input-signature} in;
    };

    static void sbus_set_${input-signature}_done(struct tevent_req *subreq);

    static struct tevent_req *
    sbus_set_${input-signature}_send
        (TALLOC_CTX *mem_ctx,
         struct sbus_connection *conn,
         const char *busname,
         const char *object_path,
         const char *interface,
         const char *property,
         ${input-type} value)
    {
        struct sbus_set_${input-signature}_state *state;
        struct tevent_req *subreq;
        struct tevent_req *req;
        DBusMessage *raw_message;
        errno_t ret;

        req = tevent_req_create(mem_ctx, &state, struct sbus_set_${input-signature}_state);
        if (req == NULL) {
            DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
            return NULL;
        }

        state->in.arg0 = value;

        raw_message = sbus_create_set_call(state,
                          (sbus_invoker_writer_fn)_sbus_invoker_write_${input-signature},
                          busname, object_path, interface, property,
                          "${dbus-type}", &state->in);
        if (raw_message == NULL) {
            ret = ENOMEM;
            goto done;
        }

        subreq = sbus_call_DBusProperties_Set_send(mem_ctx, conn, raw_message);
        if (subreq == NULL) {
            DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
            ret = ENOMEM;
            goto done;
        }

        tevent_req_set_callback(subreq, sbus_set_${input-signature}_done, req);

        ret = EAGAIN;

    done:
        if (ret == EOK) {
            tevent_req_done(req);
            tevent_req_post(req, conn->ev);
        } else if (ret != EAGAIN) {
            tevent_req_error(req, ret);
            tevent_req_post(req, conn->ev);
        }

        return req;
    }

    static void sbus_set_${input-signature}_done(struct tevent_req *subreq)
    {
        struct tevent_req *req;
        errno_t ret;

        req = tevent_req_callback_data(subreq, struct tevent_req);

        ret = sbus_call_DBusProperties_Set_recv(subreq);
        talloc_zfree(subreq);
        if (ret != EOK) {
            tevent_req_error(req, ret);
            return;
        }

        tevent_req_done(req);
        return;
    }

    static errno_t
    sbus_set_${input-signature}_recv
        (struct tevent_req *req)
    {
        TEVENT_REQ_RETURN_ON_ERROR(req);

        return EOK;
    }

</template>

<template name="property-caller">
    <toggle name="get-static">
    struct tevent_req *
    sbus_get_${token}_send
        (TALLOC_CTX *mem_ctx,
         struct sbus_connection *conn,
         const char *busname,
         const char *object_path)
    {
        return sbus_get_${output-signature}_send(mem_ctx,
                    conn, busname, object_path,
                    "${iface}", "${property}");
    }

    errno_t sbus_get_${token}_recv
        (struct tevent_req *req,
         ${output-type} _value)
    {
        return sbus_get_${output-signature}_recv(req, _value);
    }

    </toggle>
    <toggle name="get-talloc">
    struct tevent_req *
    sbus_get_${token}_send
        (TALLOC_CTX *mem_ctx,
         struct sbus_connection *conn,
         const char *busname,
         const char *object_path)
    {
        return sbus_get_${output-signature}_send(mem_ctx,
                    conn, busname, object_path,
                    "${iface}", "${property}");
    }

    errno_t sbus_get_${token}_recv
        (TALLOC_CTX *mem_ctx,
         struct tevent_req *req,
         ${output-type} _value)
    {
        return sbus_get_${output-signature}_recv(mem_ctx, req, _value);
    }

    </toggle>
    <toggle name="set">
    struct tevent_req *
    sbus_set_${token}_send
        (TALLOC_CTX *mem_ctx,
         struct sbus_connection *conn,
         const char *busname,
         const char *object_path,
         ${input-type} value)
    {
        return sbus_set_${input-signature}_send(mem_ctx,
                   conn, busname, object_path,
                   "${iface}", "${property}", value);
    }

    errno_t sbus_set_${token}_recv
        (struct tevent_req *req)
    {
        return sbus_set_${input-signature}_recv(req);
    }

    </toggle>
</template>

<template name="getall-caller">
    struct sbus_getall_${token}_state {
        struct sbus_all_${token} *properties;
    };

    static void sbus_getall_${token}_done(struct tevent_req *subreq);

    struct tevent_req *
    sbus_getall_${token}_send
        (TALLOC_CTX *mem_ctx,
         struct sbus_connection *conn,
         const char *busname,
         const char *object_path)
    {
        struct sbus_getall_${token}_state *state;
        struct tevent_req *subreq;
        struct tevent_req *req;
        errno_t ret;

        req = tevent_req_create(mem_ctx, &state, struct sbus_getall_${token}_state);
        if (req == NULL) {
            DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
            return NULL;
        }

        state->properties = talloc_zero(state, struct sbus_all_${token});
        if (state->properties == NULL) {
            ret = ENOMEM;
            goto done;
        }

        subreq = sbus_call_DBusProperties_GetAll_send(mem_ctx, conn,
                    busname, object_path, "${iface}");
        if (subreq == NULL) {
            DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
            ret = ENOMEM;
            goto done;
        }

        tevent_req_set_callback(subreq, sbus_getall_${token}_done, req);

        ret = EAGAIN;

    done:
        if (ret == EOK) {
            tevent_req_done(req);
            tevent_req_post(req, conn->ev);
        } else if (ret != EAGAIN) {
            tevent_req_error(req, ret);
            tevent_req_post(req, conn->ev);
        }

        return req;
    }

    static void sbus_getall_${token}_done(struct tevent_req *subreq)
    {
        struct sbus_getall_${token}_state *state;
        struct tevent_req *req;
        DBusMessage *reply;
        errno_t ret;

        req = tevent_req_callback_data(subreq, struct tevent_req);
        state = tevent_req_data(req, struct sbus_getall_${token}_state);

        ret = sbus_call_DBusProperties_GetAll_recv(state, subreq, &reply);
        talloc_zfree(subreq);
        if (ret != EOK) {
            tevent_req_error(req, ret);
            return;
        }

        struct sbus_parse_getall_table table[] = {
            <loop name="property-static">
            {"${name}", (sbus_value_reader_fn)sbus_iterator_read_${output-signature}, NULL,
             &state->properties->${name}.value, &state->properties->${name}.is_set},
            </loop>
            <loop name="property-talloc">
            {"${name}", NULL, (sbus_value_reader_talloc_fn)sbus_iterator_read_${output-signature},
             &state->properties->${name}.value, &state->properties->${name}.is_set},
            </loop>
            {NULL, NULL, NULL, NULL, NULL}
        };

        ret = sbus_parse_getall_message(state, table, reply);
        if (ret != EOK) {
            tevent_req_error(req, ret);
            return;
        }

        tevent_req_done(req);
        return;
    }

    errno_t sbus_getall_${token}_recv
        (TALLOC_CTX *mem_ctx,
         struct tevent_req *req,
         struct sbus_all_${token} **_properties)
    {
        struct sbus_getall_${token}_state *state;
        state = tevent_req_data(req, struct sbus_getall_${token}_state);

        TEVENT_REQ_RETURN_ON_ERROR(req);

        *_properties = talloc_steal(mem_ctx, state->properties);

        return EOK;
    }

</template>
