/*
 * libqos driver framework
 *
 * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License version 2.1 as published by the Free Software Foundation.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, see <http://www.gnu.org/licenses/>
 */

#ifndef QGRAPH_INTERNAL_H
#define QGRAPH_INTERNAL_H

/* This header is declaring additional helper functions defined in
 * qgraph.c
 * It should not be included in tests
 */

#include "qgraph.h"

typedef struct QOSGraphMachine QOSGraphMachine;
typedef enum QOSEdgeType QOSEdgeType;
typedef enum QOSNodeType QOSNodeType;

/* callback called when the walk path algorithm found a
 * valid path
 */
typedef void (*QOSTestCallback) (QOSGraphNode *path, int len);

/* edge types*/
enum QOSEdgeType {
    QEDGE_CONTAINS,
    QEDGE_PRODUCES,
    QEDGE_CONSUMED_BY
};

/* node types*/
enum QOSNodeType {
    QNODE_MACHINE,
    QNODE_DRIVER,
    QNODE_INTERFACE,
    QNODE_TEST
};

/* Graph Node */
struct QOSGraphNode {
    QOSNodeType type;
    bool available;     /* set by QEMU via QMP, used during graph walk */
    bool visited;       /* used during graph walk */
    char *name;         /* used to identify the node */
    char *qemu_name;    /* optional: see qos_node_create_driver_named() */
    char *command_line; /* used to start QEMU at test execution */
    union {
        struct {
            QOSCreateDriverFunc constructor;
        } driver;
        struct {
            QOSCreateMachineFunc constructor;
        } machine;
        struct {
            QOSTestFunc function;
            void *arg;
            QOSBeforeTest before;
            bool subprocess;
        } test;
    } u;

    /**
     * only used when traversing the path, never rely on that except in the
     * qos_traverse_graph callback function
     */
    QOSGraphEdge *path_edge;
};

/**
 * qos_graph_get_node(): returns the node mapped to that @key.
 * It performs an hash map search O(1)
 *
 * Returns: on success: the %QOSGraphNode
 *          otherwise: #NULL
 */
QOSGraphNode *qos_graph_get_node(const char *key);

/**
 * qos_graph_has_node(): returns #TRUE if the node
 * has map has a node mapped to that @key.
 */
bool qos_graph_has_node(const char *node);

/**
 * qos_graph_get_node_type(): returns the %QOSNodeType
 * of the node @node.
 * It performs an hash map search O(1)
 * Returns: on success: the %QOSNodeType
 *          otherwise: #-1
 */
QOSNodeType qos_graph_get_node_type(const char *node);

/**
 * qos_graph_get_node_availability(): returns the availability (boolean)
 * of the node @node.
 */
bool qos_graph_get_node_availability(const char *node);

/**
 * qos_graph_get_edge(): returns the edge
 * linking of the node @node with @dest.
 *
 * Returns: on success: the %QOSGraphEdge
 *          otherwise: #NULL
 */
QOSGraphEdge *qos_graph_get_edge(const char *node, const char *dest);

/**
 * qos_graph_edge_get_type(): returns the edge type
 * of the edge @edge.
 *
 * Returns: on success: the %QOSEdgeType
 *          otherwise: #-1
 */
QOSEdgeType qos_graph_edge_get_type(QOSGraphEdge *edge);

/**
 * qos_graph_edge_get_dest(): returns the name of the node
 * pointed as destination of edge @edge.
 *
 * Returns: on success: the destination
 *          otherwise: #NULL
 */
char *qos_graph_edge_get_dest(QOSGraphEdge *edge);

/**
 * qos_graph_has_edge(): returns #TRUE if there
 * exists an edge from @start to @dest.
 */
bool qos_graph_has_edge(const char *start, const char *dest);

/**
 * qos_graph_edge_get_arg(): returns the args assigned
 * to that @edge.
 *
 * Returns: on success: the arg
 *          otherwise: #NULL
 */
void *qos_graph_edge_get_arg(QOSGraphEdge *edge);

/**
 * qos_graph_edge_get_after_cmd_line(): returns the edge
 * command line that will be added after all the node arguments
 * and all the before_cmd_line arguments.
 *
 * Returns: on success: the char* arg
 *          otherwise: #NULL
 */
char *qos_graph_edge_get_after_cmd_line(QOSGraphEdge *edge);

/**
 * qos_graph_edge_get_before_cmd_line(): returns the edge
 * command line that will be added before the node command
 * line argument.
 *
 * Returns: on success: the char* arg
 *          otherwise: #NULL
 */
char *qos_graph_edge_get_before_cmd_line(QOSGraphEdge *edge);

/**
 * qos_graph_edge_get_extra_device_opts(): returns the arg
 * command line that will be added to the node command
 * line argument.
 *
 * Returns: on success: the char* arg
 *          otherwise: #NULL
 */
char *qos_graph_edge_get_extra_device_opts(QOSGraphEdge *edge);

/**
 * qos_graph_edge_get_name(): returns the name
 * assigned to the destination node (different only)
 * if there are multiple devices with the same node name
 * e.g. a node has two "generic-sdhci", "emmc" and "sdcard"
 * there will be two edges with edge_name ="emmc" and "sdcard"
 *
 * Returns always the char* edge_name
 */
char *qos_graph_edge_get_name(QOSGraphEdge *edge);

/**
 * qos_graph_get_machine(): returns the machine assigned
 * to that @node name.
 *
 * It performs a search only trough the list of machines
 * (i.e. the QOS_ROOT child).
 *
 * Returns: on success: the %QOSGraphNode
 *          otherwise: #NULL
 */
QOSGraphNode *qos_graph_get_machine(const char *node);

/**
 * qos_graph_has_machine(): returns #TRUE if the node
 * has map has a node mapped to that @node.
 */
bool qos_graph_has_machine(const char *node);


/**
 * qos_print_graph(): walks the graph and prints
 * all machine-to-test paths.
 */
void qos_print_graph(void);

/**
 * qos_graph_foreach_test_path(): executes the Depth First search
 * algorithm and applies @fn to all discovered paths.
 *
 * See qos_traverse_graph() in qgraph.c for more info on
 * how it works.
 */
void qos_graph_foreach_test_path(QOSTestCallback fn);

/**
 * qos_get_machine_type(): return QEMU machine type for a machine node.
 * This function requires every machine @name to be in the form
 * <arch>/<machine_name>, like "arm/raspi2b" or "x86_64/pc".
 *
 * The function will validate the format and return a pointer to
 * @machine to <machine_name>.  For example, when passed "x86_64/pc"
 * it will return "pc".
 *
 * Note that this function *does not* allocate any new string.
 */
char *qos_get_machine_type(char *name);

/**
 * qos_delete_cmd_line(): delete the
 * command line present in node mapped with key @name.
 *
 * This function is called when the QMP query returns a node with
 * { "abstract" : true } attribute.
 */
void qos_delete_cmd_line(const char *name);

/**
 * qos_graph_node_set_availability(): sets the node identified
 * by @node with availability @av.
 */
void qos_graph_node_set_availability(const char *node, bool av);

/*
 * Prepends a '#' character in front for not breaking TAP output format.
 */
#define qos_printf(...) printf("# " __VA_ARGS__)

/*
 * Intended for printing something literally, i.e. for appending text as is
 * to a line already been started by qos_printf() before.
 */
#define qos_printf_literal printf

#endif
