/*
 * Ouroboros - Copyright (C) 2016
 *
 * DIF directory
 *
 *    Sander Vrijders <sander.vrijders@intec.ugent.be>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * 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, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#define OUROBOROS_PREFIX "directory"

#include <ouroboros/config.h>
#include <ouroboros/logs.h>
#include <ouroboros/errno.h>

#include "dir.h"
#include "ipcp.h"
#include "ro.h"
#include "path.h"
#include "ribmgr.h"

#include <stdlib.h>
#include <string.h>

char * create_path(char * name)
{
        char * path;

        path = pathname_create(RO_DIR);
        if (path == NULL)
                return NULL;

        path = pathname_append(path, name);
        if (path == NULL) {
                pathname_destroy(path);
                return NULL;
        }

        return path;
}

int dir_init(void)
{
        char * path;
        struct ro_attr attr;

        ro_attr_init(&attr);
        attr.enrol_sync = true;
        attr.recv_set = ALL_MEMBERS;

        path = pathname_create(RO_DIR);
        if (path == NULL)
                return -1;

        if (ro_create(path, &attr, NULL, 0)) {
                pathname_destroy(path);
                LOG_ERR("Failed to create RIB object.");
                return -1;
        }

        pathname_destroy(path);

        return 0;
}

int dir_fini(void)
{
        char * path;

        path = pathname_create(RO_DIR);
        if (path == NULL)
                return -1;

        ro_delete(path);
        pathname_destroy(path);

        return 0;
}

int dir_name_reg(char * name)
{
        struct ro_attr attr;
        char * path;
        uint64_t * addr;

        pthread_rwlock_rdlock(&ipcpi.state_lock);

        if (ipcp_get_state() != IPCP_OPERATIONAL) {
                pthread_rwlock_unlock(&ipcpi.state_lock);
                LOG_ERR("IPCP is not in RUNNING state.");
                return -1;
        }

        ro_attr_init(&attr);
        attr.enrol_sync = true;
        attr.recv_set = ALL_MEMBERS;

        path = create_path(name);
        if (path == NULL) {
                pthread_rwlock_unlock(&ipcpi.state_lock);
                return -ENOMEM;
        }

        addr = malloc(sizeof(*addr));
        if (addr == NULL) {
                pthread_rwlock_unlock(&ipcpi.state_lock);
                pathname_destroy(path);
                return -ENOMEM;
        }
        *addr = ribmgr_address();

        if (ro_create(path, &attr, (uint8_t *) addr, sizeof(*addr))) {
                pthread_rwlock_unlock(&ipcpi.state_lock);
                pathname_destroy(path);
                LOG_ERR("Failed to create RIB object.");
                return -1;
        }

        pthread_rwlock_unlock(&ipcpi.state_lock);

        LOG_DBG("Registered %s.", name);
        pathname_destroy(path);

        return 0;
}

int dir_name_unreg(char * name)
{
        char * path;

        pthread_rwlock_rdlock(&ipcpi.state_lock);

        if (ipcp_get_state() != IPCP_OPERATIONAL) {
                pthread_rwlock_unlock(&ipcpi.state_lock);
                LOG_ERR("IPCP is not in RUNNING state.");
                return -1;
        }

        path = create_path(name);
        if (path == NULL) {
                pthread_rwlock_unlock(&ipcpi.state_lock);
                return -ENOMEM;
        }

        if (ro_delete(path)) {
                pthread_rwlock_unlock(&ipcpi.state_lock);
                pathname_destroy(path);
                LOG_ERR("No such RIB object exists.");
                return -1;
        }

        pthread_rwlock_unlock(&ipcpi.state_lock);

        pathname_destroy(path);

        return 0;
}

int dir_name_query(char * name)
{
        char * path;
        int ret = -1;
        uint8_t * ro_data;
        uint64_t addr;
        struct dt_const * dtc;

        pthread_rwlock_rdlock(&ipcpi.state_lock);

        if (ipcp_get_state() != IPCP_OPERATIONAL) {
                pthread_rwlock_unlock(&ipcpi.state_lock);
                return -1;
        }

        path = create_path(name);
        if (path == NULL) {
                pthread_rwlock_unlock(&ipcpi.state_lock);
                return -1;
        }

        if (ro_exists(path)) {
                if (ro_read(path, &ro_data) < 0) {
                        pathname_destroy(path);
                        return -1;
                }
                addr = *((uint64_t *) ro_data);
                free(ro_data);

                dtc = ribmgr_dt_const();
                if (dtc == NULL) {
                        pathname_destroy(path);
                        return -1;
                }

                ret = (addr == ribmgr_address()) ? -1 : 0;
        }

        pthread_rwlock_unlock(&ipcpi.state_lock);

        pathname_destroy(path);

        return ret;
}