#pragma once

#include "../../profinet/profinet_data_map.hpp"
#include <boost/interprocess/managed_shared_memory.hpp>

#include <iostream>
#include <map>
#include <memory>

class ProfinetSharedDataClient {
public:
    /**
     * @brief Подключается к выделенной разделяемой памяти
     * 
     * @param mem_name 
     */
    ProfinetData_Map * Connect(std::string mem_name) {
        ProfinetData_Map * out{nullptr};

        try
        {
            shmem_ = boost::interprocess::managed_shared_memory{boost::interprocess::open_only, mem_name.c_str()};
            try
            {
                auto mem = shmem_.find<ProfinetData_Map>("ProfinetData_Map");
                out = mem.first;
                ///Упорядочиваем память для удобства обращения по индентификатору Profinet
                collected_data = collect(out);
            }
            catch(const std::exception& e)
            {
                std::cerr << e.what() << '\n';
                return nullptr;
            }
        }
        catch(const std::exception& e)
        {
            std::cerr << e.what() << '\n';
            return out;
        }
        return out;
    }
    /**
     * @brief Возвращает указатель на данные подмодуля в разделяемой памяти
     * 
     * @param module_id идентификатор модуля 
     * @param submodule_id идентификатор подмодуля
     * @return ProfinetData_Submodule* 
     */
    ProfinetData_Submodule * getSubmodule(uint32_t module_id, uint32_t submodule_id)
    {
        return (*collected_data)[module_id].submods[submodule_id].p_submod;
    }
    /**
     * @brief Возвращает указатель на данные параметра подмодуля в разделяемой памяти
     * 
     * @param module_id идентификатор модуля
     * @param submodule_id идентификатор подмодуля
     * @param param_index индекс параметра
     * @return ProfinetData_Parameter* 
     */
    ProfinetData_Parameter * getSubmoduleParameter(uint32_t module_id, uint32_t submodule_id, uint32_t param_index)
    {
        return (*collected_data)[module_id].submods[submodule_id].params[param_index];
    }

private:

    struct ProfinetSharedDataSubmodule {
        ProfinetData_Submodule* p_submod;
        std::map<uint32_t, ProfinetData_Parameter*> params;
    };

    struct ProfinetSharedDataModule {
        ProfinetData_Module* p_mod;
        std::map<uint32_t, ProfinetSharedDataSubmodule> submods;
    };

    /**
     * @brief Собирает данные в словарь, можно использовать для удобства обращения к данным по идентификатору
     * 
     * @param p_data_map 
     * @return std::shared_ptr<std::map<uint32_t, ProfinetSharedDataModule>>  
     */
    static std::shared_ptr<std::map<uint32_t, ProfinetSharedDataModule>> collect(ProfinetData_Map * p_data_map) {
        std::shared_ptr<std::map<uint32_t, ProfinetSharedDataModule>> p_map(new std::map<uint32_t, ProfinetSharedDataModule>);
        
        for (int i = 0; i < p_data_map->mods_nbr; ++i)
        {
            auto& mod = (*p_map)[p_data_map->p_mods[i].id];

            mod.p_mod = &p_data_map->p_mods[i];

            for (int j = 0; j < p_data_map->p_mods[i].submods_nbr; ++j)
            {
                auto& submod = mod.submods[p_data_map->p_mods[i].p_submods[j].id];

                submod.p_submod = &p_data_map->p_mods[i].p_submods[j];

                for (int k = 0; k < p_data_map->p_mods[i].p_submods[j].params_nbr; ++k)
                {
                    submod.params[p_data_map->p_mods[i].p_submods[j].p_params[k].index] = &p_data_map->p_mods[i].p_submods[j].p_params[k];
                }
            }
        }

        return p_map;
    }

private:
    boost::interprocess::managed_shared_memory shmem_;
    std::shared_ptr<std::map<uint32_t, ProfinetSharedDataModule>> collected_data; /// Упорядоченная структура указателей на разделяемую память
};