#include <alloca.h>
#include <ctype.h>
#include <newt.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <unistd.h>

#include "devices.h"
#include "fs.h"
#include "install.h"
#include "intl.h"
#include "log.h"
#include "mkswap.h"
#include "run.h"
#include "windows.h"

static int partFilter(struct partition * part) {
    int rc;

    if (doMount(part->device, "/mnt", "ext2", 0, 0)) {
	return 0;
    }

    if (access("/mnt/etc", R_OK) || access("/mnt/etc/fstab", R_OK) ||
	access("/mnt/proc", R_OK) || access("/mnt/bin", R_OK) ||
	access("/mnt/etc/redhat-release", R_OK)) {
	rc = 0;
    } else 
	rc = 1;

    umount("/mnt");

    return rc;
}

int readMountTable(struct partitionTable table, struct fstab * finalFstab) {
    newtComponent okay, cancel, form, text, listbox, answer;
    int rootPartNum;
    struct partition * rootPart = NULL;
    FILE * f;
    char buf[200];
    char * start, * end;
    char device[50], mntpoint[50], type[50];
    struct fstab fstab;
    struct fstabEntry entry;
    int numParts;
    newtGrid grid, subgrid, buttons;
    char * reflowedText;
    int width, height;

    umountFilesystems(finalFstab);

    addPartitionListbox(table, 7, PART_EXT2, partFilter, &numParts,
			&subgrid, &listbox);

    if (!numParts) {
	newtGridFree(subgrid, 1);

	/* try again, but don't be quite so picky */
	addPartitionListbox(table, 7, PART_EXT2, NULL, &numParts,
			    &subgrid, &listbox);
	if (!numParts) {
	    newtPopWindow();

	    errorWindow(_("You don't have any Linux partitions. You "
			    "can't upgrade this system!"));
	    return INST_CANCEL;		/* INST_ERROR would hang everything */
	}
    }

    if (numParts == 1) {
	rootPart = newtListboxGetCurrent(listbox);
	rootPartNum = (rootPart - table.parts);
	logMessage("Using partition %s for the root device",
		    table.parts[rootPartNum].device);
	if (doMount(table.parts[rootPartNum].device, "/mnt", "ext2", 0, 0)) {
	    errorWindow(_("Could not mount automatically selected device."));
	    answer = NULL;
	    return INST_ERROR;
	}
	answer = okay;
    } else {
	reflowedText = newtReflowText(
			_("What partition holds the root partition "
			  "of your installation?"), 53, 5, 5, &width, &height);
	text = newtTextbox(-1, -1, width, height, NEWT_TEXTBOX_WRAP);
	newtTextboxSetText(text, reflowedText);
	free(reflowedText);

	buttons = newtButtonBar(_("Ok"), &okay, _("Cancel"), &cancel, NULL);

	form = newtForm(NULL, NULL, 0);
	newtGridAddComponentsToForm(subgrid, form, 1);
	newtFormAddComponents(form, text, okay, cancel, NULL);

	grid = newtCreateGrid(1, 3);

	newtGridSetField(grid, 0, 0, NEWT_GRID_COMPONENT, text,
			    0, 0, 0, 0, 0, 0);
	newtGridSetField(grid, 0, 1, NEWT_GRID_SUBGRID, subgrid,
			    0, 1, 0, 1, 0, 0);
	newtGridSetField(grid, 0, 2, NEWT_GRID_SUBGRID, buttons,
			    0, 0, 0, 0, 0, NEWT_GRID_FLAG_GROWX);

	newtGridWrappedWindow(grid, _("Root Partition"));

	do {
	    answer = newtRunForm(form);
	    if (answer == cancel) continue;

	    rootPart = newtListboxGetCurrent(listbox);
	    rootPartNum = (rootPart - table.parts);

	    if (doMount(table.parts[rootPartNum].device, "/mnt", "ext2", 
			0, 0)) {
		errorWindow("Could not mount device.");
		answer = NULL;
		continue;
	    }

	    if (testing) {
		continue;
	    }

	    if (access("/mnt/etc", R_OK) || access("/mnt/etc/fstab", R_OK) ||
		access("/mnt/proc", R_OK) || access("/mnt/bin", R_OK)) {
		errorWindow("That doesn't appear to be a root partition.");
		umount("/mnt");
		answer = NULL;
	    }
	} while (!answer);

	newtPopWindow();
	newtFormDestroy(form);
	newtGridFree(grid, 1);
    }

    if (answer == cancel) return INST_CANCEL;

    if (testing) 
	f = fopen("/etc/fstab", "r");
    else
	f = fopen("/mnt/etc/fstab", "r");

    if (!f) {
	errorWindow(_("Cannot read /mnt/etc/fstab: %s"));
	return INST_ERROR;
    }

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

    while (fgets(buf, sizeof(buf) - 1, f)) {
	start = buf;
	while (*start && isspace(*start)) start++;
	if (!*start || *start == '#') continue;

	if (strncmp(start, "/dev/", 5)) {
	    continue;
	}

	if (strstr(start, "noauto")) continue;

	end = start + strlen(start) - 1;
	while (isspace(*end)) end--;
	end++;
	*end = '\0';

	if (sscanf(start, "%s %s %s", device, mntpoint, type) != 3) {
	    errorWindow(_("Bad line in /mnt/etc/fstab -- aborting"));
	    fclose(f);
	    freeFstab(fstab);
	    umount("/mnt");
	    return INST_ERROR;
	}

	if (strncmp(device, "/dev/hd", 7) && strncmp(device, "/dev/sd", 7))
	    continue;

	entry.device = strdup(device + 5);
	entry.size = 0;
	entry.isMounted = 0;
	entry.doFormat = 0;
	entry.mntpoint = strdup(mntpoint);

	if (!strcmp(type, "ext2")) {
	    entry.type = PART_EXT2;
	    entry.tagName = "Linux native";
	} else if (!strcmp(type, "swap")) {
	    entry.type = PART_SWAP;
	    entry.tagName = "Linux swap";
	    if (canEnableSwap) {
		enableswap(entry.device, 0, 0);
		canEnableSwap = 0;
	    }
	} else if (!strcmp(type, "msdos")) {
	    entry.type = PART_DOS;
	    entry.tagName = "DOS 16-bit >=32";
	} else if (!strcmp(type, "hpfs")) {
	    entry.type = PART_HPFS;
	    entry.tagName = "OS/2 HPFS";
	} else {
	    entry.type = PART_OTHER;
	    entry.tagName = "Unknown";
	}

	addFstabEntry(&fstab, entry);
    }

    fclose(f);

    umount("/mnt");

    freeFstab(*finalFstab);
    *finalFstab = fstab;

    return 0;
}
