#pragma once

/// @file
/// @brief Определение функций VPN API
///
/// @copyright Copyright 2019 InfoTeCS.

#include <stdint.h>
#include <stdbool.h>
#include <time.h>

#if defined( WIN32 ) || defined( WIN64 )
#    define VpnApiExport
#else
#    if ( __GNUC__ >= 4 ) || defined( __clang__ )
#        define VpnApiExport __attribute__( ( visibility( "default" ) ) )
#    else
#        define VpnApiExport
#    endif
#endif

#if defined( __linux__ ) || defined( __ANDROID__ ) || defined( WIN32 ) || defined( WIN64 )
#    define FULL_API
#endif
#if defined( __APPLE__ ) && defined( __MACH__ )
#    include <vpn_api/business_mail_api.h>
#    include <vpn_api/cipher_api.h>
#    include <vpn_api/events.h>
#    include <vpn_api/vpn_api_types.h>
#    include <vpn_api/vpn_api_version.h>
#else // defined( __APPLE__ ) && defined( __MACH__ )
#    include "vpn_api_types.h"
#endif // defined( __APPLE__ ) && defined( __MACH__ )

/*! \defgroup get_client_param_keys Ключи для получения параметров с помощью функции GetClientParam
 * @{
 */
/// @brief Ключ для получения номера сети
static const char* const paramKeyNetworkNumber = "network.number";
/// @brief Ключ для получения используемого типа шифрования
static const char* const paramSecurityEncryptionMode = "node.security.encryption.mode";
/// @brief Ключ для получения ссылки для доступа к системе мониторинга по открытой сети
static const char* const paramSystemNetworkMonitoringOpenUrls = "system.monitoring.openUrls";
/// @brief Ключ для получения ссылки для доступа к системе мониторинга по защищенной сети
static const char* const paramSystemNetworkMonitoringVipnetUrls = "system.monitoring.vipnetUrls";

/// @brief Ключ для получения ссылки для доступа к системе мониторинга NVS по открытой сети
static const char* const paramNvsConnectionOpenUrls = "nvs.info/connection.openUrls";
/// @brief Ключ для получения ссылки для доступа к системе мониторинга NVS по защищенной сети
static const char* const paramNvsConnectionVipnetUrls = "nvs.info/connection.vipnetUrls";
/// @brief Ключ для получения идентификатора авторизации в NVS
static const char* const paramNvsAuthenticationId = "nvs.info/authentication.id";
/// @brief Ключ для получения секрета авторизации в NVS
static const char* const paramNvsAuthenticationSecret = "nvs.info/authentication.secret";
/// @brief Ключ для получения уникального идентификатора устройства для отправки данных в NVS
static const char* const paramNodeDeviceId = "node.deviceId";

/// @brief Ключ для получения содержимого файла nvs.info
static const char* const paramNvsInfoData = "data/nvs.info";
/// @brief Ключ для получения содержимого файла itcslic.xml
static const char* const paramItcslicXmlData = "data/itcslic.xml";
/*! @} */ // get_client_param_keys

/*! \defgroup set_init_param_keys Ключи для задания параметров инициализации
 * @{
 */
/// @brief Ключ для задания имя группы, специфичен для платформы  apple
static const char* const initKeyAppGroupName = "app_group_name";
/// @brief Ключ для задания имя приложения, специфичен для платформы  apple
static const char* const initKeyAppName = "app_name";

/*! @} */ // set_init_param_keys

/*! \defgroup set_extra_param_keys Ключи определяют расширенные параметры, например, логгер
 * @{
 */
/// @brief Ключ для передачи обработчика, выполняющего перенаправление логов в вызывающий код
static const char* const initKeyLogHandler = "log_handler";
/// @brief Ключ для передачи уровня логирования из вызывающего кода
static const char* const initKeyLogHandlerLevel = "log_level";
/// @brief Прототип обработчика логов
/// @param[in] logLevel - уровень логирования
/// @param[in] message - лог
/// @remarks по умолчанию уровень логирования 3
///
typedef void ( *LogEventHandler )( enum LoggerLevel logLevel, const char* message );
/*! @} */ // set_extra_param_keys

/// @brief Получить текущую версию ViPNet Client
/// @param[out] version - Буфер для передачи версии или NULL для получения необходимого размера буфера
/// @param[in,out] size - Размер буфера[in], размер результирующих данных с завершающим '\0'[out]
/// @remarks Память для version должна быть выделена и освобождена самостоятельно
/// @return Результат выполнения команды
typedef VpnApiReturnCode ( *GetClientVersion )( char* version, size_t* size );

/// @brief Установить ключи
/// @param[in] dstPath - Путь к дистрибутиву ключей
/// @param[in] psw - Пароль к дистрибутиву ключей
/// @return Результат выполнения команды
typedef VpnApiReturnCode ( *InstallKeys )( const char* dstPath, const char* psw );

/// @brief Удалить ключи
/// @return Результат выполнения команды
typedef VpnApiReturnCode ( *DeleteKeys )();

/// @brief Включить VPN
/// @param[in] psw - Пароль к дистрибутиву ключей
/// @return Результат выполнения команды
typedef VpnApiReturnCode ( *StartVpn )( const char* psw );

/// @brief Выключить VPN
/// @return Результат выполнения команды
typedef VpnApiReturnCode ( *StopVpn )();

/// @brief Получение статуса VPN
/// @param[out] vpnStatus - Текущий статус VPN
/// @return Результат выполнения команды
typedef VpnApiReturnCode ( *GetVpnStatus )( VpnStatus* vpnStatus );

/// @brief Получить дату и время истечения лицензии в секундах с начала времен
/// @param[out] time - Дата и время истечения лицензии в секундах
/// @remarks Для бессрочной лицензии будет возвращён 0
/// @return Результат выполнения команды
typedef VpnApiReturnCode ( *GetLicenseExpiration )( time_t* time );

/// @brief Получение информации об узлах
/// @param[out] nodesInfo - Информация об узлах
/// @param[out] size - Количество узлов
/// @remarks Память для nodesInfo выделяется динамически и должна быть освобождена с помощью ReleaseVpnNodeInfo( nodesInfo, size )
/// @return Результат выполнения команды
typedef VpnApiReturnCode ( *GetNodesInfo )( VpnNodeInfo** nodesInfo, size_t* size );

/// @brief Получение информации о пользователях
/// @param[out] usersInfo - Информация о пользователях
/// @param[out] size - Количество пользователей
/// @remarks Память для usersInfo выделяется динамически и должна быть освобождена с помощью ReleaseVpnUserInfo( usersInfo, size )
/// @remarks Структура VpnUserInfo имеет переменную длину
/// @return Результат выполнения команды
typedef VpnApiReturnCode ( *GetUsersInfo )( VpnUserInfo** usersInfo, size_t* size );

/// @brief Получение информации об узлах по маске ПЗ и свойств узла
/// @param[out] nodesInfo - Информация об узлах
/// @param[out] size - Количество узлов
/// @param[in] tasksMask - Битовая маска прикладных задач и свойств узла из VpnNodeTask, по которой выполняется фильтрация узлов
/// @remarks Память для nodesInfo выделяется динамически и должна быть освобождена с помощью ReleaseVpnNodeInfo( nodesInfo, size )
/// @return Результат выполнения команды
typedef VpnApiReturnCode ( *GetNodesInfoByTask )( VpnNodeInfo** nodesInfo, size_t* size, VpnNodeTasksMask tasksMask );

/// @brief Получение списка id узлов
/// @param[out] nodeIds - Список id узлов
/// @param[out] size - Количество узлов
/// @remarks Память для nodeIds выделяется динамически и должна быть освобождена с помощью ReleaseVpnNodeIds( nodesIds, size )
/// @return Результат выполнения команды
typedef VpnApiReturnCode ( *GetNodesIds )( uint32_t** nodeIds, size_t* size );

/// @brief Получение информации об узле по его ID
/// @param[in] id - ID узла
/// @param[out] nodeInfo - Информация об узле
/// @remarks Память для nodeInfo выделяется динамически и должна быть освобождена с помощью ReleaseVpnNodeInfo( nodeInfo, 1 )
/// @return Результат выполнения команды
typedef VpnApiReturnCode ( *GetNodeInfo )( uint32_t id, VpnNodeInfo** nodeInfo );

/// @brief Получение информации о пользовтеле по его ID
/// @param[in] id - ID пользователя
/// @param[out] userInfo - Информация о пользовтеле
/// @remarks Память для userInfo выделяется динамически и должна быть освобождена с помощью ReleaseVpnUserInfo( userInfo, 1 )
/// @return Результат выполнения команды
typedef VpnApiReturnCode ( *GetUserInfo )( uint32_t id, VpnUserInfo** userInfo );

/// @brief Получение списка id пользователей, зарегистрированных за данным узлом
/// @param[in] nodeId - id узла
/// @param[out] userIds - Список id пользователей
/// @param[out] size - Количество пользователей
/// @remarks Память для userIds выделяется динамически и должна быть освобождена с помощью ReleaseVpnUserIds( userIds, size )
/// @return Результат выполнения команды
typedef VpnApiReturnCode ( *GetUserIdsForNode )( uint32_t nodeId, uint32_t** userIds, size_t* size );

/// @brief Получение информации о своем узле
/// @param[out] nodeInfo - Информация об узле
/// @remarks Память для nodeInfo выделяется динамически и должна быть освобождена с помощью ReleaseVpnNodeInfo( nodeInfo, 1 )
/// @return Результат выполнения команды
typedef VpnApiReturnCode ( *GetOwnNodeInfo )( VpnNodeInfo** nodeInfo );

/// @brief Получение уровня полномочий своего узла
/// @param[out] privilege - уровень полномочий
/// @return Результат выполнения команды
typedef VpnApiReturnCode ( *GetOwnPrivilegeLevel )( enum VpnPrivilege* privilege );

/// @brief Получение конфигурационного параметра ViPNet в виде строковых данных по ключу
/// @param[in] key ключ для поиска данных
/// @param[in] keyLength длина буфера с ключом
/// @param[out] outData буфер для возвращения данных
/// @param[out] outDataLength размер динамически выделенного буфера с данными
/// @remarks Память для outData выделяется динамически и должна быть освобождена с помощью ReleaseClientParamData( outData )
/// @return Результат выполнения команды
typedef VpnApiReturnCode ( *GetClientParam )( const char* key,
                                              uint32_t keyLength,
                                              char** outData,
                                              uint32_t* outDataLength );

/// @brief Освобождение памяти, выделенной для передачи ClientSettings
/// @param[in] data буфер, который необходимо освободить
typedef void ( *ReleaseClientParamData )( char* data );

/// @brief Освобождение памяти, выделенной для информации об узлах
/// @param[in] nodeInfo - Информация об узлах, память которой необходимо освободить
/// @param[in] size - Количество узлов
typedef void ( *ReleaseVpnNodeInfo )( VpnNodeInfo* nodeInfo, size_t size );

/// @brief Освобождение памяти, выделенной для информации о пользователях
/// @param[in] userInfo - Информация об пользователях, память которой необходимо освободить
/// @param[in] size - Количество пользователей
typedef void ( *ReleaseVpnUserInfo )( VpnUserInfo* userInfo, size_t size );

/// @brief Освобождение памяти, выделенной для списка узлов
/// @param[in] nodeIds - Массив id узлов, который необходимо освободить
/// @param[in] size - Количество узлов
typedef void ( *ReleaseVpnNodeIds )( uint32_t* nodeIds, size_t size );

/// @brief Освобождение памяти, выделенной для списка пользователей
/// @param[in] userIds - Массив id пользователей, который необходимо освободить
/// @param[in] size - Количество пользователей
typedef void ( *ReleaseVpnUserIds )( uint32_t* userIds, size_t size );

/// @brief Получение ID активного координатора
/// @param[out] id - ID активного координатора, 0 - в случае ошибки
/// @return Результат выполнения команды
typedef VpnApiReturnCode ( *GetActiveCoordinator )( uint32_t* id );

/// @brief Получение текущего уровня логирования
/// @param[out] logLevel - Уровень логирования
/// @return Результат выполнения команды
typedef VpnApiReturnCode ( *GetLogLevel )( int32_t* logLevel );

/// @brief Задание текущего уровня логирования
/// @param[in] logLevel - Уровень логирования
/// @return Результат выполнения команды
typedef VpnApiReturnCode ( *SetLogLevel )( int32_t logLevel );

/// @brief Выполнение регламентного контроля
/// @param[in] psw - Пароль администратора узла
/// @remarks Для успешного выполнения команды VPN должен быть запущен
/// @return Результат выполнения команды
typedef VpnApiReturnCode ( *RegulationsCheck )( const char* psw );

/// @brief Сбор файлов логирования в ZIP-архив
/// @param[in] path - Путь к архиву
/// @return Результат выполнения команды
typedef VpnApiReturnCode ( *CollectReport )( const char* path );

/// @brief Сбор журнала событий в ZIP-архив
/// @param[in] path - Путь к архиву
/// @param[in] psw - Пароль администратора узла
/// @param[in] truncate - Флаг о необходимости удаления журнала после экспорта
/// @remarks Для успешного выполнения команды VPN должен быть запущен
/// @return Результат выполнения команды
typedef VpnApiReturnCode ( *CollectEventlog )( const char* path, const char* psw, bool truncate );

/// @brief Прототип обработчика событий
/// @param[in] event - событие VPN
typedef void ( *VpnEventHandler )( enum VpnEvent event );

/// @brief Подписка на события VPN
/// @param[in] eventHandler - Обработчик событий или NULL для отмены подписки
/// @remarks Поддерживается подписка только единственного обработчика
/// @remarks Не рекомендуется выполнять отмену подписки внутри обработчика событий
/// @return Результат выполнения команды
typedef VpnApiReturnCode ( *SubscribeForEvents )( VpnEventHandler eventHandler );

/// @brief Прототип обработчика событий с дополнительными данными
/// @param[in] data - указатель на произвольные данные, переданные через SubscribeForEventsExt
/// @param[in] event - событие VPN
typedef void ( *VpnEventHandlerExt )( void* data, enum VpnEvent event );

/// @brief Подписка на события VPN для объекта data
/// @param[in] data - ненулевой! указатель на произвольные данные, который будет передан обработчику
/// @param[in] eventHandler - Обработчик событий
/// @remarks Для отмены подписки необходимо использовать UnsubscribeFromEventsExt
/// @remarks Не рекомендуется выполнять отмену подписки внутри обработчика событий
/// @return Результат выполнения команды
typedef VpnApiReturnCode ( *SubscribeForEventsExt )( void* data, VpnEventHandlerExt eventHandler );

/// @brief Отмена подписки на события VPN для объекта data
/// @param[in] data - ненулевой! указатель на произвольные данные, для которого необходимо отменить подписку
/// @return Результат выполнения команды
typedef VpnApiReturnCode ( *UnsubscribeFromEventsExt )( void* data );

/*! \defgroup api_token_operations Функции SDK для усиленной аутентификации
 * @{
 */
/// @brief Получить список токенов, подключённых и поддерживаемых
/// @param[in] cbk - Обработчик дескриптора токена; может вернуть не 0 для прерывания опроса
/// @return Результат выполнения команды
typedef VpnApiReturnCode ( *CollectTokens )( int ( *cbk )( VpnTokenHandle ) );

/// @brief Задать параметры устройства аутентификации
/// @param[in] handle - дескриптор токена, который должен быть использован для аутентификации
/// @param[in] pin - пин-код пользователя устройства аутентификации (немаскированный)
/// @remarks handle может быть NULL, тогда будет использоваться то же устройство, что и при разворачивании DST или переносе ключа
/// @remarks pin может быть NULL, тогда будет использоваться пин-код, предварительно сохраненный с помощью SetPin
/// @return Результат выполнения команды
typedef VpnApiReturnCode ( *SetToken )( VpnTokenHandle handle, const char* pin );

/// @brief Сохранить пин-код для устройства аутентификации
/// @ingroup api_ver6 api_token_operations
/// @param[in] handle - дескриптор токена, пин-код которго надо сохранить
/// @param[out] pin - пин-код пользователя устройства аутентификации (немаскированный)
/// @remarks пин-код сохраняется в маскированном виде и уничтожается при выполнении ReleaseTokenHandle
/// @return Результат выполнения команды
typedef VpnApiReturnCode ( *SetPin )( VpnTokenHandle handle, const char* pin );

/// @brief Получить параметры устройства аутентификации
/// @param[in] handle - дескриптор токена, информацию о котором надо получить
/// @param[out] info - указатель на структуру, в которую будет скопирована информация
/// @remarks VpnTokenInfo содержит указатели на строки - эти объекты уничтожаются при вызове ReleaseTokenHandle
/// @return Результат выполнения команды
typedef VpnApiReturnCode ( *GetTokenInfo )( VpnTokenHandle handle, VpnTokenInfo* info );

/// @brief Освободить память, выделенную под дескриптор
/// @param[in] handle - дескриптор токена
/// @remarks Следует вызывать для каждого дескриптора, когда он перестаёт быть нужным (с учётом ремарки к GetTokenInfo)
/// @return Результат выполнения команды
typedef VpnApiReturnCode ( *ReleaseTokenHandle )( VpnTokenHandle handle );

/// @brief Получить конфигурацию модулей pkcs#11 и статус их использования
/// @param[in] cbk - Обработчик информации, получает json-структуру в utf8
/// @details Возвращаемая структура имеет следующий формат:
/**
* \code{.json}
*{
*    "global": { "paths": "<path1>:<path2>:...:<pathN>" },
*    "token01": {
*        "name": "<наименование>",
*        "file": "<имя файла модуля pkcs#11>",
*        "type": "<числовой идентификатор типа>",
*        "check": {
*            "library": "<актуальный путь к модулю pkcs#11, если найден>",
*            "load": "<true/false: загружена библиотека pkcs#11 в CSP или нет>"
*        }
*    },
*    "token02": { ... },
*    ....
*    "tokenNN": { ... }
*}
* \endcode
*/
/// @return Результат выполнения команды
typedef VpnApiReturnCode ( *GetTokenConfig )( int ( *cbk )( const char* ) );

/// @brief Перенести персональный ключ на устройство и сменить тип аутентификации на усиленную (ключ-на-токене)
/// @param[in] handle - дескриптор токена, который должен быть использован для аутентификации
/// @param[in] pin - пин-код пользователя устройства аутентификации (немаскированный)
/// @remarks Для успешного выполнения команды VPN должен быть запущен
/// @return Результат выполнения команды
typedef VpnApiReturnCode ( *MoveKeysToToken )( VpnTokenHandle handle, const char* pin );
/*! @} */ // api_token_operations

/*! \defgroup api_ver6
    @{
*/
/// @brief Запуск проверки соединения с активным координатором
/// @param[out] nodeId идентификатор активного координатора
/// @return Результат выполнения команды
typedef VpnApiReturnCode ( *CheckActiveCoordinatorConnection )( uint32_t* nodeId );

/// @brief Запуск проверки соединения с узлом
/// @param[in] nodeId идентификатор узла
/// @return Результат выполнения команды
typedef VpnApiReturnCode ( *CheckNodeConnection )( uint32_t nodeId );

/// @brief Смена активного координатора
/// @param[in] nodeId идентификатор нового активного координатора
/// @return Результат выполнения команды
typedef VpnApiReturnCode ( *SetActiveCoordinatorId )( uint32_t nodeId );

/// @brief Установка директории обмена для настройки RPC через TCP/IP
/// @param[in] folder Директория обмена
/// @return Результат выполнения команды
typedef VpnApiReturnCode ( *SetExchangeFolder )( const wchar_t* folder );

/// @brief Получение информации об узле по его ID
/// @param[in] id - ID узла
/// @param[out] nodeInfo - Информация об узле
/// @remarks Память для nodeInfo выделяется динамически и должна быть освобождена с помощью ReleaseNodeInfoEx( nodeInfo )
/// @return Результат выполнения команды
/// @details Структура VpnNodeInfoExt содержит информацию, аналогичную VpnNodeInfo, и информацию о туннелируемых ресурсах, если применимо
typedef VpnApiReturnCode ( *GetNodeInfoExt )( uint32_t id, VpnNodeInfoExt** nodeInfo );

/// @brief Освобождение памяти, выделенной для информации об узлах
/// @param[in] nodeInfo - Информация об узлах, память которой необходимо освободить
/// @return Результат выполнения команды
typedef VpnApiReturnCode ( *ReleaseNodeInfoExt )( VpnNodeInfoExt* nodeInfo );

/// @brief Установить обработчики событий сервиса vipnetclient
/// @param[in] consumer - уникальный идентификатор подписчика (не должен содержать пробельные символы)
/// @param[in] events - структура указателей на фукнции-обработчики
/// @return Результат выполнения команды
/// @details Позволяет установить индивидуальные функции для обработки каждого события, а также принимать параметры этих событий (если предусмотрено)
typedef VpnApiReturnCode ( *SetEventHandlers )( const char* consumer, VpnEventHandlers* events );
/// @brief Получение информации о ключевом дистрибутиве (DST)
/// @param[in] path - путь к файлу DST
/// @param[out] info - информация о ключевом дистрибутиве
/// @return Результат выполнения команды
typedef VpnApiReturnCode ( *GetDstInfo )( const char* path, VpnDstInfo* info );
/// @brief Развернуть файл ключевого дистрибутива (DST) и настроить службу для доступа в ViPNet
/// @param[in] path - путь к файлу DST
/// @param[in] password - пароль пользователя узла (может быть пуст или NULL)
/// @param[out] status - результат проверки DST и статус установки
/// @return Результат выполнения команды
typedef VpnApiReturnCode ( *InstallKeysExt )( const char* path, const char* password, VpnDstStatus* status );
/// @brief Получить данные о состоянии и режиме работы службы vipnetclient
/// @param[out] status - запрашиваемые данные
/// @return Результат выполнения команды
typedef VpnApiReturnCode ( *GetUltimateStatus )( VpnUltimateStatus* status );
/// @brief Выполнить дигностику работоспособности службы vipnetclient
/// @param[out] status - результат проверки
/// @return Результат выполнения команды
typedef VpnApiReturnCode ( *GetHealthStatus )( VpnHealthStatus* status );
/// @brief Включить опцию автозапуска при логине пользователя ОС
/// @return Результат выполнения команды
typedef VpnApiReturnCode ( *SetOptionAutorun )( void );
/// @brief Выключить опцию автозапуска при логине пользователя ОС
/// @return Результат выполнения команды
typedef VpnApiReturnCode ( *SetOptionNoAutorun )( void );
/*! @} */ // api_ver6
/*! \defgroup api_ver7 Функции SDK для версии 7 и выше
    @{
*/
/// @brief Включить опцию восстановления при сбоях
/// @return Результат выполнения команды
/// @remark Настройка хранится в памяти процесса, использующего API, и после перезапуска теряется
/// @remark По-умолчанию, система восстановления активируется при старте VPN
typedef VpnApiReturnCode ( *SetOptionFaultTolerance )( void );
/// @brief Выключить опцию восстановления при сбоях
/// @return Результат выполнения команды
/// @remark Настройка хранится в памяти процесса, использующего API, и после перезапуска теряется
/// @remark По-умолчанию, система восстановления активируется при старте VPN
typedef VpnApiReturnCode ( *SetOptionNoFaultTolerance )( void );
/*! @} */
/*! \defgroup api_ver8 Функции SDK для версии 8 и выше
    @{
*/
/// @brief Получить id запущенного ViPNet-соединения (Возможность множества соединений для Аврора ОС)
/// @param[out] conn - id запущенного ViPNet-соединения
/// @return Результат выполнения команды
typedef VpnApiReturnCode ( *GetConnectionId )( int32_t* conn );
/// @brief Получить id запущенного ViPNet-соединения через DBus-RPC call (Возможность множества соединений для Аврора ОС)
/// @param[out] conn - id запущенного ViPNet-соединения
/// @return Результат выполнения команды
typedef VpnApiReturnCode ( *GetConnectionIdRoutine )( int32_t* conn );
/// @brief Получить информацию о шифраторе заданного узла
/// @param[in] id - идентификатор узла
/// @param[in] keyType - указатель на строку с json-структурой в utf8
/// @remarks Строка с результатом должна быть особождена после использования с помощью вызова ReleaseNodeCrypto
/// @details Возвращаемая структура имеет следующий формат:
/**
* \code{.json}
*{
*  "keyType": "<Тип ключа: Default, Quantum, etc>",
*  "keyStartDate": "<Время начала действия квантового ключа (time_t)>"
*}
* \endcode
*/
typedef VpnApiReturnCode ( *GetNodeCrypto )( uint32_t id, char** keyType );
/// @brief Осовободить память, выделенную при вызове GetNodeCrypto
/// @param[in] keyType - указатель на строку, возвращённую GetNodeCrypto
typedef VpnApiReturnCode ( *ReleaseNodeCrypto )( char* keyType );
/*! @} */

/// @brief Функции VPN API (описание функций выше)
typedef struct
{
#ifdef FULL_API
    InstallKeys installKeys;
    DeleteKeys deleteKeys;
    StartVpn startVpn;
    StopVpn stopVpn;
    RegulationsCheck regulationsCheck;
    CollectReport collectReport;
    CollectEventlog collectEventlog;
#endif
    SubscribeForEvents subscribeForEvents;

    GetClientVersion getClientVersion;
    GetVpnStatus getVpnStatus;
    GetLicenseExpiration getLicenseExpiration;
    GetNodesIds getNodesIds;
    GetNodesInfo getNodesInfo;
    GetNodesInfoByTask getNodesInfoByMask;
    GetNodeInfo getNodeInfo;
    GetOwnNodeInfo getOwnNodeInfo;
    GetOwnPrivilegeLevel getOwnPrivilegeLevel;
    ReleaseVpnNodeInfo releaseVpnNodeInfo;
    ReleaseVpnNodeIds releaseVpnNodeIds;
    GetActiveCoordinator getActiveCoordinator;
    GetLogLevel getLogLevel;
    SetLogLevel setLogLevel;

    // API_VERSION_3
    GetUsersInfo getUsersInfo;
    GetUserInfo getUserInfo;
    GetClientParam getClientParam;
    ReleaseVpnUserInfo releaseVpnUserInfo;
    ReleaseClientParamData releaseClientParamData;
    GetUserIdsForNode getUserListForNode;
    ReleaseVpnUserIds releaseVpnUserIds;

    // API_VERSION_4
    CollectTokens collectTokens;
    SetToken setToken;
    GetTokenInfo getTokenInfo;
    ReleaseTokenHandle releaseTokenHandle;
    GetTokenConfig getTokenConfig;
    MoveKeysToToken moveKeysToToken;

    // API_VERSION 5
#ifdef FULL_API
    SubscribeForEventsExt subscribeForEventsExt;
    UnsubscribeFromEventsExt unsubscribeFromEventsExt;

    // API_VERSION 6
    SetPin setPin;
    GetNodeInfoExt getNodeInfoExt;
    ReleaseNodeInfoExt releaseNodeInfoExt;
    CheckActiveCoordinatorConnection checkActiveCoordinatorConnection;
    SetActiveCoordinatorId changeActiveCoordinator;
    CheckNodeConnection checkNodeConnection;
    SetEventHandlers setEventHandlers;
    GetDstInfo getDstInfo;
    InstallKeysExt installKeysExt;
    GetUltimateStatus getUltimateStatus;
    GetHealthStatus getHealthStatus;
    SetOptionAutorun setOptionAutorun;
    SetOptionNoAutorun setOptionNoAutorun;
    SetExchangeFolder setExchangeFolder;
    // API_VERSION 7
    SetOptionFaultTolerance setOptionFaultTolerance;
    SetOptionNoFaultTolerance setOptionNoFaultTolerance;
    // API_VERSION 8
    GetConnectionId getConnectionId;
    GetConnectionIdRoutine getConnectionIdRoutine;
    GetNodeCrypto getNodeCrypto;
    ReleaseNodeCrypto releaseNodeCrypto;
#endif
} ItcsVpnApi;

#ifdef __cplusplus
extern "C" {
#endif

/// @brief Получить текущую версию VPN API
/// @remarks При обновлении версии API рекомендуется проверить совместимость, иначе работоспособность не гарантируется
/// @return Текущая версия VPN API
VpnApiExport uint32_t GetVpnApiVersion();

/// @brief Получить указатель на структуру VPN API
/// @return Указатель на структуру VPN API
VpnApiExport ItcsVpnApi* GetVpnApi();

/// @brief Получить указатель на структуру VPN API
/// @param[in] params - параметры инициализации
/// @param[in] len -  количество параметров
/// @return Указатель на структуру VPN API
VpnApiExport ItcsVpnApi* GetVpnApiExt( VpnApiInitData* params, size_t len );

#ifdef __cplusplus
}
#endif
