/**
* Copyright (c) NVIDIA CORPORATION & AFFILIATES, 2001-2017. ALL RIGHTS RESERVED.
*
* See file LICENSE for terms.
*/

#ifndef UCT_RC_TEST_H_
#define UCT_RC_TEST_H_

#include <common/test.h>
#include <uct/uct_test.h>

extern "C" {
#include <uct/api/uct.h>
#include <uct/ib/rc/base/rc_ep.h>
#include <uct/ib/rc/base/rc_iface.h>
}


class test_rc : public uct_test {
public:
    typedef struct pending_send_request {
        uct_pending_req_t uct;
        int               cb_count;
        int               purge_count;
        uct_ep_h          ep;
    } pending_send_request_t;

    virtual void init();
    virtual void connect();

    uct_rc_iface_t* rc_iface(entity *e) {
        return ucs_derived_of(e->iface(), uct_rc_iface_t);
    }

    uct_rc_ep_t* rc_ep(entity *e, int ep_idx = 0) {
        return ucs_derived_of(e->ep(ep_idx), uct_rc_ep_t);
    }

    void send_am_messages(entity *e, int wnd, ucs_status_t expected,
                          uint8_t am_id = 0, int ep_idx = 0) {
        for (int i = 0; i < wnd; i++) {
            EXPECT_EQ(expected, send_am_message(e, am_id, ep_idx));
        }
    }

    void progress_loop(double delta_ms=10.0) {
        uct_test::short_progress_loop(delta_ms);
    }

    void test_iface_ops(int cq_len);

    static ucs_status_t am_dummy_handler(void *arg, void *data, size_t length,
                                         unsigned flags) {
        return UCS_OK;
    }

protected:
    entity *m_e1, *m_e2;

};

class test_rc_flow_control : public test_rc {
public:
    typedef struct pending_send_request {
        uct_pending_req_t uct;
        int               cb_count;
        int               purge_count;
    } pending_send_request_t;

    void init();
    void cleanup();

    virtual uct_rc_fc_t* get_fc_ptr(entity *e, int ep_idx = 0) {
        return &rc_ep(e, ep_idx)->fc;
    }

    virtual void disable_entity(entity *e) {
        rc_iface(e)->tx.cq_available = 0;
    }

    virtual void enable_entity(entity *e, unsigned cq_num = 128) {
        rc_iface(e)->tx.cq_available = cq_num;
    }

    virtual void set_tx_moderation(entity *e, int val) {
        rc_iface(e)->config.tx_moderation = val;
    }

    void set_fc_attributes(entity *e, bool enabled, int wnd, int s_thresh, int h_thresh) {
        rc_iface(e)->config.fc_enabled     = enabled;
        rc_iface(e)->config.fc_wnd_size    = get_fc_ptr(e)->fc_wnd = wnd;
        rc_iface(e)->config.fc_soft_thresh = s_thresh;
        rc_iface(e)->config.fc_hard_thresh = h_thresh;

    }

    void set_fc_disabled(entity *e) {
        /* same as default settings in rc_iface_init */
        set_fc_attributes(e, false, std::numeric_limits<int16_t>::max(), 0, 0);
    }

    void send_am_and_flush(entity *e, int num_msg);

    void progress_loop(double delta_ms=10.0) {
        uct_test::short_progress_loop(delta_ms);
    }

    static ucs_status_t am_dummy_handler(void *arg, void *data, size_t length, void *desc) {
        return UCS_OK;
    }

    static ucs_status_t am_handler(void *arg, void *data, size_t length,
                                   unsigned flags)
    {
        ++m_am_rx_count;
        return UCS_OK;
    }

    static void purge_cb(uct_pending_req_t *self, void *arg) {
        pending_send_request_t *req = ucs_container_of(self,
                                                       pending_send_request_t,
                                                       uct);
        ++req->purge_count;
    }

    static ucs_status_t pending_cb(uct_pending_req_t *self) {
        pending_send_request_t *req = ucs_container_of(self,
                                                       pending_send_request_t,
                                                       uct);
        ++req->cb_count;
        return UCS_OK;
    }

    void validate_grant(entity *e);

    void test_general(int wnd, int s_thresh, int h_thresh, bool is_fc_enabled);

    virtual void wait_fc_hard_resend(entity *e);

    virtual void test_pending_grant(int16_t wnd);

    void test_pending_purge(int wnd, int num_pend_sends);

    void test_flush_fc_disabled();

protected:
    static const uint8_t FLUSH_AM_ID = 1;
    static uint32_t m_am_rx_count;
};


#ifdef ENABLE_STATS
class test_rc_flow_control_stats : public test_rc_flow_control {
public:
    void init() {
        stats_activate();
        test_rc_flow_control::init();
    }

    void cleanup() {
        test_rc_flow_control::cleanup();
        stats_restore();
    }

    void test_general(int wnd, int s_thresh, int h_thresh);
};
#endif


class test_rc_iface_attrs : public test_uct_iface_attrs {
public:
    test_rc_iface_attrs() {
        ucs_status_t status = uct_config_modify(m_iface_config,
                                                "RC_TM_ENABLE", "y");
        EXPECT_TRUE((status == UCS_OK) || (status == UCS_ERR_NO_ELEM));
    }

    attr_map_t get_num_iov_mlx5_common(size_t av_size);

    attr_map_t get_num_iov();
};


#endif
