#include <alloca.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <string.h>
#include <newt.h>
#include <popt.h>
#include <unistd.h>

#include "install.h"
#include "intl.h"
#include "kickstart.h"
#include "log.h"

struct ksCommandNames {
    int code;
    char * name;
} ;

struct ksCommand {
    int code, argc;
    char ** argv;
};

struct ksCommandNames ksTable[] = {
    { KS_CMD_NFS, "nfs" },
    { KS_CMD_INSTALL, "install" },
    { KS_CMD_UPGRADE, "upgrade" },
    { KS_CMD_ROOTPW, "rootpw" },
#if defined(__i386__)
    { KS_CMD_LILO, "lilo" },
#else if defined(__sparc__)
    { KS_CMD_LILO, "silo" },
#endif
    { KS_CMD_PART, "part" },
    { KS_CMD_CLEARPART, "clearpart" },
    { KS_CMD_ZEROMBR, "zerombr" },
    { KS_CMD_XCONFIG, "xconfig" },
    { KS_CMD_KEYBOARD, "keyboard" },
    { KS_CMD_MOUSE, "mouse" },
    { KS_CMD_TIMEZONE, "timezone" },
    { KS_CMD_CDROM, "cdrom" },
    { KS_CMD_DEVICE, "device" },
    { KS_CMD_LANG, "lang" },
    { KS_CMD_NETWORK, "network" },
    { KS_CMD_NONE, NULL }
};

struct ksCommand * commands = NULL;
int numCommands = 0;
struct ksPackage * packages = NULL;
int numPackages = 0;
char * post = NULL;

int ksReadCommands(char * cmdFile) {
    int fd;
    char * buf;
    struct stat sb;
    char * start, * end, * chptr;
    char oldch;
    int line = 0;
    char ** argv; 
    int argc;
    int inPackages = 0;
    struct ksCommandNames * cmd;
    int commandsAlloced = 5;
    int packagesAlloced = 5;

    if ((fd = open(cmdFile, O_RDONLY)) < 0) {
	newtWinMessage(_("Kickstart Error"), _("Ok"), 
			_("Error opening: kickstart file %s: %s"), cmdFile, 
			strerror(errno));
	return INST_ERROR;
    }

    fstat(fd, &sb);

    buf = alloca(sb.st_size + 1);
    if (read(fd, buf, sb.st_size) != sb.st_size) {
	newtWinMessage(_("Kickstart Error"), _("Ok"), 
			_("Error reading contents of kickstart file %s: %s"),
			cmdFile, strerror(errno));
	close(fd);
	return INST_ERROR;
    }

    close(fd);

    buf[sb.st_size] = '\0';

    commands = malloc(sizeof(*commands) * commandsAlloced);
    packages = malloc(sizeof(*packages) * packagesAlloced);

    start = buf;
    while (*start) {
	line++;

	if (!(end = strchr(start, '\n')))
	    end = start + strlen(start);

	oldch = *end;
	*end = '\0';

	while (*start && isspace(*start)) start++;

	chptr = end - 1;
	while (chptr > start && isspace(*chptr)) chptr--;

	if (isspace(*chptr)) 
	    *chptr = '\0';
	else
	    *(chptr + 1) = '\0';

	if (!*start || *start == '#') {
	    /* no nothing */
	} else if (!strcmp(start, "%post")) {
	    if (oldch)
		start = end + 1;
	    else
		start = end;

	    if (*start) {
		post = strdup(start);
	    }
	    break;
	    
	} else if (!strcmp(start, "%packages")) {
	    inPackages = 1;
	} else if (inPackages) {
	    if (numPackages == packagesAlloced) {
		packagesAlloced += 5;
		packages = realloc(packages,
				   sizeof(*packages) * packagesAlloced);
	    }

	    if (*start == '@') {
		packages[numPackages].isComponent = 1;
		start++;
		while (*start && isspace(*start)) start++;
		if (*start)
		    packages[numPackages++].name = strdup(start);
	    } else {
		packages[numPackages].isComponent = 0;
		packages[numPackages++].name = strdup(start);
	    }
	} else {
	    if (poptParseArgvString(start, &argc, &argv) || !argc) {
		newtWinMessage(_("Kickstart Error"), _("Ok"), 
			       _("Error on line %d of kickstart file %s."),
				argv[0], line, cmdFile);
	    } else {
		for (cmd = ksTable; cmd->name; cmd++)
		    if (!strcmp(cmd->name, argv[0])) break;

		if (!cmd->name) {
		    newtWinMessage(_("Kickstart Error"), _("Ok"), 
				   _("Unknown command %s on line %d of "
				     "kickstart file %s."), argv[0], line, 
				   cmdFile);
		} else {
		    if (numCommands == commandsAlloced) {
			commandsAlloced += 5;
			commands = realloc(commands,
					   sizeof(*commands) * commandsAlloced);
		    }

		    commands[numCommands].code = cmd->code;
		    commands[numCommands].argc = argc;
		    commands[numCommands].argv = argv;
		    numCommands++;
		}
	    }
	}

	if (oldch)
	    start = end + 1;
	else
	    start = end;
    }

    return 0;
}

int ksGetCommand(int cmd, char ** last, int * argc, char *** argv) {
    int i = 0;

    if (last) {
	for (i = 0; i < numCommands; i++) {
	    if (commands[i].argv == last) break;
	}

	i++;
    }

    while (i < numCommands) {
	if (commands[i].code == cmd) {
	    if (argv) *argv = commands[i].argv;
	    if (argc) *argc = commands[i].argc;
	    return 0;
	}
	i++;
    }

    return 1;
}

void ksGetPackageList(struct ksPackage ** list, int * count) {
    *list = packages;
    *count = numPackages;
}

int ksRunPost(void) {
    pid_t child;
    int fd;

    if (!post) return 0;

    logMessage("running kickstart post-install script");

    if ((fd = open("/mnt/tmp/ks.script", O_CREAT | O_RDWR, 0755)) < 0) {
	newtWinMessage(_("Error"), _("Ok"), _("Failed to create "
			    "/mnt/tmp/ks.script: %s"), strerror(errno));
	return INST_ERROR;
    }

    write(fd, "#!/bin/sh\n", 10);

    if (write(fd, post, strlen(post)) != strlen(post)) {
	newtWinMessage(_("Error"), _("Ok"), 
		       _("Failed to write ks post script: %s"),
		       strerror(errno));
	close(fd);
	unlink("/mnt/tmp/ks.script");
	return INST_ERROR;
    }

    close(fd);
    
    if (!(child = fork())) {
	chdir("/mnt");
	chroot("/mnt");
	execl("/tmp/ks.script", "/tmp/ks.script", NULL);
	logMessage("exec failed: %s\n", strerror(errno));
	exit(1);
    }

    waitpid(child, NULL, 0);

    unlink("/mnt/tmp/ks.script");

    return 0;
}
