/**
 * @file
 * @brief Unit test for a basic BACnet MS/TP Datalink
 * @author Steve Karg <skarg@users.sourceforge.net>
 * @date November 2024
 * @copyright SPDX-License-Identifier: MIT
 */
#include <zephyr/ztest.h>
#include <bacnet/datalink/mstp.h>
#include <bacnet/datalink/dlmstp.h>
#include <bacnet/basic/sys/bytes.h>
#include <mstp-rs485.h>

/**
 * @addtogroup bacnet_tests
 * @{
 */
static uint8_t RS485_Rx_Buffer[512];
static uint8_t RS485_Tx_Buffer[512];
static struct mstp_port_struct_t MSTP_Port;
static struct dlmstp_user_data_t MSTP_User;
static struct dlmstp_rs485_driver MSTP_RS485_Driver = {
    .init = MSTP_RS485_Init,
    .send = MSTP_RS485_Send,
    .read = MSTP_RS485_Read,
    .transmitting = MSTP_RS485_Transmitting,
    .baud_rate = MSTP_RS485_Baud_Rate,
    .baud_rate_set = MSTP_RS485_Baud_Rate_Set,
    .silence_milliseconds = MSTP_RS485_Silence_Milliseconds,
    .silence_reset = MSTP_RS485_Silence_Reset
};

/**
 * @brief Test the basic operation of the MS/TP Datalink
 */
#if defined(CONFIG_ZTEST_NEW_API)
ZTEST(dlsmtp_tests, test_MSTP_Datalink)
#else
static void test_MSTP_Datalink(void)
#endif
{
    bool status = false;
    struct dlmstp_statistics test_stats;
    uint32_t test_baudrate;
    uint8_t test_mac_address;
    BACNET_ADDRESS test_address;
    uint8_t test_frames;
    uint16_t test_length;
    uint8_t test_data[10] = { PDU_TYPE_ABORT, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    BACNET_NPDU_DATA test_npdu_data = { 0 };

    /* error handling before port is initialized */
    test_length = dlmstp_send_pdu(NULL, NULL, 0, 0);
    test_length = MSTP_Get_Send(NULL, 0);
    zassert_equal(test_length, 0, NULL);
    test_length = MSTP_Get_Reply(NULL, 0);
    zassert_equal(test_length, 0, NULL);
    MSTP_Send_Frame(NULL, NULL, 0);
    test_length = dlmstp_receive(&test_address, NULL, 0, 0);
    zassert_equal(test_length, 0, NULL);
    test_mac_address = dlmstp_mac_address();
    zassert_equal(test_mac_address, 0, NULL);
    test_frames = dlmstp_max_info_frames();
    zassert_equal(test_frames, 0, NULL);
    test_mac_address = dlmstp_max_master();
    zassert_equal(test_mac_address, 0, NULL);
    status = dlmstp_sole_master();
    zassert_equal(status, false, NULL);
    status = dlmstp_slave_mode_enabled_set(true);
    zassert_equal(status, false, NULL);
    status = dlmstp_slave_mode_enabled();
    zassert_equal(status, false, NULL);
    status = dlmstp_zero_config_enabled();
    zassert_equal(status, false, NULL);
    test_mac_address = dlmstp_zero_config_preferred_station();
    zassert_equal(test_mac_address, 0, NULL);
    dlmstp_zero_config_preferred_station_set(0);
    dlmstp_set_baud_rate(38400);
    test_baudrate = dlmstp_baud_rate();
    zassert_equal(test_baudrate, 0, NULL);
    dlmstp_set_frame_rx_complete_callback(NULL);
    dlmstp_set_invalid_frame_rx_complete_callback(NULL);
    dlmstp_set_frame_rx_start_callback(NULL);
    dlmstp_fill_statistics(NULL);
    dlmstp_reset_statistics();
    /* initialize */
    MSTP_User.RS485_Driver = &MSTP_RS485_Driver;
    MSTP_Port.UserData = &MSTP_User;
    MSTP_Port.InputBuffer = RS485_Rx_Buffer;
    MSTP_Port.InputBufferSize = sizeof(RS485_Rx_Buffer);
    MSTP_Port.OutputBuffer = RS485_Tx_Buffer;
    MSTP_Port.OutputBufferSize = sizeof(RS485_Tx_Buffer);
    ztest_expect_value(MSTP_Init, mstp_port, &MSTP_Port);
    status = dlmstp_init((char *)&MSTP_Port);
    zassert_true(status, NULL);
    zassert_true(MSTP_User.Initialized, NULL);
    zassert_equal(
        Ringbuf_Size(&MSTP_User.PDU_Queue), DLMSTP_MAX_INFO_FRAMES, NULL);
    zassert_equal(
        Ringbuf_Data_Size(&MSTP_User.PDU_Queue), sizeof(struct dlmstp_packet),
        NULL);
    zassert_equal(dlmstp_send_pdu_queue_empty(), true, NULL);
    zassert_equal(dlmstp_send_pdu_queue_full(), false, NULL);
    ztest_returns_value(MSTP_RS485_Silence_Milliseconds, 0);
    zassert_equal(dlmstp_silence_milliseconds(&MSTP_Port), 0, NULL);
    dlmstp_silence_reset(&MSTP_Port);
    ztest_returns_value(MSTP_RS485_Silence_Milliseconds, 0);
    zassert_equal(dlmstp_silence_milliseconds(&MSTP_Port), 0, NULL);
    zassert_equal(dlmstp_max_master_limit(), DLMSTP_MAX_MASTER, NULL);
    zassert_equal(dlmstp_max_info_frames_limit(), DLMSTP_MAX_INFO_FRAMES, NULL);
    dlmstp_fill_statistics(NULL);
    dlmstp_fill_statistics(&test_stats);
    zassert_equal(
        test_stats.transmit_frame_counter,
        MSTP_User.Statistics.transmit_frame_counter, NULL);
    zassert_equal(
        test_stats.receive_valid_frame_counter,
        MSTP_User.Statistics.receive_valid_frame_counter, NULL);
    zassert_equal(
        test_stats.receive_valid_frame_not_for_us_counter,
        MSTP_User.Statistics.receive_valid_frame_not_for_us_counter, NULL);
    zassert_equal(
        test_stats.receive_invalid_frame_counter,
        MSTP_User.Statistics.receive_invalid_frame_counter, NULL);
    zassert_equal(
        test_stats.transmit_pdu_counter,
        MSTP_User.Statistics.transmit_pdu_counter, NULL);
    zassert_equal(
        test_stats.receive_pdu_counter,
        MSTP_User.Statistics.receive_pdu_counter, NULL);
    zassert_equal(
        test_stats.bad_crc_counter, MSTP_User.Statistics.bad_crc_counter, NULL);
    zassert_equal(
        test_stats.poll_for_master_counter,
        MSTP_User.Statistics.poll_for_master_counter, NULL);
    dlmstp_reset_statistics();
    dlmstp_set_frame_rx_complete_callback(NULL);
    dlmstp_set_invalid_frame_rx_complete_callback(NULL);
    dlmstp_set_frame_rx_start_callback(NULL);
    dlmstp_get_broadcast_address(&test_address);
    zassert_equal(test_address.mac_len, 1, NULL);
    zassert_equal(test_address.mac[0], MSTP_BROADCAST_ADDRESS, NULL);
    zassert_equal(test_address.net, BACNET_BROADCAST_NETWORK, NULL);
    zassert_equal(test_address.len, 0, NULL);
    dlmstp_get_my_address(&test_address);
    zassert_equal(test_address.mac_len, 1, NULL);
    zassert_equal(test_address.mac[0], MSTP_Port.This_Station, NULL);
    zassert_equal(test_address.net, 0, NULL);
    zassert_equal(test_address.len, 0, NULL);
    test_mac_address = dlmstp_max_master();
    zassert_equal(test_mac_address, MSTP_Port.Nmax_master, NULL);
    dlmstp_set_max_master(10);
    test_mac_address = dlmstp_max_master();
    zassert_equal(test_mac_address, 10, NULL);
    test_frames = dlmstp_max_info_frames();
    zassert_equal(test_frames, MSTP_Port.Nmax_info_frames, NULL);
    dlmstp_set_max_info_frames(10);
    test_frames = dlmstp_max_info_frames();
    zassert_equal(test_frames, 10, NULL);
    test_mac_address = dlmstp_mac_address();
    zassert_equal(test_mac_address, MSTP_Port.This_Station, NULL);
    dlmstp_set_mac_address(10);
    test_mac_address = dlmstp_mac_address();
    zassert_equal(test_mac_address, 10, NULL);
    dlmstp_fill_bacnet_address(&test_address, 10);
    zassert_equal(test_address.mac_len, 1, NULL);
    zassert_equal(test_address.mac[0], 10, NULL);
    zassert_equal(test_address.net, 0, NULL);
    zassert_equal(test_address.len, 0, NULL);
    dlmstp_fill_bacnet_address(&test_address, MSTP_BROADCAST_ADDRESS);
    zassert_equal(test_address.mac_len, 0, NULL);
    zassert_equal(test_address.mac[0], 0, NULL);
    zassert_equal(test_address.net, 0, NULL);
    zassert_equal(test_address.len, 0, NULL);
    ztest_returns_value(MSTP_RS485_Baud_Rate, 38400);
    test_baudrate = dlmstp_baud_rate();
    zassert_equal(test_baudrate, 38400, NULL);
    ztest_expect_value(MSTP_RS485_Baud_Rate_Set, baud, 19200);
    ztest_returns_value(MSTP_RS485_Baud_Rate_Set, true);
    dlmstp_set_baud_rate(19200);
    dlmstp_zero_config_preferred_station_set(65);
    test_mac_address = dlmstp_zero_config_preferred_station();
    zassert_equal(test_mac_address, 65, NULL);
    dlmstp_zero_config_enabled_set(true);
    status = dlmstp_zero_config_enabled();
    zassert_equal(status, true, NULL);
    status = dlmstp_slave_mode_enabled_set(true);
    zassert_equal(status, true, NULL);
    status = dlmstp_slave_mode_enabled();
    zassert_equal(status, true, NULL);
    status = dlmstp_sole_master();
    zassert_equal(status, MSTP_Port.SoleMaster, NULL);
    /* various test cases */
    ztest_returns_value(MSTP_RS485_Transmitting, true);
    test_length = dlmstp_receive(&test_address, NULL, 0, 0);
    zassert_equal(test_length, 0, NULL);
    /* fixme: dlmstp_receive() calls driver with local var pointer
       and zmock does not seem to have a way to test that. */
    test_length = MSTP_Put_Receive(&MSTP_Port);
    zassert_equal(test_length, MSTP_Port.DataLength, NULL);
    zassert_equal(MSTP_User.ReceivePacketPending, true, NULL);
    ztest_expect_data(MSTP_RS485_Send, payload, test_data);
    MSTP_Send_Frame(&MSTP_Port, test_data, sizeof(test_data));
    zassert_equal(MSTP_User.Statistics.transmit_frame_counter, 1, NULL);
    test_length = MSTP_Get_Reply(&MSTP_Port, 0);
    zassert_equal(test_length, 0, NULL);
    test_npdu_data.data_expecting_reply = false;
    test_length = dlmstp_send_pdu(
        &test_address, &test_npdu_data, test_data, sizeof(test_data));
    zassert_equal(
        test_length, sizeof(test_data), "dlmstp_send_pdu() length=%d",
        test_length);
    ztest_expect_value(MSTP_Create_Frame, buffer, &MSTP_Port.OutputBuffer[0]);
    ztest_expect_value(
        MSTP_Create_Frame, buffer_len, MSTP_Port.OutputBufferSize);
    ztest_expect_value(
        MSTP_Create_Frame, frame_type,
        FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY);
    ztest_expect_value(MSTP_Create_Frame, destination, MSTP_BROADCAST_ADDRESS);
    ztest_expect_value(MSTP_Create_Frame, source, MSTP_Port.This_Station);
    ztest_expect_data(MSTP_Create_Frame, data, test_data);
    ztest_returns_value(
        MSTP_Create_Frame, sizeof(test_data) + DLMSTP_HEADER_MAX);
    test_length = MSTP_Get_Send(&MSTP_Port, 0);
    zassert_equal(
        test_length, sizeof(test_data) + DLMSTP_HEADER_MAX,
        "MSTP_Get_Send() length=%d", test_length);
}
/**
 * @}
 */

#if defined(CONFIG_ZTEST_NEW_API)
ZTEST_SUITE(dlmstp_tests, NULL, NULL, NULL, NULL, NULL);
#else
void test_main(void)
{
    ztest_test_suite(dlmstp_tests, ztest_unit_test(test_MSTP_Datalink));

    ztest_run_test_suite(dlmstp_tests);
}
#endif
