/*
 *                         Vortex OpenSplice
 *
 *   This software and documentation are Copyright 2006 to TO_YEAR ADLINK
 *   Technology Limited, its affiliated companies and licensors. All rights
 *   reserved.
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 *
 */

#include "dds_dcps.h"
#include "sac_common.h"
#include "sac_objManag.h"
#include "sac_object.h"
#include "sac_entity.h"
#include "sac_dataWriter.h"
#include "sac_typeSupport.h"
#include "sac_topicDescription.h"
#include "dds_builtinTopicsSplDcps.h"
#include "sac_domainParticipant.h"
#include "sac_genericCopyIn.h"
#include "u_writer.h"
#include "v_status.h"
#include "sac_report.h"
#include "sac_genericCopyOut.h"

#define DDS_DataWriterClaim(_this, writer) \
        DDS_Object_claim(DDS_Object(_this), DDS_DATAWRITER, (_Object *)writer)

#define DDS_DataWriterClaimRead(_this, writer) \
        DDS_Object_claim(DDS_Object(_this), DDS_DATAWRITER, (_Object *)writer)

#define DDS_DataWriterRelease(_this) \
        DDS_Object_release(DDS_Object(_this))

#define DDS_DataWriterCheck(_this, writer) \
        DDS_Object_check_and_assign(DDS_Object(_this), DDS_DATAWRITER, (_Object *)writer)

#define _DataWriter_get_user_entity(_this) \
        u_writer(_Entity_get_user_entity(_Entity(_DataWriter(_this))))

#define WRITER_STATUS_MASK (DDS_LIVELINESS_LOST_STATUS            |\
                            DDS_OFFERED_DEADLINE_MISSED_STATUS    |\
                            DDS_OFFERED_INCOMPATIBLE_QOS_STATUS   |\
                            DDS_PUBLICATION_MATCHED_STATUS)

typedef struct writerInfo_s {
    _DataWriter writer;
    void *data;
} writerInfo;


static v_copyin_result
_DataWriterCopy (
    c_type type,
    const void *data,
    void *to)
{
    v_copyin_result result;
    c_base base = c_getBase(c_object(type));
    writerInfo *info = (writerInfo *)data;

    if (info->writer->copy_cache) {
        C_STRUCT(DDS_srcInfo) dataInfo;
        dataInfo.copyProgram = info->writer->copy_cache;
        dataInfo.src = info->data;
        result = info->writer->copy_in (base, &dataInfo, to);
    } else {
        result = info->writer->copy_in (base, info->data, to);
    }

    return result;
}

static DDS_ReturnCode_t
_DataWriter_deinit (
    _Object _this)
{
    DDS_ReturnCode_t result;
    _DataWriter w;

    w = _DataWriter(_this);
    if (w == NULL) {
        result = DDS_RETCODE_BAD_PARAMETER;
        SAC_REPORT(result, "DataWriter = NULL");
    } else {
        DDS_Entity_set_listener_interest(DDS_Entity(_this), 0);
        DDS_Entity_disable_callbacks(DDS_Entity(_this));
        DDS_TopicDescription_free(DDS_TopicDescription(w->topic));
        result = _Entity_deinit(_this);
    }
    return result;
}

DDS_ReturnCode_t
DDS_DataWriterNew (
    u_writer uWriter,
    const DDS_Publisher publisher,
    const DDS_Topic topic,
    DDS_DataWriter *writer)
{
    _DataWriter _this;
    DDS_ReturnCode_t result = DDS_RETCODE_OK;
    DDS_TypeSupport typeSupport;

    if (uWriter == NULL) {
        result = DDS_RETCODE_BAD_PARAMETER;
        SAC_REPORT(result, "uWriter = NULL");
    }
    if (publisher == NULL) {
        result = DDS_RETCODE_BAD_PARAMETER;
        SAC_REPORT(result, "Publisher = NULL");
    }
    if (topic == NULL) {
        result = DDS_RETCODE_BAD_PARAMETER;
        SAC_REPORT(result, "Topic = NULL");
    }
    if (writer == NULL) {
        result = DDS_RETCODE_BAD_PARAMETER;
        SAC_REPORT(result, "DataWriter holder = NULL");
    }
    if (result == DDS_RETCODE_OK) {
        result = DDS_TopicDescription_get_typeSupport(topic, &typeSupport);
    }
    if (result == DDS_RETCODE_OK) {
        result = DDS_Object_new(DDS_DATAWRITER, _DataWriter_deinit, (_Object *)&_this);
    }
    if (result == DDS_RETCODE_OK) {
        result = DDS_Entity_init(_this, u_entity(uWriter));
        DDS_Object_set_domain_id(_Object(_this), DDS_Object_get_domain_id(publisher));
    }
    if (result == DDS_RETCODE_OK) {
        _this->publisher = publisher;
        _this->topic      = DDS_Topic(DDS_TopicDescription_keep(DDS_TopicDescription(topic)));
        _this->copy_in    = DDS_TypeSupportCopyIn(typeSupport);
        _this->copy_out   = DDS_TypeSupportCopyOut(typeSupport);
        _this->copy_cache = DDS_TypeSupportCopyCache(typeSupport);
        /* TODO copy_action = DDS_TypeSupportGetWriterCopy(typeSupport);*/

        *writer = (DDS_DataWriter)_this;
    }
    return result;
}

DDS_ReturnCode_t
DDS_DataWriterFree (
    DDS_DataWriter _this)
{
    DDS_ReturnCode_t result;
    result = DDS__free(_this);
    return result;
}

/* ReturnCode_t
 * set_qos(
 *      in DataWriterQos qos);
 */
DDS_ReturnCode_t
DDS_DataWriter_set_qos (
    DDS_DataWriter _this,
    const DDS_DataWriterQos *qos)
{
    DDS_ReturnCode_t result = DDS_RETCODE_OK;
    _DataWriter w;
    u_writerQos wQos;
    u_result uResult;
    DDS_DataWriterQos writerQos;
    DDS_TopicQos topicQos;

    SAC_REPORT_STACK();

    memset(&writerQos, 0, sizeof(DDS_DataWriterQos));
    (void)DDS_DataWriterQos_init(&writerQos, DDS_DATAWRITER_QOS_DEFAULT);

    result = DDS_DataWriterQos_is_consistent(qos);
    if (result == DDS_RETCODE_OK) {
        result = DDS_DataWriterClaim(_this, &w);
    }
    if (result == DDS_RETCODE_OK) {
        if (qos == DDS_DATAWRITER_QOS_DEFAULT ||
            qos == DDS_DATAWRITER_QOS_USE_TOPIC_QOS)
        {
            result = DDS_Publisher_get_default_datawriter_qos(
                w->publisher, &writerQos);
            qos = &writerQos;
        }
        if (qos == DDS_DATAWRITER_QOS_USE_TOPIC_QOS) {
            memset(&topicQos, 0, sizeof(DDS_TopicQos));
            (void)DDS_TopicQos_init(&topicQos, DDS_TOPIC_QOS_DEFAULT);
            result = DDS_Topic_get_qos(w->topic, &topicQos);
            if (result == DDS_RETCODE_OK) {
                result = DDS_Publisher_copy_from_topic_qos(
                    w->publisher, &writerQos, &topicQos);
            }
            if (result == DDS_RETCODE_OK) {
                result = DDS_DataWriterQos_is_consistent(&writerQos);
            }
            (void)DDS_TopicQos_deinit(&topicQos);
        }
        if (result == DDS_RETCODE_OK) {
            wQos = DDS_DataWriterQos_copyIn(qos);
            if (wQos == NULL) {
                result = DDS_RETCODE_OUT_OF_RESOURCES;
                SAC_REPORT(result, "Failed to copy DDS_DataWriterQos");
            }
        }
        if (result == DDS_RETCODE_OK) {
            uResult = u_writerSetQos(_DataWriter_get_user_entity(w), wQos);
            result = DDS_ReturnCode_get(uResult);
            u_writerQosFree(wQos);
        }
        DDS_DataWriterRelease(_this);
    }

    (void)DDS_DataWriterQos_deinit(&writerQos);

    SAC_REPORT_FLUSH(_this, result != DDS_RETCODE_OK);
    return result;
}

/* ReturnCode_t
 * get_qos(
 *      inout DataWriterQos qos);
 */
DDS_ReturnCode_t
DDS_DataWriter_get_qos (
    DDS_DataWriter _this,
    DDS_DataWriterQos *qos)
{
    DDS_ReturnCode_t result;
    _DataWriter w;
    u_writerQos uQos;
    u_result uResult;

    SAC_REPORT_STACK();

    result = DDS_DataWriterCheck(_this, &w);
    if (result == DDS_RETCODE_OK) {
        if (qos == NULL) {
            result = DDS_RETCODE_BAD_PARAMETER;
            SAC_REPORT(result, "DataWriterQos = NULL");
        } else if (qos == DDS_DATAWRITER_QOS_DEFAULT) {
            result = DDS_RETCODE_BAD_PARAMETER;
            SAC_REPORT(result, "QoS 'DATAWRITER_QOS_DEFAULT' is read-only.");
        } else if (qos == DDS_DATAWRITER_QOS_USE_TOPIC_QOS) {
            result = DDS_RETCODE_BAD_PARAMETER;
            SAC_REPORT(result, "QoS 'DATAWRITER_QOS_USE_TOPIC_QOS' is read-only.");
        }
    }
    if (result == DDS_RETCODE_OK) {
        uResult = u_writerGetQos(_DataWriter_get_user_entity(w), &uQos);
        if (uResult == U_RESULT_OK) {
            result = DDS_DataWriterQos_copyOut(uQos, qos);
            u_writerQosFree(uQos);
        } else {
            result = DDS_ReturnCode_get(uResult);
        }
    }
    SAC_REPORT_FLUSH(_this, result != DDS_RETCODE_OK);
    return result;
}

/* ReturnCode_t
 * set_listener(
 *      in DataWriterListener a_listener,
 *      in StatusKindMask mask);
 */
DDS_ReturnCode_t
DDS_DataWriter_set_listener (
    DDS_DataWriter _this,
    const struct DDS_DataWriterListener *a_listener,
    const DDS_StatusMask mask)
{
    DDS_ReturnCode_t result;
    _DataWriter w;

    SAC_REPORT_STACK();

    result = DDS_DataWriterClaim(_this, &w);
    if (result == DDS_RETCODE_OK) {
        if (a_listener != NULL) {
            w->listener = *a_listener;
            result = DDS_Entity_set_listener_interest(DDS_Entity(w), mask);
        } else {
            memset(&w->listener, 0, sizeof(struct DDS_DataWriterListener));
            result = DDS_Entity_set_listener_interest(DDS_Entity(w), mask);
        }
        DDS_DataWriterRelease(_this);
    }
    SAC_REPORT_FLUSH(_this, result != DDS_RETCODE_OK);
    return result;
}

/* DataWriterListener
 * get_listener();
 */
struct DDS_DataWriterListener
DDS_DataWriter_get_listener (
    DDS_DataWriter _this)
{
    DDS_ReturnCode_t result;
    _DataWriter w;
    struct DDS_DataWriterListener *listener, noListener;

    SAC_REPORT_STACK();

    result = DDS_DataWriterCheck(_this, &w);
    if (result == DDS_RETCODE_OK) {
        listener = &w->listener;
    } else {
        memset(&noListener, 0, sizeof(struct DDS_DataWriterListener));
        listener = &noListener;
    }
    SAC_REPORT_FLUSH(_this, result != DDS_RETCODE_OK);
    return *listener;
}

/* ReturnCode_t
 * set_listener_mask(
 *      in StatusKindMask mask);
 */
DDS_ReturnCode_t
DDS_DataWriter_set_listener_mask (
    _DataWriter _this,
    const DDS_StatusMask mask)
{
    DDS_ReturnCode_t result;

    assert(_this);

    result = DDS_Entity_set_listener_interest(DDS_Entity(_this), mask);

    return result;
}


/* Topic
 * get_topic();
 */
DDS_Topic
DDS_DataWriter_get_topic (
    DDS_DataWriter _this)
{
    DDS_ReturnCode_t result;
    _DataWriter w;
    DDS_Topic topic = NULL;

    SAC_REPORT_STACK();

    result = DDS_DataWriterCheck(_this, &w);
    if (result == DDS_RETCODE_OK) {
        topic = w->topic;
    }
    SAC_REPORT_FLUSH(_this, result != DDS_RETCODE_OK);
    return topic;
}

/* Publisher
 * get_publisher();
 */
DDS_Publisher
DDS_DataWriter_get_publisher (
    DDS_DataWriter _this)
{
    DDS_ReturnCode_t result;
    _DataWriter w;
    DDS_Publisher publisher = NULL;

    SAC_REPORT_STACK();

    result = DDS_DataWriterCheck(_this, &w);
    if (result == DDS_RETCODE_OK) {
        publisher = w->publisher;
    }
    SAC_REPORT_FLUSH(_this, result != DDS_RETCODE_OK);
    return publisher;
}

/* ReturnCode_t
 * wait_for_acknowledgments(
 *      in Duration_t max_wait);
 */
DDS_ReturnCode_t
DDS_DataWriter_wait_for_acknowledgments (
    DDS_DataWriter _this,
    const DDS_Duration_t *max_wait)
{
    DDS_ReturnCode_t result;
    _DataWriter w;
    os_duration timeout;
    u_result uResult;

    SAC_REPORT_STACK();

    result = DDS_DataWriterCheck(_this, &w);
    if (result == DDS_RETCODE_OK) {
        result = DDS_Duration_copyIn(max_wait, &timeout);
    }
    if (result == DDS_RETCODE_OK) {
        uResult = u_writerWaitForAcknowledgments(_DataWriter_get_user_entity(w), timeout);
        result = DDS_ReturnCode_get(uResult);
    }
    SAC_REPORT_FLUSH(_this, (result != DDS_RETCODE_OK) && (result != DDS_RETCODE_TIMEOUT));
    return result;
}

static v_result
copy_liveliness_lost_status(
    c_voidp info,
    c_voidp arg)
{
    struct v_livelinessLostInfo *from;
    DDS_LivelinessLostStatus *to;

    from = (struct v_livelinessLostInfo *)info;
    to = (DDS_LivelinessLostStatus *)arg;

    to->total_count = from->totalCount;
    to->total_count_change = from->totalChanged;
    return V_RESULT_OK;
}

/* Access the status
 * ReturnCode_t
 * get_liveliness_lost_status(
 *      inout LivelinessLostStatus a_status);
 */
DDS_ReturnCode_t
DDS_DataWriter_get_liveliness_lost_status (
    DDS_DataWriter _this,
    DDS_LivelinessLostStatus *status)
{
    DDS_ReturnCode_t result;
    _DataWriter w;
    u_result uResult;

    SAC_REPORT_STACK();

    if (status != NULL) {
        result = DDS_DataWriterCheck(_this, &w);
        if (result == DDS_RETCODE_OK) {
            uResult = u_writerGetLivelinessLostStatus(
                          _DataWriter_get_user_entity(w),
                          TRUE,
                          copy_liveliness_lost_status,
                          status);
            result = DDS_ReturnCode_get(uResult);
        }
    } else {
        result = DDS_RETCODE_BAD_PARAMETER;
        SAC_REPORT(result, "LivelinessLostStatus holder = NULL");
    }
    SAC_REPORT_FLUSH(_this, result != DDS_RETCODE_OK);
    return result;
}

static v_result
copy_deadline_missed_status(
    c_voidp info,
    c_voidp arg)
{
    struct v_deadlineMissedInfo *from;
    DDS_OfferedDeadlineMissedStatus *to;
    v_result result;
    v_object instance;

    result = V_RESULT_INTERNAL_ERROR;
    from = (struct v_deadlineMissedInfo *)info;
    to = (DDS_OfferedDeadlineMissedStatus *)arg;

    to->total_count = from->totalCount;
    to->total_count_change = from->totalChanged;

    if (!v_handleIsNil(from->instanceHandle)) {
        if (v_handleClaim(from->instanceHandle, &instance) == V_HANDLE_OK) {
            to->last_instance_handle = u_instanceHandleNew(v_public(instance));
            if (v_handleRelease(from->instanceHandle) == V_HANDLE_OK) {
                result = V_RESULT_OK;
            }
        }
    } else {
        result = V_RESULT_OK;
    }
    return result;
}

/* Access the status
 * ReturnCode_t
 * get_offered_deadline_missed_status(
 *      inout OfferedDeadlineMissedStatus a_status);
 */
DDS_ReturnCode_t
DDS_DataWriter_get_offered_deadline_missed_status (
    DDS_DataWriter _this,
    DDS_OfferedDeadlineMissedStatus *status)
{
    DDS_ReturnCode_t result;
    _DataWriter w;
    u_result uResult;

    SAC_REPORT_STACK();

    if (status != NULL) {
        result = DDS_DataWriterCheck(_this, &w);
        if (result == DDS_RETCODE_OK) {
            uResult = u_writerGetDeadlineMissedStatus(
                          _DataWriter_get_user_entity(w),
                          TRUE,
                          copy_deadline_missed_status,
                          status);
            result = DDS_ReturnCode_get(uResult);
        }
    } else {
        result = DDS_RETCODE_BAD_PARAMETER;
        SAC_REPORT(result, "OfferedDeadlineMissedStatus holder = NULL");
    }
    SAC_REPORT_FLUSH(_this, result != DDS_RETCODE_OK);
    return result;
}

static v_result
copy_IncompatibleQosStatus(
    c_voidp info,
    c_voidp arg)
{
    DDS_unsigned_long i, j, len;
    v_result result = V_RESULT_OK;
    struct v_incompatibleQosInfo *from;
    DDS_OfferedIncompatibleQosStatus *to;

    from = (struct v_incompatibleQosInfo *)info;
    to = (DDS_OfferedIncompatibleQosStatus *)arg;

    to->total_count = from->totalCount;
    to->total_count_change = from->totalChanged;
    to->last_policy_id = from->lastPolicyId;

    len = 0;
    for (i=0; i<V_POLICY_ID_COUNT; i++) {
        if (from->policyCount[i] > 0) len++;
    }
    if ((to->policies._buffer != NULL) && (len > to->policies._maximum)) {
        DDS_free(to->policies._buffer);
        to->policies._buffer = NULL;
    }
    if ((to->policies._buffer == NULL) && (len > 0)) {
        to->policies._buffer = DDS_QosPolicyCountSeq_allocbuf(len);
        if (to->policies._buffer == NULL) {
            result = V_RESULT_OUT_OF_RESOURCES;
        } else {
            to->policies._maximum = len;
        }
    }
    if ( to->policies._buffer ) {
        to->policies._length = len;
        j=0;
        for ( i = 0; i < V_POLICY_ID_COUNT; i++ ) {
            if (from->policyCount[i] > 0) {
                to->policies._buffer[j].policy_id = (DDS_QosPolicyId_t) i;
                to->policies._buffer[j++].count = from->policyCount[i];
            }
        }
    } else {
        to->policies._maximum = 0;
        to->policies._length = 0;
    }
    return result;
}

/* Access the status
 * ReturnCode_t
 * get_offered_incompatible_qos_status(
 *      inout OfferedIncompatibleQosStatus a_status);
 */
DDS_ReturnCode_t
DDS_DataWriter_get_offered_incompatible_qos_status (
    DDS_DataWriter _this,
    DDS_OfferedIncompatibleQosStatus *status)
{
    DDS_ReturnCode_t result;
    _DataWriter w;
    u_result uResult;

    SAC_REPORT_STACK();

    if (status != NULL) {
        result = DDS_DataWriterCheck(_this, &w);
        if (result == DDS_RETCODE_OK) {
            uResult = u_writerGetIncompatibleQosStatus(
                          _DataWriter_get_user_entity(w),
                          TRUE,
                          copy_IncompatibleQosStatus,
                          status);
            result = DDS_ReturnCode_get(uResult);
        }
    } else {
        result = DDS_RETCODE_BAD_PARAMETER;
        SAC_REPORT(result, "OfferedIncompatibleQosStatus holder = NULL");
    }
    SAC_REPORT_FLUSH(_this, result != DDS_RETCODE_OK);
    return result;
}

static v_result
copy_publication_matched_status(
    c_voidp info,
    c_voidp arg)
{
    struct v_topicMatchInfo *from;
    DDS_PublicationMatchedStatus *to;

    from = (struct v_topicMatchInfo *)info;
    to = (DDS_PublicationMatchedStatus *)arg;

    to->total_count = from->totalCount;
    to->total_count_change = from->totalChanged;
    to->current_count = from->currentCount;
    to->current_count_change = from->currentChanged;
    to->last_subscription_handle = u_instanceHandleFromGID(from->instanceHandle);
    return V_RESULT_OK;
}

/* Access the status
 * ReturnCode_t
 * get_publication_matched_status(
 *      inout PublicationMatchedStatus a_status);
 */
DDS_ReturnCode_t
DDS_DataWriter_get_publication_matched_status (
    DDS_DataWriter _this,
    DDS_PublicationMatchedStatus *status)
{
    DDS_ReturnCode_t result;
    _DataWriter w;
    u_result uResult;

    SAC_REPORT_STACK();

    if (status != NULL) {
        result = DDS_DataWriterCheck(_this, &w);
        if (result == DDS_RETCODE_OK) {
            uResult = u_writerGetPublicationMatchStatus (
                          _DataWriter_get_user_entity(w),
                          TRUE,
                          copy_publication_matched_status,
                          status);
            result = DDS_ReturnCode_get(uResult);
        }
    } else {
        result = DDS_RETCODE_BAD_PARAMETER;
        SAC_REPORT(result, "PublicationMatchedStatus holder = NULL");
    }
    SAC_REPORT_FLUSH(_this, result != DDS_RETCODE_OK);
    return result;
}

/* ReturnCode_t
 * assert_liveliness();
 */
DDS_ReturnCode_t
DDS_DataWriter_assert_liveliness (
    DDS_DataWriter _this)
{
    DDS_ReturnCode_t result;
    _DataWriter w;
    u_result uResult;

    SAC_REPORT_STACK();

    result = DDS_DataWriterCheck(_this, &w);
    if (result == DDS_RETCODE_OK) {
        uResult = u_writerAssertLiveliness(_DataWriter_get_user_entity(w));
        result = DDS_ReturnCode_get(uResult);
    }
    SAC_REPORT_FLUSH(_this, result != DDS_RETCODE_OK);
    return result;
}

static v_result
copy_matched_subscription(
    u_subscriptionInfo *info,
    void *arg)
{
    DDS_InstanceHandleSeq *to;
    DDS_InstanceHandle_t *tmp_buffer;

    to = (DDS_InstanceHandleSeq *)arg;

    if (to->_maximum <= to->_length) {
        tmp_buffer = to->_buffer;
        to->_buffer = DDS_InstanceHandleSeq_allocbuf(to->_length + 10);
        to->_maximum = to->_length + 10;
        if (tmp_buffer) {
            memcpy(to->_buffer, tmp_buffer, to->_length * sizeof(*to->_buffer));
            DDS_free(tmp_buffer);
        }
    }
    to->_buffer[to->_length] = u_instanceHandleFromGID(info->key);
    ++to->_length;
    return V_RESULT_OK;
}

/* ReturnCode_t
 * get_matched_subscriptions(
 *      inout InstanceHandleSeq subscription_handles);
 */
DDS_ReturnCode_t
DDS_DataWriter_get_matched_subscriptions (
    DDS_DataWriter _this,
    DDS_InstanceHandleSeq *subscription_handles)
{
    DDS_ReturnCode_t result;
    _DataWriter w;
    u_result uResult;

    SAC_REPORT_STACK();

    result = DDS_DataWriterCheck(_this, &w);
    if (result == DDS_RETCODE_OK) {
        subscription_handles->_length = 0;
        uResult = u_writerGetMatchedSubscriptions(
                      _DataWriter_get_user_entity(w),
                      copy_matched_subscription,
                      subscription_handles);
        result = DDS_ReturnCode_get(uResult);
    }
    SAC_REPORT_FLUSH(_this, result != DDS_RETCODE_OK);
    return result;
}

/* TODO:
 * Temporary wrapper to fix the missing result value.
 * The preprocessor must be changes to generate copy operation with a result value.
 */
static v_result
___DDS_SubscriptionBuiltinTopicData__copyOut(
    u_subscriptionInfo *info,
    void *arg)
{
    __DDS_SubscriptionBuiltinTopicData__copyOut(info, arg);
    return V_RESULT_OK;
}

/* ReturnCode_t
 * get_matched_subscription_data(
 *      inout SubscriptionBuiltinTopicData subscription_data,
 *      in InstanceHandle_t subscription_handle);
 */
DDS_ReturnCode_t
DDS_DataWriter_get_matched_subscription_data (
    DDS_DataWriter _this,
    DDS_SubscriptionBuiltinTopicData *subscription_data,
    const DDS_InstanceHandle_t subscription_handle)
{
    DDS_ReturnCode_t result;
    _DataWriter w;
    u_result uResult;

    SAC_REPORT_STACK();

    result = DDS_DataWriterCheck(_this, &w);
    if (result == DDS_RETCODE_OK) {
        uResult = u_writerGetMatchedSubscriptionData(
                      _DataWriter_get_user_entity(w),
                      subscription_handle,
                      ___DDS_SubscriptionBuiltinTopicData__copyOut,
                      subscription_data);
        result = DDS_ReturnCode_get(uResult);
    }
    SAC_REPORT_FLUSH(_this, result != DDS_RETCODE_OK);
    return result;
}



/*
 * Typeless Foo API operations
 */

/* Alternative function for DDS_Time_copyIn, which can also handle
 * the proprietary DDS_TIMESTAMP_CURRENT.
 */
static DDS_ReturnCode_t
time_copyIn (
    const DDS_Time_t *from,
    os_timeW *to,
    os_int64 maxSupportedSeconds)
{
    DDS_ReturnCode_t result = DDS_RETCODE_OK;

    assert(to);

    if (from) {
        if ((from->sec == DDS_TIMESTAMP_CURRENT_SEC) &&
            (from->nanosec == DDS_TIMESTAMP_CURRENT_NSEC))
        {
            /* Proprietary time extension, used to indicate that current time
             * needs to be used. Kernel translates invalid to current.
             */
            *to = OS_TIMEW_INVALID;
        } else if (DDS_Time_is_valid(from, maxSupportedSeconds)) {
            result = DDS_Time_copyIn(from, to, maxSupportedSeconds);
        } else {
            result = DDS_RETCODE_BAD_PARAMETER;
        }
    } else {
        result = DDS_RETCODE_BAD_PARAMETER;
        SAC_REPORT(result, "Time = NULL");
    }

    return result;
}

DDS_InstanceHandle_t
DDS_DataWriter_register_instance (
    DDS_DataWriter _this,
    const DDS_Sample instance_data)
{
    DDS_InstanceHandle_t handle;
    DDS_Time_t now = DDS_TIMESTAMP_CURRENT;

    SAC_REPORT_STACK();

    handle = DDS_DataWriter_register_instance_w_timestamp(_this, instance_data, &now);
    SAC_REPORT_FLUSH(_this, handle == DDS_HANDLE_NIL);
    return handle;
}

DDS_InstanceHandle_t
DDS_DataWriter_register_instance_w_timestamp (
    DDS_DataWriter _this,
    const DDS_Sample instance_data,
    const DDS_Time_t *source_timestamp)
{
    DDS_ReturnCode_t result;
    DDS_InstanceHandle_t handle = DDS_HANDLE_NIL;
    writerInfo data;
    _DataWriter w;
    u_result uResult;
    os_timeW timestamp;

    SAC_REPORT_STACK();

    if (instance_data != NULL) {
        result = DDS_DataWriterCheck(_this, &w);
        if (result == DDS_RETCODE_OK) {
            result = time_copyIn(source_timestamp, &timestamp, SAC_ENTITY_MAX_SUPPORTED_SECONDS(_this));
            if (result == DDS_RETCODE_OK) {
                data.writer = _this;
                data.data = (void *)instance_data;
                uResult = u_writerRegisterInstance(
                                  _DataWriter_get_user_entity(w),
                                  _DataWriterCopy,
                                  (void *)&data,
                                  timestamp,
                                  (u_instanceHandle *) &handle);
                result = DDS_ReturnCode_get(uResult);
            }
        }
    } else {
        result = DDS_RETCODE_BAD_PARAMETER;
        SAC_REPORT(result, "Sample = NULL");
    }
    SAC_REPORT_FLUSH(_this, (result != DDS_RETCODE_OK) && (result != DDS_RETCODE_TIMEOUT));
    return handle;
}

DDS_ReturnCode_t
DDS_DataWriter_unregister_instance (
    DDS_DataWriter _this,
    const DDS_Sample instance_data,
    const DDS_InstanceHandle_t handle)
{
    DDS_ReturnCode_t result;
    DDS_Time_t now = DDS_TIMESTAMP_CURRENT;

    SAC_REPORT_STACK();

    result = DDS_DataWriter_unregister_instance_w_timestamp(_this, instance_data, handle, &now);
    SAC_REPORT_FLUSH(_this, (result != DDS_RETCODE_OK) && (result != DDS_RETCODE_TIMEOUT));
    return result;
}

DDS_ReturnCode_t
DDS_DataWriter_unregister_instance_w_timestamp (
    DDS_DataWriter _this,
    const DDS_Sample instance_data,
    const DDS_InstanceHandle_t handle,
    const DDS_Time_t *source_timestamp)
{
    DDS_ReturnCode_t result;
    _DataWriter w;
    u_result uResult;
    writerInfo data, *_data;
    os_timeW timestamp;

    SAC_REPORT_STACK();

    result = DDS_DataWriterCheck(_this, &w);
    if (result == DDS_RETCODE_OK) {
        result = time_copyIn(source_timestamp, &timestamp, SAC_ENTITY_MAX_SUPPORTED_SECONDS(_this));
    }
    if (result == DDS_RETCODE_OK) {
        if (instance_data != NULL) {
            data.writer = _this;
            data.data = (void *)instance_data;
            _data = &data;
        } else {
            _data = NULL;
        }
        uResult = u_writerUnregisterInstance(
                              _DataWriter_get_user_entity(w),
                              _DataWriterCopy,
                              (void *)_data,
                              timestamp,
                              (u_instanceHandle)handle);
        result = DDS_ReturnCode_get(uResult);
    }
    SAC_REPORT_FLUSH(_this, (result != DDS_RETCODE_OK) && (result != DDS_RETCODE_TIMEOUT));
    return result;
}

DDS_ReturnCode_t
DDS_DataWriter_write (
    DDS_DataWriter _this,
    const DDS_Sample instance_data,
    const DDS_InstanceHandle_t handle)
{
    DDS_ReturnCode_t result;
    DDS_Time_t now = DDS_TIMESTAMP_CURRENT;

    SAC_REPORT_STACK();

    result = DDS_DataWriter_write_w_timestamp(_this, instance_data, handle, &now);
    SAC_REPORT_FLUSH(_this, (result != DDS_RETCODE_OK) && (result != DDS_RETCODE_TIMEOUT));
    return result;
}

DDS_ReturnCode_t
DDS_DataWriter_write_w_timestamp (
    DDS_DataWriter _this,
    const DDS_Sample instance_data,
    const DDS_InstanceHandle_t handle,
    const DDS_Time_t *source_timestamp)
{
    DDS_ReturnCode_t result;
    _DataWriter w;
    u_result uResult;
    os_timeW timestamp;
    writerInfo data;

    SAC_REPORT_STACK();

    if (instance_data != NULL) {
        result = DDS_DataWriterCheck(_this, &w);
        if (result == DDS_RETCODE_OK) {
            result = time_copyIn(source_timestamp, &timestamp, SAC_ENTITY_MAX_SUPPORTED_SECONDS(_this));
        }
        if (result == DDS_RETCODE_OK) {
            data.writer = _this;
            data.data = (void *)instance_data;
            uResult = u_writerWrite(_DataWriter_get_user_entity(w),
                                    _DataWriterCopy,
                                    (void *)&data,
                                    timestamp,
                                    (u_instanceHandle)handle);
            result = DDS_ReturnCode_get(uResult);
        }
    } else {
        result = DDS_RETCODE_BAD_PARAMETER;
        SAC_REPORT(result, "InstanceHandle = NULL");
    }
    SAC_REPORT_FLUSH(_this, (result != DDS_RETCODE_OK) && (result != DDS_RETCODE_TIMEOUT));
    return result;
}

DDS_ReturnCode_t
DDS_DataWriter_dispose (
    DDS_DataWriter _this,
    const DDS_Sample instance_data,
    const DDS_InstanceHandle_t instance_handle)
{
    DDS_ReturnCode_t result;
    DDS_Time_t now = DDS_TIMESTAMP_CURRENT;

    SAC_REPORT_STACK();

    result = DDS_DataWriter_dispose_w_timestamp(_this, instance_data, instance_handle, &now);
    SAC_REPORT_FLUSH(_this, (result != DDS_RETCODE_OK) && (result != DDS_RETCODE_TIMEOUT));
    return result;
}

DDS_ReturnCode_t
DDS_DataWriter_dispose_w_timestamp (
    DDS_DataWriter _this,
    const DDS_Sample instance_data,
    const DDS_InstanceHandle_t instance_handle,
    const DDS_Time_t *source_timestamp)
{
    DDS_ReturnCode_t result;
    _DataWriter w;
    u_result uResult;
    os_timeW timestamp;
    writerInfo data;

    SAC_REPORT_STACK();

    if (instance_data != NULL) {
        result = DDS_DataWriterCheck(_this, &w);
        if (result == DDS_RETCODE_OK) {
            result = time_copyIn(source_timestamp, &timestamp, SAC_ENTITY_MAX_SUPPORTED_SECONDS(_this));
        }
        if (result == DDS_RETCODE_OK) {
            data.writer = _this;
            data.data = (void *)instance_data;
            uResult = u_writerDispose(_DataWriter_get_user_entity(w),
                                      _DataWriterCopy,
                                      (void *)&data,
                                      timestamp,
                                      (u_instanceHandle)instance_handle);
            result = DDS_ReturnCode_get(uResult);
        }
    } else {
        result = DDS_RETCODE_BAD_PARAMETER;
        SAC_REPORT(result, "InstanceHandle = NULL");
    }
    SAC_REPORT_FLUSH(_this, (result != DDS_RETCODE_OK) && (result != DDS_RETCODE_TIMEOUT));
    return result;
}

DDS_ReturnCode_t
DDS_DataWriter_writedispose (
    DDS_DataWriter _this,
    const DDS_Sample instance_data,
    const DDS_InstanceHandle_t instance_handle)
{
    DDS_ReturnCode_t result;
    DDS_Time_t now = DDS_TIMESTAMP_CURRENT;

    SAC_REPORT_STACK();

    result = DDS_DataWriter_writedispose_w_timestamp(_this, instance_data, instance_handle, &now);
    SAC_REPORT_FLUSH(_this, (result != DDS_RETCODE_OK) && (result != DDS_RETCODE_TIMEOUT));
    return result;
}

DDS_ReturnCode_t
DDS_DataWriter_writedispose_w_timestamp (
    DDS_DataWriter _this,
    const DDS_Sample instance_data,
    const DDS_InstanceHandle_t instance_handle,
    const DDS_Time_t *source_timestamp)
{
    DDS_ReturnCode_t result;
    _DataWriter w;
    u_result uResult;
    os_timeW timestamp;
    writerInfo data;

    SAC_REPORT_STACK();

    if (instance_data != NULL) {
        result = DDS_DataWriterCheck(_this, &w);
        if (result == DDS_RETCODE_OK) {
            result = time_copyIn(source_timestamp, &timestamp, SAC_ENTITY_MAX_SUPPORTED_SECONDS(_this));
        }
        if (result == DDS_RETCODE_OK) {
            data.writer = _this;
            data.data = (void *)instance_data;
            uResult = u_writerWriteDispose(_DataWriter_get_user_entity(w),
                                           _DataWriterCopy,
                                           (void *)&data,
                                           timestamp,
                                           (u_instanceHandle)instance_handle);
            result = DDS_ReturnCode_get(uResult);
        }
    } else {
        result = DDS_RETCODE_BAD_PARAMETER;
        SAC_REPORT(result, "InstanceHandle = NULL");
    }
    SAC_REPORT_FLUSH(_this, (result != DDS_RETCODE_OK) && (result != DDS_RETCODE_TIMEOUT));
    return result;
}

DDS_ReturnCode_t
DDS_DataWriter_get_key_value (
    DDS_DataWriter _this,
    DDS_Sample key_holder,
    const DDS_InstanceHandle_t handle)
{
    DDS_ReturnCode_t result;
    _DataWriter w;
    u_result uResult;

    SAC_REPORT_STACK();

    result = DDS_RETCODE_OK;
    if (key_holder == NULL) {
        result = DDS_RETCODE_BAD_PARAMETER;
        SAC_REPORT(result, "Sample key_holder = NULL");
    }
    if (handle == DDS_HANDLE_NIL) {
        result = DDS_RETCODE_BAD_PARAMETER;
        SAC_REPORT(result, "InstanceHandle = DDS_HANDLE_NIL");
    }
    if (result == DDS_RETCODE_OK) {
        result = DDS_DataWriterClaim(_this, &w);
    }
    if (result == DDS_RETCODE_OK) {
        PREPEND_COPYOUTCACHE(w->copy_cache, key_holder, NULL);
        if (key_holder != NULL) {
            uResult = u_writerCopyKeysFromInstanceHandle(
                        _DataWriter_get_user_entity(w),
                        (u_instanceHandle)handle,
                        (u_copyOut)w->copy_out,
                        key_holder);
            result = DDS_ReturnCode_get(uResult);
            REMOVE_COPYOUTCACHE(w->copy_cache, key_holder);
        } else {
            result = DDS_RETCODE_OUT_OF_RESOURCES;
            SAC_REPORT(result, "DataWriter could not copy out key value");
        }
        /* The OpenSplice user-layer may detect that the instance is deleted
         * In that case according to the spec return PRECONDITION_NOT_MET.
         */
        if (result == DDS_RETCODE_ALREADY_DELETED) {
            result = DDS_RETCODE_PRECONDITION_NOT_MET;
            SAC_REPORT(result, "InstanceHandle was already deleted");
        }
        DDS_DataWriterRelease(_this);
    }
    SAC_REPORT_FLUSH(_this, result != DDS_RETCODE_OK);
    return result;
}

DDS_InstanceHandle_t
DDS_DataWriter_lookup_instance(
    DDS_DataWriter _this,
    const DDS_Sample instance_data)
{
    DDS_ReturnCode_t result;
    _DataWriter w;
    u_result uResult;
    DDS_InstanceHandle_t handle = DDS_HANDLE_NIL;
    writerInfo data;

    SAC_REPORT_STACK();

    if (instance_data != NULL) {
        result = DDS_DataWriterCheck(_this, &w);
        if (result == DDS_RETCODE_OK) {
            data.writer = _this;
            data.data = (void *)instance_data;
            uResult = u_writerLookupInstance(_DataWriter_get_user_entity(w),
                                             _DataWriterCopy,
                                             &data,
                                             (u_instanceHandle *)&handle);
            result = DDS_ReturnCode_get(uResult);
        }
    } else {
        result = DDS_RETCODE_BAD_PARAMETER;
        SAC_REPORT(result, "Sample = NULL");
    }
    SAC_REPORT_FLUSH(_this, result != DDS_RETCODE_OK);
    return handle;
}


DDS_ReturnCode_t
DDS_DataWriter_notify_listener (
    DDS_DataWriter _this,
    v_listenerEvent event)
{
    DDS_ReturnCode_t result;
    u_eventMask triggerMask;
    struct DDS_DataWriterListener cb;

    cb = _DataWriter(_this)->listener;
    triggerMask = event->kind;
    result = DDS_RETCODE_OK;

    if ((triggerMask & V_EVENT_LIVELINESS_LOST) &&
            (cb.on_liveliness_lost != NULL))
    {
        DDS_LivelinessLostStatus status;
        DDS_LivelinessLostStatus_init(&status, &((v_writerStatus)event->eventData)->livelinessLost);
        cb.on_liveliness_lost(cb.listener_data, _this, &status);
    }
    if ((triggerMask & V_EVENT_OFFERED_DEADLINE_MISSED) &&
            (cb.on_offered_deadline_missed != NULL))
    {
        DDS_OfferedDeadlineMissedStatus status;
        DDS_OfferedDeadlineMissedStatus_init(&status, &((v_writerStatus)event->eventData)->deadlineMissed);
        cb.on_offered_deadline_missed(cb.listener_data, _this, &status);
    }
    if ((triggerMask & V_EVENT_OFFERED_INCOMPATIBLE_QOS) &&
            (cb.on_offered_incompatible_qos != NULL))
    {
        DDS_OfferedIncompatibleQosStatus status;
        DDS_OfferedIncompatibleQosStatus_init(&status, &((v_writerStatus)event->eventData)->incompatibleQos);
        cb.on_offered_incompatible_qos(cb.listener_data, _this, &status);
    }
    if ((triggerMask & V_EVENT_PUBLICATION_MATCHED) &&
            (cb.on_publication_matched != NULL))
    {
        DDS_PublicationMatchedStatus status;
        DDS_PublicationMatchedStatus_init(&status, &((v_writerStatus)event->eventData)->publicationMatch);
        cb.on_publication_matched(cb.listener_data, _this, &status);
    }

    return result;
}
