/*
 * This file is part of the bayou project.
 *
 * Copyright (C) 2008 Advanced Micro Devices, Inc.
 *
 * 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.
 */

#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <libgen.h>
#include "liblar.h"
#include "pbuilder.h"

void do_lzma_compress(char *in, int in_len, char *out, int *out_len);

int add_bpt_to_lar(struct LAR *lar, struct config *config)
{
	char *buffer;
	int ret, i, len = sizeof(struct bpt_config);
	struct bpt_config *cfg;
	struct LARAttr attr;
	char *ptr;

	for (i = 0; i < config->n_entries; i++) {
		len += sizeof(struct bpt_pentry);

		if (config->entries[i]->type != BPT_TYPE_CHAIN)
			len += ((strlen(config->entries[i]->larname)
				+ 15) & ~0x0F);
	}

	buffer = calloc(len, 1);

	if (buffer == NULL)
		return -1;

	cfg = (struct bpt_config *)buffer;

	cfg->id = BPT_ID;
	cfg->timeout = config->timeout;
	cfg->entries = config->n_entries;

	ptr = buffer + sizeof(struct bpt_config);

	for (i = 0; i < config->n_entries; i++) {
		int nlen = 0;
		struct bpt_pentry *pentry = (struct bpt_pentry *)ptr;

		pentry->index = config->entries[i]->index;
		pentry->parent = config->entries[i]->parent;
		pentry->type = config->entries[i]->type;
		pentry->flags = config->entries[i]->flags;

		strncpy((char *)pentry->title,
			(char *)config->entries[i]->title,
			sizeof(pentry->title));

		if (config->entries[i]->type != BPT_TYPE_CHAIN) {
			nlen = strlen(config->entries[i]->larname);
			nlen = (nlen + 15) & ~0x0F;

			strcpy((char *)(ptr + sizeof(struct bpt_pentry)),
			       config->entries[i]->larname);

			pentry->nlen = nlen;
		}

		ptr += sizeof(struct bpt_pentry);

		if (config->entries[i]->type != BPT_TYPE_CHAIN)
			ptr += nlen;
	}

	LAR_SetAttrs(&attr, "bayou_payload_table", ALGO_NONE);

	ret = LAR_AppendBuffer(lar, (unsigned char *)buffer, len, &attr);
	free(buffer);
	return ret;
}

struct lfile {
	char *file;
	char *larname;
};

int n_lfiles;

int create_lar_from_config(const char *input, const char *output)
{
	struct config config;
	FILE *stream;
	struct LAR *lar;
	struct LARAttr attr;
	int i, j, ret = -1;
	struct lfile *lfiles;

	stream = fopen(input, "r");

	if (stream == NULL) {
		warn("E:  Couldn't open %s for reading\n", input);
		return -1;
	}

	memset(&config, 0, sizeof(config));

	parseconfig(stream, &config);
	fclose(stream);

	lar = LAR_Create(output);

	if (lar == NULL) {
		warn("E:  Couldn't create a new lar file\n");
		return -1;
	}

	LAR_SetCompressionFuncs(lar, ALGO_LZMA, do_lzma_compress, NULL);

	lfiles = calloc(sizeof(struct lfile), config.n_entries);

	if (lfiles == NULL) {
		warn("E: Couldn't allocate memory: %m\n");
		return -1;
	}

	for (i = 0; i < config.n_entries; i++) {
		/* Master chain entries don't have files associated with them. */
		if (config.entries[i]->type == BPT_TYPE_CHAIN)
			continue;

		if (access(config.entries[i]->file, R_OK)) {
			warn("E:  Could not find file %s\n",
			     config.entries[i]->file);

			goto err;
		}

		if (config.entries[i]->larname == NULL) {
			config.entries[i]->larname =
			    strdup(basename(config.entries[i]->file));

			if (config.entries[i]->larname == NULL) {
				warn("E: Could not allocate memory for the default name\n");
				goto err;
			}
		}

		/*
		 * Add the file to the list of files to add to the LAR - skip
		 * any duplicates, but be on the lookout for the same LAR name
		 * attached to a different file.
		 */
		for (j = 0; j < n_lfiles; j++) {
			if (!strcmp(lfiles[j].larname,
				    config.entries[i]->larname)) {
				if (strcmp(lfiles[j].file,
					   config.entries[i]->file)) {
					warn("E:  LAR name '%s' has already been used\n", config.entries[i]->larname);
					goto err;
				}
				break;
			}
		}

		if (j == n_lfiles) {
			lfiles[n_lfiles].file = config.entries[i]->file;
			lfiles[n_lfiles++].larname = config.entries[i]->larname;
		}
	}

	/* Add all the files to the LAR. */
	for (i = 0; i < n_lfiles; i++) {
		LAR_SetAttrs(&attr, lfiles[i].larname, ALGO_LZMA);

		if (LAR_AppendFile(lar, lfiles[i].file, &attr)) {
			warn("E: Could not add %s to the LAR\n",
			     lfiles[i].file);
			goto err;
		}
	}

	ret = add_bpt_to_lar(lar, &config);

err:
	LAR_Close(lar);
	return ret;
}