#pragma once

#include <cstdint>
#include <string>
#include <map>
#include <memory>
#include <mutex>
#include <vector>

#include "profinet_parameter.hpp"
#include "../shared_data/shared_data_iface.hpp"

#include "../../libs/include/pnet_api.h"


class ProfinetSubmodule {
public:
    static std::shared_ptr<ProfinetSubmodule> Create(uint32_t submodule_id, 
                                                     std::string submodule_name, 
                                                     pnet_data_cfg_t& submodule_data_cfg,
                                                     SharedDataIface * inp_data_ptr,
                                                     SharedDataIface * out_data_ptr)
    {
        return std::shared_ptr<ProfinetSubmodule>(new ProfinetSubmodule(submodule_id,
                                                                        submodule_name, 
                                                                        submodule_data_cfg,
                                                                        inp_data_ptr,
                                                                        out_data_ptr));
    }

    ProfinetSubmodule(uint32_t submodule_id, 
                      std::string submodule_name, 
                      pnet_data_cfg_t& submodule_data_cfg,
                      SharedDataIface * inp_data_ptr,
                      SharedDataIface * out_data_ptr);
    
    bool addParameter(std::shared_ptr<ProfinetParameter>& param);

    std::shared_ptr<ProfinetParameter> getParameterPtr(uint32_t index);

    bool inputSetData( pnet_t * pnet_ptr, 
                       uint32_t api, 
                       uint16_t slot_nbr, 
                       uint16_t subslot_nbr, 
                       uint8_t iops);
    
    bool outputGetData(pnet_t * pnet_ptr, 
                       uint32_t api, 
                       uint16_t slot_nbr, 
                       uint16_t subslot_nbr, bool& data_update);
    
    void readOutputData(std::vector<uint8_t>& out);

    bool writeInputData(std::vector<uint8_t>& in);

    bool isDataUpdated() {      
        return data_updated_; 
    };

public:
    const uint32_t        m_id;               /// Идентификатор подмодуля
    const std::string     m_name;             /// Имя подмодуля
    const pnet_data_cfg_t m_data_cfg;         /// Конфигурация циклических данных подмодуля
    
    /// iocs = I/O consumer status data (формат данных из pnet_ioxs_values_t)
    uint8_t m_indata_iocs; 

    /// iops = I/O provider status data (формат данных из pnet_ioxs_values_t)
    uint8_t m_outdata_iops;
    
    
private:
    /// Набор параметров подмодуля
    std::map<uint32_t, std::shared_ptr<ProfinetParameter>> m_params;
    
    /// Буферы для взаимодействия с pnet и решения вопросов синхронизации
    std::shared_ptr<uint8_t[]> out_data_buf_;
    std::shared_ptr<uint8_t[]> inp_data_buf_;

    SharedDataIface * const inp_data_ptr_; /// Входные циклические данные (DEV->PLC)
    SharedDataIface * const out_data_ptr_; /// Выходные циклические данные (PLC->DEV)

    bool data_updated_;
};