/*
 * Copyright (C) 2016-2024 Canonical, Ltd.
 * Author: Martin Pitt <martin.pitt@ubuntu.com>
 * Author: Lukas Märdian <slyon@ubuntu.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 3.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/**
 * @file util.h
 * @brief A set of generic helper functions that can be used when working with
 *        Netplan data structures. Such as handling of @ref NetplanError data.
 */

#pragma once

#include <glib.h>
#include <stdint.h>
#include "types.h"

/**
 * @brief Parses YAML hierarchy from @p rootdir, drops the configuration for @p id
 *        from the @ref NetplanState and re-generates the YAML files.
 * @param[in] id      The Netplan ID for a specific configuration block of network interface(s)
 * @param[in] rootdir If not `NULL`, parse configuration from this root directory (useful for testing)
 * @return            Indication of success or failure
 */
NETPLAN_PUBLIC gboolean
netplan_delete_connection(const char* id, const char* rootdir);

/**
 * @brief   Extract the Netplan ID from the filepath of a NetworkManager keyfile.
 * @details Copies a `NUL`-terminated string into a sized @p out_buffer. If the
 *          buffer is too small, its content is not `NUL`-terminated.
 * @note    This can be applied to NetworkManager connection profiles generated by Netplan, following a `netplan-ID[-SSID].nmconnection` naming scheme, as used by the NetworkManager YAML backend.
 * @param[in]  filename     Full path to a NetworkManager keyfile
 * @param[in]  ssid         Wi-Fi SSID of this connection profile, or `NULL`
 * @param[out] out_buffer   A pre-allocated buffer to write the output string into, owned by the caller
 * @param[in]  out_buf_size The maximum size (in bytes) available for @p out_buffer
 * @return                  The size of the copied string, including the final `NUL` character.
 *                          If the buffer is too small, returns @ref NETPLAN_BUFFER_TOO_SMALL instead.
 */
NETPLAN_PUBLIC ssize_t
netplan_get_id_from_nm_filepath(const char* filename, const char* ssid, char* out_buffer, size_t out_buf_size);

/**
 * @brief   Free a @ref NetplanError, including any dynamically allocated data.
 * @details Free @p error and set `*error` to `NULL`.
 * @param[out] error The @ref NetplanError to free and nullify
 */
NETPLAN_PUBLIC void
netplan_error_clear(NetplanError** error);

/**
 * @brief   Get the human readable error message of a @ref NetplanError.
 * @details Copies a `NUL`-terminated string into a sized @p out_buffer. If the
 *          buffer is too small, its content is not `NUL`-terminated.
 * @param[in]  error    The @ref NetplanError to query
 * @param[out] buf      A pre-allocated buffer to write the output string into, owned by the caller
 * @param[in]  buf_size The maximum size (in bytes) available for @p out_buffer
 * @return              The size of the copied string, including the final `NUL` character.
 *                      If the buffer is too small, returns @ref NETPLAN_BUFFER_TOO_SMALL instead.
 */
NETPLAN_PUBLIC ssize_t
netplan_error_message(NetplanError* error, char* buf, size_t buf_size);

/**
 * @brief   Returns a `u64` value containing both the @ref NETPLAN_ERROR_DOMAINS
 *          and specific error code of that domain.
 * @details The two values are concatenated, so relevant data can be masked: `(u32)domain | (u32)code`
 *
 * Example:
 *
 *          domain = error >> 32  # upper 32 bits
 *          code = (uint32_t) error  # lower 32 bits
 * @note    Error codes can be of enumeration type @ref NETPLAN_PARSER_ERRORS, @ref NETPLAN_VALIDATION_ERRORS, @ref NETPLAN_BACKEND_ERRORS, etc.
 * @return  A `u64` value containing concatenated @ref NetplanError domain and a specific error code
 */
NETPLAN_PUBLIC uint64_t
netplan_error_code(NetplanError* error);

/**
 * @brief   Create a YAML document from a `netplan-set expression`.
 * @details A `set expression` here consists of a path formed of `TAB`-separated
 *          keys, indicating where in the YAML file we want to make our changes,
 *          and a valid YAML expression that is the payload to insert at
 *          that place. The result is a well-formed YAML document.
 * Example:
 *
 *          # netplan set "network.ethernets.eth0={dhcp4: true}"
 *          conf_obj_path = "network\tethernets\teth0"
 *          obj_payload = "{dhcp4: true}"
 * @param[in]     conf_obj_path A `TAB`-separated YAML path
 * @param[in]     obj_payload YAML expression
 * @param[in,out] out_fd A file descriptor referencing the output file to contain the resulting YAML document
 * @param[out]    error  Filled with a @ref NetplanError in case of failure
 * @return               Indication of success or failure
 */
NETPLAN_PUBLIC gboolean
netplan_util_create_yaml_patch(const char* conf_obj_path, const char* obj_payload, int out_fd, NetplanError** error);

/**
 * @brief   Get a subset of a YAML document given in @p input_fd.
 * @details A `set expression` here consists of a path formed of `TAB`-separated
 *          keys, indicating where in the YAML doc we want to make our changes,
 *          and a valid YAML expression that is the payload to insert at
 *          that place. The result is a well-formed YAML document.
 * Example:
 *
 *          # netplan get "network.ethernets.eth0"
 *          prefix = "network\tethernets\teth0"
 * @param[in]     prefix    A `TAB`-separated YAML path
 * @param[in]     input_fd  A file descriptor, referencing the input YAML file
 * @param[in,out] output_fd A file descriptor, referencing the output file to contain the resulting subset of the input YAML file
 * @param[out]    error     Filled with a @ref NetplanError in case of failure
 * @return                  Indication of success or failure
 */
NETPLAN_PUBLIC gboolean
netplan_util_dump_yaml_subtree(const char* prefix, int input_fd, int output_fd, NetplanError** error);
