/*
 * fragment.c
 * Management of fragment lists.
 *
 * Copyright (c) 2012 pkgconf authors (see AUTHORS).
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * This software is provided 'as is' and without any warranty, express or
 * implied.  In no event shall the authors be liable for any damages arising
 * from the use of this software.
 */

#include "pkg.h"
#include "bsdstubs.h"

void
pkg_fragment_add(pkg_list_t *list, const char *string)
{
	pkg_fragment_t *frag;

	frag = calloc(sizeof(pkg_fragment_t), 1);

	if (*string == '-' && strncmp(string, "-lib:", 5) && strncmp(string, "-framework", 10))
	{
		frag->type = *(string + 1);
		frag->data = strdup(string + 2);
	}
	else
	{
		frag->type = 0;
		frag->data = strdup(string);
	}

	pkg_node_insert_tail(&frag->iter, frag, list);
}

static inline pkg_fragment_t *
pkg_fragment_lookup(pkg_list_t *list, pkg_fragment_t *base)
{
	pkg_node_t *node;

	PKG_FOREACH_LIST_ENTRY(list->head, node)
	{
		pkg_fragment_t *frag = node->data;

		if (base->type != frag->type)
			continue;

		if (!strcmp(base->data, frag->data))
			return frag;
	}

	return NULL;
}

static inline bool
pkg_fragment_can_merge_back(pkg_fragment_t *base)
{
	if (base->type == 'F')
		return false;

	return true;
}

static inline bool
pkg_fragment_can_merge(pkg_fragment_t *base)
{
	if (!strncmp(base->data, "-framework", 10))
		return false;

	return true;
}

static inline pkg_fragment_t *
pkg_fragment_exists(pkg_list_t *list, pkg_fragment_t *base)
{
	if (!pkg_fragment_can_merge_back(base))
		return NULL;

	if (!pkg_fragment_can_merge(base))
		return NULL;

	return pkg_fragment_lookup(list, base);
}

void
pkg_fragment_copy(pkg_list_t *list, pkg_fragment_t *base)
{
	pkg_fragment_t *frag;

	if ((frag = pkg_fragment_exists(list, base)) != NULL)
		pkg_fragment_delete(list, frag);
	else if (!pkg_fragment_can_merge_back(base) && (pkg_fragment_lookup(list, base) != NULL))
		return;

	frag = calloc(sizeof(pkg_fragment_t), 1);

	frag->type = base->type;
	frag->data = strdup(base->data);

	pkg_node_insert_tail(&frag->iter, frag, list);
}

void
pkg_fragment_delete(pkg_list_t *list, pkg_fragment_t *node)
{
	pkg_node_delete(&node->iter, list);

	free(node->data);
	free(node);
}

void
pkg_fragment_free(pkg_list_t *list)
{
	pkg_node_t *node, *next;

	PKG_FOREACH_LIST_ENTRY_SAFE(list->head, next, node)
	{
		pkg_fragment_t *frag = node->data;

		free(frag->data);
		free(frag);
	}
}

void
pkg_fragment_parse(pkg_list_t *list, pkg_list_t *vars, const char *value)
{
	int i, argc;
	char **argv;
	char *repstr = pkg_tuple_parse(vars, value);

	pkg_argv_split(repstr, &argc, &argv);

	for (i = 0; i < argc; i++)
		pkg_fragment_add(list, argv[i]);

	pkg_argv_free(argv);
	free(repstr);
}