#include "app_settings.hpp"
#include "../libs/include/pnet_api.h"
#include "profinet.hpp"
#include <thread>
#include <iostream>

#define ECHO_MODULE_ID 0x00000040 /// Идентификатор тестового модуля
#define ECHO_SUBMOD_ID 0x00000140 /// Идентификатор тестового подмодуля
#define ECHO_INPUT_DATA_SIZE 8
#define ECHO_OUTPUT_DATA_SIZE 8
#define ECHO_PARAMETER_GAIN_IDX 125 /// Индекс параметра Gain для подмодуля ECHO

using namespace std;

uint32_t Echo_Gain = 0;

struct EchoData {
    float  data_f;
    uint32_t data_i;
};

union EchoDataMem
{
    EchoData data;
    uint8_t mem[sizeof(EchoData)];
};

EchoDataMem Echo_inpCycData;
EchoDataMem Echo_outCycData;

uint8_t be_data_inp[sizeof(EchoData)];

void endian_convert_32(uint8_t * p_data)
{
    uint8_t tmp = p_data[1];
    p_data[1] = p_data[2];
    p_data[2] = tmp;
    tmp = p_data[0];
    p_data[0] = p_data[3];
    p_data[3] = tmp;
}

int main(int argc, char * argv[])
{
    Echo_inpCycData.data.data_f = 54321.9f; /// Преобразовать в big endian
    Echo_inpCycData.data.data_i = 876;      /// Преобразовать в big endian

    endian_convert_32((uint8_t*)&Echo_inpCycData.data.data_f);
    endian_convert_32((uint8_t*)&Echo_inpCycData.data.data_i);

    Echo_outCycData.data.data_f = 0.0f;
    Echo_outCycData.data.data_i = 0;

    AppSettings app_settings = {
        .EthDevName = "enp6s1",
    };

    ProfinetSettings settings = {
        .ticks_us = 1000,     /// Период таймера в мкс
        .EthIface = "enp6s1", /// Имя интерфейса Ethernet к которому будет подключаться
    };

    ProfinetDeviceSettings profinet_settings = {
        .product_name = "P-Net Sample Application",
        .station_name = "rt-labs-dev",
        .im_0 = {
            .vendor_id = 0x0493,
            .hw_revision = 3,
            .sw_revision = {
                .prefix = 'V',
                .functional_enhancement = 0,
                .bug_fix = 2,
                .internal_change = 0,
            },
            .revision_counter = 0,
            .profile_id = 0x1234,
            .profile_specific_type = 0x5678,
            .supported = (PNET_SUPPORTED_IM1 | PNET_SUPPORTED_IM2 | PNET_SUPPORTED_IM3),
            .order_id = "12345 Abcdefghijk",
            .serial_number = "007",
        },
        .im_1 = {
            .tag_function = "my function",
            .tag_location = "my location"
        },
        .im_2 = {
            .date = "2022-03-01 10:03"
        },
        .im_3 = {
            .descriptor = "my descriptor"
        },
        .im_4 = {
            .signature = ""
        },
        .device_id = 0x0002,
        .oem_vendor_id = 0xcafe,
        .oem_device_id = 0xee02,
        .send_hello = true,
        .min_device_interval = 32 //1ms ??
    };

    Profinet profinet;
    /// Настройка  Profinet: Инициализация pnet, добавление DAP слотов и подслотов и подключение к ним модулей и подмодулей
    if (!profinet.Config(settings, profinet_settings))
        return 0;
    /**
     * @brief Создание тестового модуля ECHO
     * От контроллера принимает данные в составе 1 значения uint32_t и 1 значения float
     * Умножает их на определенное число Gain и передает получившиеся значения обратно в контроллер.
     * 
     */
    /// 1. Создаем тестовый модуль ECHO: module_id = 0x40 (64)
    auto echo_module_ptr = make_shared<ProfinetModule>(ECHO_MODULE_ID, "Echo module");
    
    /// 2. Создаем конфигурацию циклических данных подмодуля
    pnet_data_cfg_t echo_data_cfg = { PNET_DIR_IO, ECHO_INPUT_DATA_SIZE, ECHO_OUTPUT_DATA_SIZE };
    
    /// 3. Создаем тестовый подмодуль ECHO: submodule_id = 0x140 (320)
    auto echo_submodule_ptr = make_shared<ProfinetSubmodule>(ECHO_SUBMOD_ID, 
                                                             "Echo submodule", 
                                                             echo_data_cfg, 
                                                             static_cast<void*>(Echo_inpCycData.mem),
                                                             static_cast<void*>(Echo_outCycData.mem));
    
    /// 4. Создаем конфигурационные данные подмодуля (не имеет отношения к echo_data_cfg). Это кастомные настройки подмодуля.
    auto echo_parameter = make_shared<ProfinetParameter>(ECHO_PARAMETER_GAIN_IDX, 
                                                         "Echo gain setting",
                                                         (uint8_t *)&Echo_Gain, 
                                                         sizeof(Echo_Gain));
    
    /// 5. Добавляем параметр к подмодулю
    echo_submodule_ptr->addParameter(echo_parameter);
    
    /// 6. Добавляем подмодуль к модулю
    echo_module_ptr->addSubmodule(echo_submodule_ptr);
    
    /// 7. Добавляем модуль к конфигурации. В этом месте модуль еще не активен, т.к. он не вставлен в слот.
    /// Это делает profinet controller.
    profinet.addModule(echo_module_ptr);

    /**
     * Циклические данные устройства передаются с определенным периодом, сообщения от контроллера при этом не требуются.
     */
    /// Запуск потока Profinet
    profinet.Start();
    
    uint32_t ix = 0;
    while(1)
    {
        std::this_thread::sleep_for(std::chrono::milliseconds(1000));
        ++ix;
        std::cout << "tick: " << ix << endl;
    }
    
    return 0;
}