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

#include "hd.h"
#include "fs.h"
#include "install.h"
#include "methods.h"
#include "net.h"
#include "windows.h"

/* This was split into two pieces to keep the initial install program small */

static int nfsPrepare(struct installMethod * method);
static int cdromPrepare(struct installMethod * method);
int floppyRoot(struct installMethod * method);
static int nfsGetSetup(char ** hostptr, char ** dirptr);

static struct installMethod methods[] = {
    { "Local CDROM", 		"cdrom", 0, cdromPrepare, NULL, NULL,
		NULL, NULL },
    { "NFS image", 		"nfs", 0, nfsPrepare, NULL, NULL,
		NULL, NULL },
    { "hard drive",		"hd", 0, floppyRoot, NULL, NULL,
		NULL, NULL },
    { "FTP", 			"ftp", 0, floppyRoot, NULL, NULL,
		NULL, NULL },
} ;
static int numMethods = sizeof(methods) / sizeof(struct installMethod);

static int installMethodWindow(struct installMethod ** method) {
    newtComponent form, listbox, okay, text;
    struct installMethod * newMethod;
    int i;

    newtOpenWindow(21, 4, 38, 15, "Installation Method");

    form = newtForm(NULL, NULL, 0);

    text = newtTextbox(3, 1, 32, 2, NEWT_TEXTBOX_WRAP);
    newtTextboxSetText(text, "What type of media contains the packages "
			"to be installed?");

    listbox = newtListbox(12, 4, 0, NEWT_LISTBOX_RETURNEXIT);

    for (i = 0; i < numMethods; i++) {
	newtListboxAddEntry(listbox, methods[i].name, methods + i);
    }

    okay = newtButton(14, 11, "Ok");

    newtFormAddComponents(form, text, listbox, okay, NULL);

    newtRunForm(form);

    newMethod = newtListboxGetCurrent(listbox);

    newtFormDestroy(form);
    newtPopWindow();

    *method = newMethod;

    return 0;
}

int chooseInstallMethod(struct installMethod ** method) {
    int rc;
    do {
	rc = installMethodWindow(method);
	if (rc) return rc;

	if ((*method)->prepareImage) {
	    rc = (*method)->prepareImage((*method));
	    if (rc == INST_ERROR) return rc;
	}
    } while (rc);

    return 0;
}

static int nfsGetSetup(char ** hostptr, char ** dirptr) {
    newtComponent form, okay, cancel, siteEntry, dirEntry, answer, text;
    char * site, * dir;

    if (*hostptr) {
	site = *hostptr;
	dir = *dirptr;
    } else {
	site = "";
	dir = "";
    }

    newtOpenWindow(15, 4, 50, 14, "NFS Setup");

    form = newtForm(NULL, NULL, 0);
    okay = newtButton(10, 10, "Ok");
    cancel = newtButton(30, 10, "Cancel");

    text = newtTextbox(1, 1, 47, 5, NEWT_TEXTBOX_WRAP);
    newtTextboxSetText(text,
	"Please enter the following information:\n"
	"\n"
	"    o the name or IP number of your NFS server\n"
	"    o the directory on that server containing\n"
	"      Red Hat Linux for your architecture");

    newtFormAddComponent(form, newtLabel(3, 7, "NFS server name  :"));
    newtFormAddComponent(form, newtLabel(3, 8, "Red Hat directory:"));

    siteEntry = newtEntry(22, 7, site, 24, &site, NEWT_ENTRY_SCROLL);
    dirEntry = newtEntry(22, 8, dir, 24, &dir, NEWT_ENTRY_SCROLL);

    newtFormAddComponents(form, text, siteEntry, dirEntry, okay, cancel, NULL);

    answer = newtRunForm(form);
    if (answer == cancel) {
	newtFormDestroy(form);
	newtPopWindow();
	
	return INST_CANCEL;
    }

    *hostptr = strdup(site);
    *dirptr = strdup(dir);

    newtFormDestroy(form);
    newtPopWindow();

    return 0;
}

static int cdromPrepare(struct installMethod * method) {
    char * cddev;
    struct driversLoaded * dl = NULL;
    int rc;

    messageWindow("Note", "Insert your Red Hat CD into your CD drive now");

    while (1) {
	rc = setupCDdevice(&cddev, &dl);
	if (rc) return rc;

	rc = doMount(cddev, "/tmp/rhimage", "iso9660", 1);
	if (rc) {
	    removeCDmodule(&dl);
	    messageWindow("Error", 
		    "I could not mount a CD on device /dev/%s", cddev);
	    continue;
	}

	if (access("/tmp/rhimage/RedHat", R_OK)) {
	    umount("/tmp/rhimage");
	    removeCDmodule(&dl);
	    messageWindow("Error", "That CDROM device does not seem "
			  "to contain a Red Hat CDROM.");
	    continue;
	}

	break;
    }

    if (access("/usr/bin/runinstall2", R_OK)) {
	symlink("/tmp/rhimage/RedHat/instimage/lib", "/lib");
	symlink("/tmp/rhimage/RedHat/instimage/etc", "/etc");
	symlink("/tmp/rhimage/RedHat/instimage/usr/bin", "/usr/bin");
    }

    return 0;
}

static int nfsPrepare(struct installMethod * method) {
    struct netInterface intf;
    struct netConfig netc;
    struct driversLoaded * dl = NULL;
    char * host = NULL, * dir = NULL;
    char * buf;
    enum { NFS_STEP_NET, NFS_STEP_INFO, NFS_STEP_MOUNT, NFS_STEP_DONE }
		step = NFS_STEP_NET;
    int rc;

    intf.isConfigured = netc.isConfigured = 0;

    while (step != NFS_STEP_DONE) {
	switch (step) {
	  case NFS_STEP_NET:
	    rc = bringUpNetworking(&intf, &netc, &dl);
	    if (rc) return rc;
	    step = NFS_STEP_INFO;
	    break;

	  case NFS_STEP_INFO:
	    rc = nfsGetSetup(&host, &dir);
	    if (rc == INST_CANCEL)
		step = NFS_STEP_NET;
	    else if (rc == INST_ERROR)
		return INST_ERROR;
	    else
		step = NFS_STEP_MOUNT;
	    break;

	  case NFS_STEP_MOUNT:
	    if (!strlen(host) || !strlen(dir))
		rc = INST_ERROR;
	    else {
		buf = malloc(strlen(host) + strlen(dir) + 10);
		strcpy(buf, host);
		strcat(buf, ":");
		strcat(buf, dir);
		rc = doMount(buf, "/tmp/rhimage", "nfs", 1);
		free(buf);
	    }

	    if (rc) {
		step = NFS_STEP_INFO;
		messageWindow("Error", 
			"I could not mount that directory from the server");
	    } else {
	        if (access("/tmp/rhimage/RedHat", R_OK)) {
		    step = NFS_STEP_INFO;
		    messageWindow("Error", "That directory does not seem "
				  "to contain a Red Hat installation tree.");
		    umount("/tmp/rhimage");
		} else
		    step = NFS_STEP_DONE;
	    }

	    break;

	  case NFS_STEP_DONE:
	    break;
	}
    }

    free(host);
    free(dir);

    writeNetInterfaceConfig("/tmp", &intf);
    writeNetConfig("/tmp", &netc, &intf, 1);
    writeModuleConf("/tmp", dl);

    if (access("/usr/bin/runinstall2", R_OK)) {
	symlink("/tmp/rhimage/RedHat/instimage/lib", "/lib");
	symlink("/tmp/rhimage/RedHat/instimage/etc", "/etc");
	symlink("/tmp/rhimage/RedHat/instimage/usr/bin", "/usr/bin");
    }

    return 0;
}

int floppyRoot(struct installMethod * method) { 
    newtComponent form, text, okay, cancel, answer;

    if (access("/usr/bin/runinstall2", R_OK)) {
	newtOpenWindow(20, 4, 40, 15, "Second Floppy");

	text = newtTextbox(1, 1, 38, 5, NEWT_TEXTBOX_WRAP);
	newtTextboxSetText(text, 
	    "This install method requires a second disk. Please remove "
	    "the boot disk currently in your drive and replace it with "
	    "the Red Hat Supplementary Install disk.");

	okay = newtButton(6, 10, "Ok");
	cancel = newtButton(24, 10, "Cancel");

	form = newtForm(NULL, NULL, 0);
	newtFormAddComponents(form, text, okay, cancel, NULL);
      
	answer = newtRunForm(form);

	newtFormDestroy(form);
	newtPopWindow();

	if (answer == cancel) return INST_CANCEL;

	while (doMount("fd0", "/tmp/image", "ext2", 1) ||
	       access("/tmp/image/usr/bin/runinstall2", R_OK)) {
	    /* in case the mount succeeded */
	    umount("/tmp/image");

	    newtOpenWindow(20, 4, 40, 15, "Second Floppy");
	    text = newtTextbox(1, 1, 38, 5, NEWT_TEXTBOX_WRAP);

	    newtTextboxSetText(text, 
		"I failed to mount the floppy. Please insert the "
		"Red Hat Supplementary Install disk, or choose "
		"Cancel to pick a different installation process.");

	    okay = newtButton(6, 10, "Ok");
	    cancel = newtButton(24, 10, "Cancel");

	    form = newtForm(NULL, NULL, 0);
	    newtFormAddComponents(form, text, okay, cancel);
	  
	    answer = newtRunForm(form);
	    
	    newtFormDestroy(form);
	    newtPopWindow();

	    if (answer == cancel) return INST_CANCEL;
	}

	symlink("/tmp/image/lib", "/lib");
	symlink("/tmp/image/etc", "/etc");
	symlink("/tmp/image/usr/bin", "/usr/bin");
    }

    return 0;
}

