

/**
 * @brief Структура, описывающая элемент списка 
 */


TYPE HSAL_LL_Node:
STRUCT
    item      : REF_TO VOID;   (* void* ->  REF_TO VOID *)
    itemSize  : USIZE;             (* size_t -> USIZE (64-bit) *)
    prev      : REF_TO HSAL_LL_Node;
    next      : REF_TO HSAL_LL_Node;
END_STRUCT
END_TYPE

@EXTERNAL FUNCTION typedef_HSAL_FuncPtr_Destructor :  DINT
VAR_INPUT
    object      : REF_TO VOID;  
END_VAR
END_FUNCTION

/**
 * @brief Структура, описывающая объект списка 
 */
TYPE HSAL_LL_List:
STRUCT
    head      : REF_TO HSAL_LL_Node;
    tail      : REF_TO HSAL_LL_Node;
    items     : UDINT;
    max_items : UDINT;
    mem       : REF_TO VOID;   (* static memory block for nodes *)
    destr     : REF_TO typedef_HSAL_FuncPtr_Destructor;  (* callback: DINT func( REF_TO VOID) *)
END_STRUCT
END_TYPE


/**
 * @brief Создать и инициализировать новый список
 * @param [in] list Существующий объект списка для инициализации, или NULL для создания нового 
 * @param [in] max_items Максимальное количество элементов в списке
 * @param [in] mem Статическая память, где требуется разместить список (для embedded реализаций) 
 * @param [in] destr Коллбек для корректного удаления элемента конкретного типа из списка (или NULL если не нужен)
 * @return Новый объект списка или NULL в случае неудачи
 */
@EXTERNAL FUNCTION hsal_ll_list_create : REF_TO HSAL_LL_List
VAR_INPUT
    list      : REF_TO HSAL_LL_List;  (* NULL = allocate new *)
    max_items : UDINT;
    mem       :  REF_TO VOID;          (* static buffer, or NULL *)
    destr     : REF_TO typedef_HSAL_FuncPtr_Destructor;  (* destructor callback, or 0 *)
END_VAR

END_FUNCTION

/**
 * @brief Удалить список со всем содержимым
 * @return 0 в случае успеха, EINVAL - списка не существует
 * @note Является "деструктором" для списков созданных динамически 
 */
@EXTERNAL FUNCTION hsal_ll_list_destroy : DINT
VAR_INPUT
    _this : REF_TO HSAL_LL_List;
END_VAR

END_FUNCTION

/**
 * @brief Вставить элемент в произвольную позицию
 * @param [in] _this Указатель на объект списка
 * @param [in] item Новые данные для помещения в список
 * @param [in] itemSize Размер данных
 * @param [in] position Индекс нового добавленного в список элемента
 * @return 0 в случае успеха, EINVAL - список не существует, ENOMEM - список переполнен или невозможно вставить, EFAULT - неверная позиция
 */
@EXTERNAL FUNCTION hsal_ll_list_insert : DINT
VAR_INPUT
    _this    : REF_TO HSAL_LL_List;
    item     :  REF_TO VOID;
    itemSize : UDINT;
    pos      : DINT;   (* position index, 0-based *)
END_VAR

END_FUNCTION

/**
 * @brief Вставить элемент в конец списка
 * @param [in] _this Указатель на объект списка
 * @param [in] item Новые данные для помещения в список
 * @param [in] itemSize Размер данных
 * @return 0 в случае успеха, EINVAL - список не существует, ENOMEM - список переполнен или невозможно вставить 
 */
@EXTERNAL FUNCTION hsal_ll_list_push_back : DINT
VAR_INPUT
    _this    : REF_TO HSAL_LL_List;
    item     :  REF_TO VOID;
    itemSize : UDINT;
END_VAR
END_FUNCTION


/**
 * @brief Вставить элемент в начало списка
 * @param [in] _this Указатель на объект списка
 * @param [in] item Новые данные для помещения в список
 * @param [in] itemSize Размер данных
 * @return 0 в случае успеха, EINVAL - список не существует, ENOMEM - список переполнен или невозможно вставить 
 */
@EXTERNAL FUNCTION hsal_ll_list_push_front : DINT
VAR_INPUT
    _this    : REF_TO HSAL_LL_List;
    item     :  REF_TO VOID;
    itemSize : UDINT;
END_VAR
END_FUNCTION

/**
 * @brief Получить указатель на данные из элемента списка по индексу, не удаляя их
 * @param [in] _this Указатель на объект списка
 * @param [in] pos Индекс элемента для извлечения данных
 * @return Указатель на item или NULL в случае ошибки
 */
@EXTERNAL FUNCTION hsal_ll_list_get :  REF_TO VOID
VAR_INPUT
    _this : REF_TO HSAL_LL_List;
    pos   : DINT;
END_VAR

END_FUNCTION

/**
 * @brief Получить указатель на данные из элемента списка по индексу, не удаляя их
 * @param [in] _this Указатель на объект списка
 * @param [in] pos Индекс элемента для извлечения данных
 * @return Указатель на item или NULL в случае ошибки
 */
@EXTERNAL FUNCTION hsal_ll_list_front :  REF_TO VOID
VAR_INPUT
    _this : REF_TO HSAL_LL_List;
END_VAR
END_FUNCTION

/**
 * @brief Получить указатель на данные из последнего элемента списка, не удаляя их 
 * @param [in] _this Указатель на объект списка
 * @return Указатель на item, или NULL в случае ошибки
 */
@EXTERNAL FUNCTION hsal_ll_list_back :  REF_TO VOID
VAR_INPUT
    _this : REF_TO HSAL_LL_List;
END_VAR
END_FUNCTION

/**
 * @brief Получить указатель на данные из последнего элемента списка, не удаляя их 
 * @param [in] _this Указатель на объект списка
 * @return Указатель на item, или NULL в случае ошибки
 */
@EXTERNAL FUNCTION hsal_ll_list_get_and_remove : DINT
VAR_INPUT
    _this : REF_TO HSAL_LL_List;
    pos   : DINT;
END_VAR
VAR_OUTPUT
    item  :  REF_TO VOID;  (* buffer to COPY data into *)
END_VAR

END_FUNCTION

/**
 * @brief Скопировать данные из первого элемента списка в заданный буфер, и удалить их 
 * @param [in] _this Указатель на объект списка
 * @param [out] item Буфер для данных 
 * @return 0 в случае успеха, или если список уже пуст, EINVAL - список не существует
 */
@EXTERNAL FUNCTION hsal_ll_list_pop_front : DINT
VAR_INPUT
    _this : REF_TO HSAL_LL_List;
END_VAR
VAR_OUTPUT
    item  :  REF_TO VOID;
END_VAR
END_FUNCTION

/**
 * @brief Скопировать данные из последнего элемента списка в заданный буфер, и удалить их 
 * @param [in] _this Указатель на объект списка
 * @param [out] item Буфер для данных 
 * @return 0 в случае успеха, или если список уже пуст, EINVAL - список не существует
 */
@EXTERNAL FUNCTION hsal_ll_list_pop_back : DINT
VAR_INPUT
    _this : REF_TO HSAL_LL_List;
END_VAR	
VAR_OUTPUT
    item  :  REF_TO VOID;
END_VAR
END_FUNCTION


/**
 * @brief Получить текущее количество элементов в списке
 * @param [in] _this Указатель на объект списка
 * @return Текущее количество элементов 
 */
@EXTERNAL FUNCTION hsal_ll_list_size : UDINT
VAR_INPUT
    _this : REF_TO HSAL_LL_List;
END_VAR
END_FUNCTION

/**
 * @brief Проверить, пуст ли список
 * @param [in] _this Указатель на объект списка
 * @return true - список пустой, false - не пустой 
 */
@EXTERNAL FUNCTION hsal_ll_list_is_empty : BOOL
VAR_INPUT
    _this : REF_TO HSAL_LL_List;
END_VAR
END_FUNCTION

/**
 * @brief Проверить, пуст ли список
 * @param [in] _this Указатель на объект списка
 * @return true - список пустой, false - не пустой 
 */
@EXTERNAL FUNCTION hsal_ll_list_is_full : BOOL
VAR_INPUT
    _this : REF_TO HSAL_LL_List;
END_VAR
END_FUNCTION

/**
 * @brief Получить максимальную емкость списка
 * @param [in] _this Указатель на объект списка
 * @return Максимальная емкость списка 
 */
@EXTERNAL FUNCTION hsal_ll_list_max_size : UDINT
VAR_INPUT
    _this : REF_TO HSAL_LL_List;
END_VAR
END_FUNCTION

/**
 * @brief Очистить список от всех существующих элементов 
 * @param [in] _this Указатель на объект списка
 * @return 0 - успех, EINVAL - списка не существует
 * @note Является "деструктором" для списков созданных статически 
 */
@EXTERNAL FUNCTION hsal_ll_list_clear : DINT
VAR_INPUT
    _this : REF_TO HSAL_LL_List;
END_VAR
END_FUNCTION



/*
VAR_GLOBAL CONSTANT
    EINVAL : DINT := 22;   (* Invalid argument *)
    ENOMEM : DINT := 12;   (* Out of memory *)
    EFAULT : DINT := 14;   (* Bad address *)
END_VAR
*/