#include <sys/resource.h>
#include <sys/types.h>
#include <sys/stat.h>

#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <time.h>

#include <btc/block.h>
#include <btc/net.h>
#include <btc/utils.h>
#include <btc/serialize.h>
#include <btc/memory.h>
#include <btc/tx.h>

#include <daemonize.h>

/*
 * Les variables globales
 */
char* simple_db_path = "data/simpledb";
FILE* blockchain_fd = NULL;

/*
 * Les outils
 */
static void signal_handler(int sig){
    switch(sig){
    case SIGUSR1:
        // signal USR1
        break;
    case SIGUSR2:
        // Signal USR2
        break;
    case SIGHUP:
        // Reload conf and reinitialize processus
        break;
    case SIGINT:
        // CTRL+C
    case SIGTERM:
        // Terminate : kill -15
        exit(0);
        break;
    }
}

static btc_bool file_exist(const char *filename)
{
  struct stat   buffer;   
  return (stat (filename, &buffer) == 0);
}

/*
 * Les fonctions spécfiques
 */
void request_header_or_block_from_hashbin(btc_node *node, uint256* hash, btc_bool blocks)
{
    if (node == NULL) return;
    if (hash == NULL) return;

    // request next headers
    vector*  blocklocators = vector_new(1, NULL);
    cstring* getheader_msg = cstr_new_sz(256);
    cstring* p2p_msg = NULL;

    vector_add(blocklocators, (void *)hash);

    btc_p2p_msg_getheaders(blocklocators, NULL, getheader_msg);

    /* create p2p message */
    p2p_msg = btc_p2p_message_new(node->nodegroup->chainparams->netmagic, (blocks ? BTC_MSG_GETBLOCKS : BTC_MSG_GETHEADERS), getheader_msg->str, getheader_msg->len);

    /* send message */
    btc_node_send(node, p2p_msg);
    node->state |= ( blocks ? NODE_BLOCKSYNC : NODE_HEADERSYNC);

    /* remember last headers request time */
    if (blocks) {
        node->time_last_request = time(NULL);
    }

    /* cleanup */
    vector_free(blocklocators, true);
    cstr_free(p2p_msg, true);
    cstr_free(getheader_msg, true);
}

void request_header_or_block_from_hashhex(btc_node *node, const char* hex, btc_bool blocks)
{
    if (node == NULL) return;
    if (hex == NULL) return;

    uint256 *hash = btc_calloc(1, sizeof(uint256));
    if (hash == NULL) return;
    utils_uint256_sethex((char *)hex, (uint8_t *)hash);
    request_header_or_block_from_hashbin(node,hash,blocks);
}

uint256  request_headers_or_blocks_hash;
btc_bool request_headers_or_blocks_hash_init = false;
void request_headers_or_blocks(btc_node *node, btc_bool blocks)
{
    /* Sans initialisation, on repart du Genesis Block */
    if (!request_headers_or_blocks_hash_init)
    {
        node->nodegroup->log_write_cb("STATUS : Setting requested hash with genesis block\n");
        memcpy(&request_headers_or_blocks_hash, node->nodegroup->chainparams->genesisblockhash, sizeof(uint256));
        request_headers_or_blocks_hash_init = true;
    }

    // On lance la recherche la synchro
    if (request_headers_or_blocks_hash_init)
        request_header_or_block_from_hashbin(node, &request_headers_or_blocks_hash, blocks);
}

static void find_and_add_nodes(btc_node_group* group)
{
    const btc_dns_seed seed = btc_chainparams_main.dnsseeds[0];
    vector* ips;

    /* collect and add some btc nodes to the group */
    ips = vector_new(group->desired_amount_connected_nodes + 5, free);
    btc_get_peers_from_dns(seed.domain, ips, btc_chainparams_main.default_port, AF_INET);
    for (unsigned int i = 0; i<ips->len; i++)
    {
        /* create a node */
        btc_node *node = btc_node_new();
        char *ip = (char *)vector_idx(ips, i);
        btc_node_set_ipport(node, ip);
        btc_node_group_add_node(group, node);
    }
    vector_free(ips, true);

    /* connect to the next node */
    btc_node_group_connect_next_nodes(group);
}

static void check_connected_nodes(btc_node_group* group)
{
    int nb_connected = btc_node_group_amount_of_connected_nodes(group, NODE_CONNECTED);
    if (nb_connected < group->desired_amount_connected_nodes)
    {
        group->log_write_cb("STATUS : new connection needed (%d node connected)\n", nb_connected);
        find_and_add_nodes(group);
    }
}

/*
 * Les callbacks
 */
static int cb_default_write_log(const char *format, ...)
{
    va_list args;
    va_start(args, format);
    vprintf(format, args);
    va_end(args);
    return 1;
}

FILE*    cb_file_write_log_fp = NULL;
btc_bool cb_file_write_log_flag = false;
char*    cb_file_write_log_filter = "";
static int cb_file_write_log(const char *format, ...)
{
    if (cb_file_write_log_flag)
    {
        // Appliquer le filtre
        unsigned int limit = strlen(cb_file_write_log_filter);
        for(unsigned int i=0; i < limit; i++)
            if (format[i] != cb_file_write_log_filter[i])
                return 1;

        // Ouvrir le flix si besoin
        if (cb_file_write_log_fp == NULL) 
            cb_file_write_log_fp = fopen("log.txt","a+");

        // Impossible d'ouvrir le fichier de log
        if (cb_file_write_log_fp == NULL) return 0;

        va_list args;
        va_start(args, format);
        vfprintf(cb_file_write_log_fp, format, args);
        va_end(args);
        fflush(cb_file_write_log_fp);
    } else {
        if (cb_file_write_log_fp != NULL) 
        {
            // Fermer le flux
            fclose(cb_file_write_log_fp);
            cb_file_write_log_fp = NULL;
        }
    }
    return 1;
}

static btc_bool cb_timer(btc_node *node, uint64_t *now)
{
    check_connected_nodes(node->nodegroup);

    /* return true = run internal timer logic (ping, disconnect-timeout, etc.) */
    return true;
}

static btc_bool cb_parse_cmd(struct btc_node_ *node, btc_p2p_msg_hdr *hdr, struct const_buffer *buf)
{
    (void)(node);
    (void)(hdr);
    (void)(buf);
    return true;
}

static void cb_node_connection_state_changed(struct btc_node_ *node)
{
    int nb_connected = btc_node_group_amount_of_connected_nodes(node->nodegroup, NODE_CONNECTED);
    node->nodegroup->log_write_cb("STATUS : connexion change status (%d node connected)\n", nb_connected);
    check_connected_nodes(node->nodegroup);
}

unsigned int cb_handshake_done_bestknownheight = 0;
static void cb_handshake_done(struct btc_node_ *node)
{
    int nb_connected = btc_node_group_amount_of_connected_nodes(node->nodegroup, NODE_CONNECTED);
    node->nodegroup->log_write_cb("STATUS : handshake (%d node connected)\n", nb_connected);

    /* make sure only one node is used for header sync */
    for(size_t i =0;i< node->nodegroup->nodes->len; i++)
    {
        btc_node *check_node = vector_idx(node->nodegroup->nodes, i);
        if (  ( (check_node->state & NODE_HEADERSYNC) == NODE_HEADERSYNC
                 ||
                (check_node->state & NODE_BLOCKSYNC) == NODE_BLOCKSYNC
               )
            &&
            (check_node->state & NODE_CONNECTED) == NODE_CONNECTED)
            return;
    }

    /* 
     * Si le nouveau noeud a une meilleur connaissance de la blockchain que nous
     * On relance une synchro de la blockchain
     * Si possible on repart de 20 blocks en arrière 
     * Cela pour éviter de rester coincés dans un fork 
     * 
     * De même, on garde une marge de manoeuvre dans les positions
     * du fichier
     */
    if (node->bestknownheight > cb_handshake_done_bestknownheight) 
    {
        if (blockchain_fd != NULL)
        {
            /*
             * Définir et intialiser un tampon de 20 blocks
             */
#define MARGE_TAMPON 25
            char     buffer[MARGE_TAMPON][65];
            uint32_t read_height[MARGE_TAMPON];
            fpos_t   fpos[MARGE_TAMPON];
            (void) fseek(blockchain_fd, 0L, SEEK_SET);
            for(uint i = 0; i < MARGE_TAMPON; i++)
            {
                fgetpos(blockchain_fd, fpos+i);
                strcpy(buffer[i],"");
                read_height[i] = 0;
            }

            request_headers_or_blocks_hash_init = false;

            while(fscanf(blockchain_fd, "%s %d\n", buffer[0], read_height))
            {
                /* Décaler le tampon d'un cran vers le bas */
                for(uint i = (MARGE_TAMPON-1); i > 0 ; i--)
                {
                    strcpy(buffer[i], buffer[i-1]);
                    read_height[i] = read_height[i-1];
                    memcpy(fpos+i, fpos+(i-1), sizeof(fpos_t));
                }
                
                if (feof(blockchain_fd)) break;
            }
            if (read_height[0] > MARGE_TAMPON)
            {
                int outlen;
                node->nodegroup->log_write_cb("STATUS : Setting requested hash with %s\n", buffer[19]);
                utils_reverse_hex(buffer[19], 64);
                utils_hex_to_bin(buffer[19], request_headers_or_blocks_hash, 64, &outlen);
                cb_handshake_done_bestknownheight = read_height[19];
                request_headers_or_blocks_hash_init = true;
                fsetpos(blockchain_fd, fpos+(MARGE_TAMPON-1));
            }
        }

        node->nodegroup->log_write_cb("STATUS : starting header sync from node %d\n", node->nodeid);
        node->state |= NODE_HEADERSYNC;
        request_headers_or_blocks(node, false);
    } else {
        node->state &= ~NODE_HEADERSYNC;
    }
}

int btc_header_hash_compare(uint8_t *hashA, uint8_t *hashB)
{
    /* byte per byte compare */
    for (unsigned int i = 0; i < sizeof(uint256); i++) {
        uint8_t iA = hashA[i];
        uint8_t iB = hashB[i];
        if (iA > iB)
            return -1;
        else if (iA < iB)
            return 1;
    }
    return 0;
}

static void post_inv_cmd(struct btc_node_ *node, struct const_buffer *buf)
{
    struct const_buffer buf_copy = { buf->p, buf->len };
    btc_bool contains_block = false;
    uint32_t vsize;

    // directly create a getdata message
    cstring *p2p_msg = btc_p2p_message_new(node->nodegroup->chainparams->netmagic, BTC_MSG_GETDATA, buf->p, buf->len);

    if (!deser_varlen(&vsize, &buf_copy)) return;

    for (unsigned int i = 0; i < vsize; i++)
     {
        uint8_t hash[36];
        uint32_t type;
        if (!deser_u32(&type, &buf_copy)) return;
        if (!deser_u256(hash, &buf_copy)) return;

        contains_block |= (type == BTC_INV_TYPE_BLOCK);
    }

    /*
     * This a MSG for inventory one block
     */
    if (contains_block && (vsize == 1))
    {
        node->time_last_request = time(NULL);   
        btc_node_send(node, p2p_msg);
    }

    /* cleanup */
    cstr_free(p2p_msg, true);
}

static void cb_post_cmd(struct btc_node_ *node, btc_p2p_msg_hdr *hdr, struct const_buffer *buf)
{
    struct const_buffer buf_copy = { buf->p, buf->len };

    uint256 hash_bin_val;
    char hash_hex_str[65];

    /*
     * MSG is inventory
     */
    if (strcmp(hdr->command, BTC_MSG_INV) == 0)
    {
        post_inv_cmd(node, buf);
    }

    /*
     * MSG is block data
     */
    if (strcmp(hdr->command, BTC_MSG_BLOCK) == 0)
    {
        FILE*   block_fd;
        char    block_filename[2000];

        char prev_hex_str[65];
        btc_bool is_a_known_block = false;

        const char* coinbase_hash = "0000000000000000000000000000000000000000000000000000000000000000";
        btc_block_header header;

        uint32_t nb_tx;
        uint32_t block_height = 0;
        uint64_t total_outputs = 0;
        uint64_t total_fees = 0;
        uint64_t reward = 5000000000;

        /* 
         * La synchro des ent^tes n'est pas terminée 
         * Dans ce cas on ne traite pas le bloc ...
         */
        if (node->bestknownheight > cb_handshake_done_bestknownheight) return;

        /*
         * Désérialisation du bloc
         */
        if (!btc_block_header_deserialize(&header, &buf_copy)) return;
        if (!deser_varlen(&nb_tx, &buf_copy)) return;

        btc_block_header_hash(&header, (uint8_t *)&hash_bin_val);

        memset(hash_hex_str, 0, sizeof(hash_hex_str));
        utils_bin_to_hex(hash_bin_val, sizeof(hash_bin_val), hash_hex_str);
        utils_reverse_hex(hash_hex_str, 64);

        memset(prev_hex_str, 0, sizeof(prev_hex_str));
        utils_bin_to_hex(header.prev_block, sizeof(header.prev_block), prev_hex_str);
        utils_reverse_hex(prev_hex_str, 64);

        // Nous connaissons déjà ce block comme étant le sommet de la blockchain
        if (0 == btc_header_hash_compare((uint8_t *)&hash_bin_val, (uint8_t *)&request_headers_or_blocks_hash)) return;

        // Est-ce un nouveau sommet de la blockchain ?
        if (0 == btc_header_hash_compare((uint8_t *)&header.prev_block, (uint8_t *)&request_headers_or_blocks_hash))
        {
            cb_handshake_done_bestknownheight += 1;
            memcpy(&request_headers_or_blocks_hash, &hash_bin_val, sizeof(uint256));
            block_height = cb_handshake_done_bestknownheight;  
            node->nodegroup->log_write_cb("STATUS : New chain top detected (height is now %d) !\n",cb_handshake_done_bestknownheight);
        } else {
            node->nodegroup->log_write_cb("STATUS : Orphan block detected !\n");
            /*
             * TODO 
             *  Conserver une liste des blocks orphelins
             *  Grâce au timer, tenter de les rattacher après coup
             * 
             *      On peut recevoir un nouveau block alors que nous sommes 
             *      en train de se sycnhroniser.
             * 
             *      Il peut également s'agir d'un début de fork.
             */
            if (blockchain_fd != NULL)
            {
                char buffer[65];
                uint32_t read_height = 0;
                (void) fseek(blockchain_fd, 0L, SEEK_SET);
                while(fscanf(blockchain_fd, "%s %d\n", buffer, &read_height))
                {
                    if (!strcmp(buffer, hash_hex_str))
                    {
                        /*
                         * Ce block est déjà connu
                         */
                        is_a_known_block = true;
                        block_height = read_height;
                        (void) fseek(blockchain_fd, 0L, SEEK_END);
                        node->nodegroup->log_write_cb("STATUS : Known block detected (%d) !\n", block_height);
                        break;
                    }
                    if (!strcmp(buffer, prev_hex_str))
                    {
                        /*
                         * Ce block est potentiellement un début de fork
                         *      Il n'est pas au sommet
                         *      Mais on connait son parent
                         */
                        block_height = read_height + 1;
                    }
                    if (feof(blockchain_fd)) break;
                }
                (void) fseek(blockchain_fd, 0L, SEEK_END);
            }
            if (!is_a_known_block)
            {
                if (block_height > 0)
                {
                    /*
                     * Ce block est potentiellement un début de fork
                     * On le place au sommet
                     * Attention :
                     *      Je ne suis pas sûr que c'est ce qu'il faut faire ...
                     */
                    memcpy(&request_headers_or_blocks_hash, &hash_bin_val, sizeof(uint256));
                    cb_handshake_done_bestknownheight = block_height;  
                    node->nodegroup->log_write_cb("STATUS : Fork block detected (%d) !\n", block_height);
                } else {
                    /*
                     * Le block a-t-il moins de 2 heures ?
                     */
                    if (difftime(time(NULL), header.timestamp) < (2*3600))
                    {
                        /*
                         * Ce block est un orphelin
                         * TODO
                         *      Keep in memory and use the timer to try to connect it again to the blockchain
                         */
                        if (node->bestknownheight > cb_handshake_done_bestknownheight)
                        {
                            /*
                             * La synchronisation n'est pas finie
                             */
                            node->nodegroup->log_write_cb("STATUS : Orphan block while synchro !\n");
                        } else {
                            node->nodegroup->log_write_cb("STATUS : Orphan block detected !\n");
                        }
                    }
                    return;
                }
            } 
        }

        if (!is_a_known_block)
        {
            if (blockchain_fd != NULL)
            {
                (void) fseek(blockchain_fd, 0L, SEEK_END);
                fprintf(blockchain_fd, "%s %d\n", hash_hex_str, block_height);
                fflush(blockchain_fd);
            }
        }

        sprintf(block_filename,"%s/blocks/%u_%s", simple_db_path, block_height, hash_hex_str);
        if (file_exist(block_filename)) return;

        // Save the block in a file
        block_fd = fopen(block_filename,"wb");
        if (block_fd == NULL) return;

        fwrite((const void*) &block_height, sizeof(uint32_t), 1, block_fd);
        fwrite((const void*) &hash_bin_val, sizeof(uint256), 1, block_fd);
        fwrite((const void*) &header.version, sizeof(int32_t), 1, block_fd);
        fwrite((const void*) &header.timestamp, sizeof(uint32_t), 1, block_fd);
        fwrite((const void*) &header.nonce, sizeof(uint32_t), 1, block_fd);
        fwrite((const void*) &header.prev_block, sizeof(uint256), 1, block_fd);
        fwrite((const void*) &header.merkle_root, sizeof(uint256), 1, block_fd);
        fwrite((const void*) &nb_tx, sizeof(uint32_t), 1, block_fd);

        for (unsigned int i = 0; i < nb_tx; i++)
        {
            uint64_t tx_total_outputs = 0;
            size_t consummed = 0;
            btc_tx* tx =btc_tx_new(); //needs to be on the heap

            btc_tx_deserialize(buf_copy.p, buf_copy.len, tx, &consummed, true);
            deser_skip(&buf_copy, consummed);

            btc_tx_hash(tx, hash_bin_val);
            utils_bin_to_hex(hash_bin_val, sizeof(hash_bin_val), hash_hex_str);
            utils_reverse_hex(hash_hex_str, 64);

            for (size_t victx = 0; victx < tx->vin->len; victx++) 
            {
                btc_tx_in* tx_in = vector_idx(tx->vin, victx);
                char* hex_txin = utils_uint8_to_hex(tx_in->prevout.hash, 32);

                utils_reverse_hex(hex_txin, strlen(hex_txin));
                if (!strncmp(hex_txin,coinbase_hash,strlen(coinbase_hash))) strcpy(hex_txin,"COINBASE");
            }

            for (size_t voctx = 0; voctx < tx->vout->len; voctx++) 
            {
                btc_tx_out* tx_out = vector_idx(tx->vout, voctx);
                
                tx_total_outputs += tx_out->value;
            }

            fwrite((const void*) &hash_bin_val, sizeof(uint256), 1, block_fd);
            fwrite((const void*) &tx->locktime, sizeof(uint32_t), 1, block_fd);
            fwrite((const void*) &tx_total_outputs, sizeof(uint64_t), 1, block_fd);

            if (i == 0) 
            {
                // Compute fees
                while(reward > 0)
                {
                    if (reward < tx_total_outputs)
                        break;
                    reward /= 2;
                }
                total_fees = tx_total_outputs - reward;
            }
            total_outputs += tx_total_outputs;
            btc_tx_free(tx);
        }

        fclose(block_fd);

        fflush(stdout);
    }

    /*
     * MSG is headers data
     */
    if (strcmp(hdr->command, BTC_MSG_HEADERS) == 0)
    {
        uint32_t amount_of_headers;
        if (!deser_varlen(&amount_of_headers, &buf_copy)) return;
        node->nodegroup->log_write_cb("STATUS : (%d) headers received\n", amount_of_headers);

        while(amount_of_headers--)
        {
            uint256 hash_bin_val;
            btc_block_header header;
            if (!btc_block_header_deserialize(&header, &buf_copy)) return;
            if (!deser_skip(&buf_copy, 1)) return;

            btc_block_header_hash(&header, (uint8_t *)&hash_bin_val);
            
            if (0 == btc_header_hash_compare((uint8_t *)&header.prev_block, (uint8_t *)&request_headers_or_blocks_hash))
            {
                cb_handshake_done_bestknownheight += 1;
                memcpy(&request_headers_or_blocks_hash, &hash_bin_val, sizeof(uint256));

                memset(hash_hex_str, 0, sizeof(hash_hex_str));
                utils_bin_to_hex(hash_bin_val, sizeof(hash_bin_val), hash_hex_str);
                utils_reverse_hex(hash_hex_str, 64);

                if (blockchain_fd != NULL)
                {
                    // Ne rajouter un bloc que si on ne le connait pas
                    char        buffer[65];
                    uint32_t    read_height;
                    btc_bool    flag_local = false;
                    /*
                     * Cette ligen a été mise en commentaire
                     * Car elle ralenti énormément le processus de synchro
                     * Elle fait repartir du début du fichier à chaque fois.
                     * Pour accélérer le processus, on se cale en début de synchro (cd_handcheck_done)
                     * Puis on ne fait qu'avancer dans le fichier
                     * Attention : 
                     *    Cela suppose que le noeud nous envoie les entêtes de blocks dans l'ordre
                     */
                    // (void) fseek(blockchain_fd, 0L, SEEK_SET);
                    while(fscanf(blockchain_fd, "%s %d\n", buffer, &read_height))
                    {
                        if (!strcmp(buffer, hash_hex_str))
                        {
                            flag_local = true;
                            break;
                        }
                        if (feof(blockchain_fd)) break;
                    }
                    if (!flag_local) 
                    {
                        (void) fseek(blockchain_fd, 0L, SEEK_END);
                        fprintf(blockchain_fd, "%s %d\n", hash_hex_str,cb_handshake_done_bestknownheight );
                    }
                }
            } else {
                node->nodegroup->log_write_cb("STATUS : Orphan block header !\n");    
            }
        }

        if (node->bestknownheight > cb_handshake_done_bestknownheight)
        {
            /*
             * La synchronisation n'est pas finie
             * Demander d'autres blocs
             */
            node->nodegroup->log_write_cb("STATUS : bestknownheigt (%d / %d)\n", cb_handshake_done_bestknownheight, node->bestknownheight);
            request_headers_or_blocks(node, false);
        } else {
            node->nodegroup->log_write_cb("STATUS : Headers sync is finished !\n", cb_handshake_done_bestknownheight, node->bestknownheight);
            node->state &= ~NODE_HEADERSYNC;
            if (blockchain_fd != NULL)
                fflush(blockchain_fd);
         }
    }
}

/*
 * Gestion du fichier de chaine de blocks
 */
int init_blockchain_file() 
{
    const char* blockchain_filename = "blockchain.txt";
    char* blockchain_full_filename = NULL;
    char buffer[65];
    uint32_t block_height;

    blockchain_full_filename = calloc(strlen(simple_db_path)+strlen(blockchain_filename)+2, sizeof(char));
    if (blockchain_full_filename == NULL) return 1;

    sprintf(blockchain_full_filename,"%s/%s", simple_db_path, blockchain_filename);

    if (!file_exist(blockchain_full_filename))
    {
        blockchain_fd = fopen(blockchain_full_filename,"w");
        if (blockchain_fd != NULL)
        {
            int outlen;

            /*
             * Init with Genesis Block
             */
            sprintf(buffer, "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f");
            block_height = 0;

            fprintf(blockchain_fd, "%s %d\n", buffer, block_height);
            fclose(blockchain_fd);
            blockchain_fd = NULL;

            utils_reverse_hex(buffer, 64);
            utils_hex_to_bin(buffer, request_headers_or_blocks_hash, 64, &outlen);

            request_headers_or_blocks_hash_init = true;
            cb_handshake_done_bestknownheight = block_height;
        }
    }

    blockchain_fd = fopen(blockchain_full_filename,"r+");
    if (blockchain_fd != NULL)
    {
        while(fscanf(blockchain_fd, "%s %d\n", buffer, &block_height))
        {
            if (block_height > cb_handshake_done_bestknownheight)
            {
                int outlen;

                utils_reverse_hex(buffer, 64);
                utils_hex_to_bin(buffer, request_headers_or_blocks_hash, 64, &outlen);

                request_headers_or_blocks_hash_init = true;
                cb_handshake_done_bestknownheight = block_height;
            }    

            if (feof(blockchain_fd)) break;
        }
    }
    free(blockchain_full_filename);

    return 0;
}

int main(int ac, char** av)
{
    btc_node_group* group = NULL;

    cb_file_write_log_flag = false;
    cb_file_write_log_filter = "STATUS";

    /*
     * Daemonize
     */
    daemonize();
    
    /*
     * Trapper les signaux pour fermer correctement le programme
     */
    if (signal(SIGCHLD,SIG_IGN) == SIG_ERR)
    {
        printf("\ncan't catch SIGCHLD\n");
        return 1;
    }
    if (signal(SIGTSTP,SIG_IGN) == SIG_ERR)
    {
        printf("\ncan't catch SIGTSTP\n");
        return 1;
    }
    if (signal(SIGTTOU,SIG_IGN) == SIG_ERR)
    {
        printf("\ncan't catch SIGTTOU\n");
        return 1;
    }
    if (signal(SIGTTIN,SIG_IGN) == SIG_ERR)
    {
        printf("\ncan't catch SIGTTIN\n");
        return 1;
    }

    if (signal(SIGHUP,signal_handler) == SIG_ERR)
    {
        printf("\ncan't catch SIGHUP\n");
        return 1;
    }
    if (signal(SIGTERM,signal_handler) == SIG_ERR)
    {
        printf("\ncan't catch SIGTERM\n");
        return 1;
    }
    if (signal(SIGINT, signal_handler) == SIG_ERR)
    {
        printf("\ncan't catch SIGINT\n");
        return 1;
    }

    /*
     * Gestion du fichier de chaine de blocks
     */
    if (ac > 1) simple_db_path = av[1];
    if (0 != init_blockchain_file()) return 1;

    /*
     * Create a node group 
     */
    group = btc_node_group_new(NULL);
    group->desired_amount_connected_nodes = 1;
    group->periodic_timer_cb = cb_timer;
    group->log_write_cb = cb_file_write_log;
    group->parse_cmd_cb = cb_parse_cmd;
    group->postcmd_cb = cb_post_cmd;
    group->node_connection_state_changed_cb = cb_node_connection_state_changed;
    group->handshake_done_cb = cb_handshake_done;

    sprintf(group->clientstr,"TOPISTO is using libbtc");

    find_and_add_nodes(group);

    /* connect to the next node */
    btc_node_group_connect_next_nodes(group);

    /* start the event loop */
    btc_node_group_event_loop(group);

    /* cleanup */
    btc_node_group_free(group); //will also free the nodes structures from the heap

    return 0;
}
