#include <stdio.h>
#include <dlist.h>
#include <graph.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>

struct graph
{
    dlist *nodes; // The graph nodes are stored in a directed list
    int max_nodes;
    // int connected;
};


struct node
{
    void *value;
    bool seen; 
    dlist *edge;
};




// ===========INTERNAL FUNCTION IMPLEMENTATIONS ============

/**
 * node_create() - Allocate and populate a graph entry.
 * @key: A pointer to a function to be used to compare keys.
 * @value: A pointer to a function (or NULL) to be called to
 *                 de-allocate memory for keys on remove/kill.
 *
 * Returns: A pointer to the newly created graph entry.
 */
node *node_create(const void *value, int max_node)
{
    // Allocate space for a graph entry. Use calloc as a defensive
    // measure to ensure that all pointers are initialized to NULL.
    node *e = calloc(1, sizeof(*e));
    // Populate the entry.
    e->value = value;
    e->seen = false;
    e->edge = dlist_empty(NULL); // lista med tillhörande edges
    return e;
}

/**
 * node_kill() - Return the memory allocated to a graph entry.
 * @e: The graph entry to deallocate.
 *
 * Returns: Nothing.
 */
void node_kill(void *v)
{
    node *e = v; // Convert the pointer (useful if debugging the code)

    // All we need to do is to deallocate the struct.
    free(e);
}

/**
 * graph_empty() - Create an empty graph.
 * @key_cmp_func: A pointer to a function to be used to compare keys.
 * @key_kill_func: A pointer to a function (or NULL) to be called to
 *                 de-allocate memory for keys on remove/kill.
 * @value_kill_func: A pointer to a function (or NULL) to be called to
 *                   de-allocate memory for values on remove/kill.
 *
 * Returns: Pointer to a new graph.
 */
graph *graph_empty(int max_nodes)
{
    // Allocate the graph header.
    graph *g = calloc(1, sizeof(graph));
    // Create the list to hold the node-ies.
    g->nodes = dlist_empty(NULL);
    g->max_nodes = max_nodes;
    // Store the key compare function and key/value kill functions.

    return g;
}

/**
 * graph_is_empty() - Check if a graph is empty.
 * @graph: graph to check.
 *
 * Returns: True if graph contains no entries, false otherwise.
 */
bool graph_is_empty(const graph *g)
{
    return dlist_is_empty(g->nodes);
}


graph *graph_insert_node(graph *g, const char *s)
{
    node *e = node_create(s, g->max_nodes);
    //e->seen = false;
    
    dlist_insert(g->nodes, e, dlist_first(g->nodes));
    return g;
}

bool nodes_are_equal(const node *n1, const node *n2)
{
    if (n1 == NULL || n2 == NULL)
    {
        return n1 == n2;
    }
    return strcmp(n1->value, n2->value) == 0;
}

bool graph_has_edges(const graph *g)
{
    dlist_pos pos = dlist_first(g->nodes);

    while (!dlist_is_end(g->nodes, pos))
    {
        node *e = dlist_inspect(g->nodes, pos);
        if (!dlist_is_empty(e->edge)) 
        {
            return true;
        }
        pos = dlist_next(g->nodes, pos);
    }
    return false;
}

node *graph_find_node(const graph *g, const char *s)
{
    dlist_pos pos = dlist_first(g->nodes);

    while (!dlist_is_end(g->nodes, pos))
    {
        node *e = dlist_inspect(g->nodes, pos);
        if (strcmp(e->value, s) == 0 && !e->edge)
        {
            return e;
        }
        pos = dlist_next(g->nodes, pos);
    }
    return NULL;
}

bool graph_node_is_seen(const graph *g, const node *n)
{
    dlist_pos pos = dlist_first(g->nodes);

    while (!dlist_is_end(g->nodes, pos))
    {
        node *e = dlist_inspect(g->nodes, pos);
        if (nodes_are_equal(e, n) && e->edge == false)
        {
            return e->seen;
        }
        pos = dlist_next(g->nodes, pos);
    }
    return false;
}

graph *graph_node_set_seen(graph *g, node *n, bool seen)
{
    dlist_pos pos = dlist_first(g->nodes);

    while (!dlist_is_end(g->nodes, pos))
    {
        node *e = dlist_inspect(g->nodes, pos);
        if (nodes_are_equal(e, n) && e->edge == false)
        {
            e->seen = seen;
            break;
        }
        pos = dlist_next(g->nodes, pos);
    }
    return g;
}

graph *graph_reset_seen(graph *g)
{
    dlist_pos pos = dlist_first(g->nodes);

    while (!dlist_is_end(g->nodes, pos))
    {
        node *e = dlist_inspect(g->nodes, pos);
        if (e->seen == true)
        {
            e->seen = false;
        }
        pos = dlist_next(g->nodes, pos);
    }
    return g;
}

graph *graph_insert_edge(graph *g, node *n1, node *n2)
{
    dlist_insert(n1->edge, n2, dlist_first(n1->edge));
    return g;
}



dlist *graph_neighbours(const graph *g,const node *n)
{
    return n->edge;
}



void graph_print(const graph *g)
{
    dlist_pos pos = dlist_first(g->nodes);
    while (!dlist_is_end(g->nodes, pos))
    {
        node *e = dlist_inspect(g->nodes, pos);
        printf("%s to nodes: ", (char *)e->value);
        dlist_pos pos_edge = dlist_first(e->edge);
        while (!dlist_is_end(e->edge, pos_edge))
        {
            node *edges = dlist_inspect(e->edge,pos_edge);
            printf("%s,", (char *)edges->value);
            pos_edge = dlist_next(e->edge, pos_edge);
        }
        printf("\n");
    
        pos = dlist_next(g->nodes, pos);
    }
}
