/* ==== fc_do_file.c =========================================================
 * Copyright (c) 1995 by Chris Provenzano, proven@mit.edu
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *  This product includes software developed by Chris Provenzano.
 * 4. The name of Chris Provenzano may not be used to endorse or promote 
 *	  products derived from this software without specific prior written
 *	  permission.
 *
 * THIS SOFTWARE IS PROVIDED BY CHRIS PROVENZANO ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL CHRIS PROVENZANO BE LIABLE FOR ANY 
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
 * SUCH DAMAGE.
 *
 * Description : Main fc functions.
 *
 *  1.00 95/06/28  proven
 *      -Started coding this file.
 */

#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <errno.h>
#include "util.h"
#include "parse.h"
#include "fc.h"

/* TTT Remove printfs and use a generic error system. */

/* ==========================================================================
 * fc_file_insert()
 */
static void fc_file_insert(struct fc_files **fc_filep, struct fc_files *fc_file)
{
	int i;

	for (; *fc_filep; fc_filep = &((*fc_filep)->file_next)) {
		for (i = 0; i < (*fc_filep)->filekey_count; i++) {
			if (i == fc_file->filekey_count) {
				fc_file->file_next = (*fc_filep);
				*fc_filep = fc_file;
				return;
			}
			if (fc_file->filekey_first[i].key->key_num >
			  (*fc_filep)->filekey_first[i].key->key_num)
				break;
			if (fc_file->filekey_first[i].key->key_num ==
			  (*fc_filep)->filekey_first[i].key->key_num)
				continue;
			fc_file->file_next = (*fc_filep);
			*fc_filep = fc_file;
			return;
		}
	}
	fc_file->file_next = (*fc_filep);
	*fc_filep = fc_file;
}

/* ==========================================================================
 * fc_do_af()
 *
 * For now, only allow adding one file at a time.
 */
int fc_do_af(struct fc * fc, char * data)
{
	struct fc_filekeys * fc_filekeys;
	struct fc_listkeys * fc_listkey;
    struct fc_files * listfile;
    struct fc_files * fc_file;
    struct fc_keys * fc_key;
	char * file_name;
	int i, j, k, l;

	/* if (file_name = parse_get_file(data, NULL)) { */
	if (file_name = parse_get_key(data, NULL)) { 
		/* Check if file name already exists in database */
		for (fc_file = fc->files; fc_file; fc_file = fc_file->file_next) {
			if (!strcmp(fc_file->file_name, file_name)) {
				printf("Can't add currently used file name %s for command %s\n",
				  file_name, "af");
				return(NOTOK);
			}
		}
		if (!parse_key_is_special(file_name)) {
			if (fc_file = fc_file_alloc(2)) {
				i = strlen(file_name) + 1;
				listfile = fc_file->file_next;
				if (fc_file->file_name = (char *)malloc(i)) {
					i = fc->count_current * sizeof(struct fc_filekeys);
					if (fc_filekeys = (struct fc_filekeys *)malloc(i)) {
						for (l = i = 0; i < fc->count_current; i++) {
							fc_listkey = fc->listkeys_current + i;

							/* Find pointer to key */
							for (fc_key = fc->keys; fc_listkey->key->key_num !=
							  fc_key->key_num; fc_key = fc_key->key_next);

							/* Insert filekey */
							for (j = 0; j < l; j++) {
								if (fc_listkey->key->key_num < 
								  fc_filekeys[j].key->key_num) {
									for (k = l++; k > j; k--) {
										fc_filekeys[k].key=fc_filekeys[k-1].key;
									}
									fc_filekeys[j].key = fc_listkey->key;
								}
								if (fc_listkey->key->key_num == 
								   fc_filekeys[j].key->key_num) {
									break;
								}
							}
							if (j == l) {
								fc_filekeys[l++].key = fc_listkey->key;
							}
						}
						for (j = 0; j < l; j++) {
							struct fc_keys * fc_key;
							fc_key = fc_filekeys[j].key;
							fc_filekeys[j].file_next = fc_key->file_first;
							fc_filekeys[j].filekey_next = fc_key->filekey_first;
							fc_key->filekey_first = fc_filekeys + j;
							fc_key->file_first = fc_file;
							fc_key->file_count++;
						}
						fc_file->alloctype = IND_ALLOCED;
						strcpy(fc_file->file_name, file_name);
						fc_file->filekey_first = fc_filekeys;
						fc_file->filekey_count = i;
					
						fc->filecount++;
						/* Insert file into file list */
						fc_file_insert(&fc->files, fc_file);

						listfile->alloctype = OTHER_ALLOCED;
						listfile->file_name = fc_file->file_name;
						listfile->filekey_first = fc_file->filekey_first;
						listfile->filekey_count = fc_file->filekey_count;

						l = fc->count_current - 1;
						if (!(listfile->file_next = 
						  fc->listkeys_current[l].file_first)) {
							fc->listkeys_current[l].file_last = listfile;
						}
						fc->listkeys_current[l].file_first = listfile;
						fc->listkeys_current[l].file_count++;
						return(OK);
					} else {
						printf("Can't alloc space for command %s\n", "af");
						free(fc_file->file_name);
						fc_file_free(listfile);
						fc_file_free(fc_file);
					}
				} else {
					printf("Can't alloc space for command %s\n", "af");
					fc_file_free(listfile);
					fc_file_free(fc_file);
				}
			} else {
				printf("Can't alloc space for command %s\n", "af");
			}
		} else {
			printf("Can't add reserved key %s for command %s\n",file_name,"af");
		}
	} else {
		printf("Not enough args for command %s\n", "af");
	}
	return(NOTOK);
}

/* ==========================================================================
 * fc_do_lf()
 */
int fc_do_lf(struct fc * fc, char * data)
{
	struct fc_files * fc_file;
	char ** strings;
	int count, i;

	if (fc->count_current) {
		fc_file = fc->listkeys_current[fc->count_current - 1].file_first; 
		count = fc->listkeys_current[fc->count_current - 1].file_count;
	} else {
		count = fc->filecount;
		fc_file = fc->files;
	}
	if (count) {
		if (strings = (char **) malloc(sizeof(char *) * count)) {
			for (i = 0; fc_file; fc_file = fc_file->file_next) {
				strings[i++] = fc_file->file_name;
			}
			if (i != count) abort();
			fc_display(strings, i);
			free(strings);
		} else {
			printf("Can't alloc space for command %s\n", "lf");
            return(NOTOK);
        }
	} else {
		printf("\n");
	}
	return(OK);
}

/* ==========================================================================
 * fc_do_rf()
 */
int fc_do_rf(struct fc * fc, char * data)
{
	struct fc_files * fc_file, ** fc_filep, ** fc_filep2;
	int i, found_one = 0;
	char * file;

	if (file = parse_get_key(data, &data)) {
		for (fc_filep = &fc->files; *fc_filep; 
		  fc_filep = &((*fc_filep)->file_next)) {
			if (!strcmp((*fc_filep)->file_name, file)) {
				break;
			}
		}
		if (*fc_filep) {
			/* First remove it from fc_listkeys if it exists there */
			for (found_one = i = 0; (i < fc->count_current)&&(!found_one); i++){

				for (fc_filep2 = &(fc->listkeys_current[i].file_first); 
				  *fc_filep2; fc_filep2 = &((*fc_filep2)->file_next)) {
					if ((*fc_filep2)->file_name == (*fc_filep)->file_name) {
						fc_file = *fc_filep2;
						fc->listkeys_current[i].file_count--;
						*fc_filep2 = (*fc_filep2)->file_next;
						fc_file_free(fc_file);
						found_one++;
						break;
					}
				}
			}
			/* Now remove it from filekeys */
			for (i = 0; i < (*fc_filep)->filekey_count; i++) {
				struct fc_filekeys ** fc_filekey;
				fc_filekey=&((*fc_filep)->filekey_first[i].key->filekey_first);
				fc_filep2 = &((*fc_filep)->filekey_first[i].key->file_first);
				(*fc_filep)->filekey_first[i].key->file_count--;
				while (*fc_filep2 != *fc_filep) {
					fc_filep2 = &((*fc_filekey)->file_next);
					fc_filekey = &((*fc_filekey)->filekey_next);
				}
				*fc_filep2 = (*fc_filekey)->file_next;
				*fc_filekey = (*fc_filekey)->filekey_next;
			}
			fc_file = *fc_filep;
			*fc_filep = (*fc_filep)->file_next;
			free(fc_file->filekey_first);
			fc_file_free(fc_file);
			fc->filecount--;
			return(OK);
		} else {
			printf("File %s doesn't exist\n", file);
		}
	} else {
		printf("Not enough args for command %s\n", "rf");
	}
	return(NOTOK);
}

/* ==========================================================================
 * fc_do_lfk()
 */
int fc_do_lfk(struct fc * fc, char * data)
{
	struct fc_listkeys * fc_listkeys = NULL;
	struct fc_files * fc_file;
	char * key;
	int i;

	if (key = parse_get_key(data, &data)) {
		switch (parse_key_is_special(key)) {
		case 3:
			fc_listkeys = NULL;
			break;
		case 1:
		default:
			printf("Not implemented\n");
			return(NOTOK);
		}

		/* If there are no listkeys then we want all the files */
		if (fc_listkeys == NULL) {
			fc_file = fc->files;
		}
	} else {
		if (fc->listkeys_current) {
			fc_file = fc->listkeys_current[fc->count_current - 1].file_first;
		} else {
			fc_file = fc->files;
		}
	}

	while (fc_file) {
		printf("%s :", fc_file->file_name);
		for (i = 0; i < fc_file->filekey_count; i++) {
			printf(" %s", fc_file->filekey_first[i].key->key_name);
		}
		fc_file = fc_file->file_next;
		printf("\n");
	}
	printf("\n");
	return(OK);
}
