/*
 * Copyright (c)2004 The DragonFly Project.  All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 
 *   Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 * 
 *   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.
 * 
 *   Neither the name of the DragonFly Project nor the names of its
 *   contributors may be used to endorse or promote products derived
 *   from this software without specific prior written permission. 
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``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 THE
 * COPYRIGHT HOLDERS OR CONTRIBUTORS 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. 
 */

/*
 * flow.c
 * Workflow logic for installer.
 * $Id: flow.c,v 1.9 2004/07/11 08:39:32 cpressey Exp $
 */

#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <dfui/dfui.h>
#include <dfui/fspred.h>
#include <dfui/chain.h>
#include <dfui/mem.h>
#ifdef DEBUG
#include <dfui/dump.h>
#endif

#include "flow.h"
#include "diskutil.h"
#include "uiutil.h"
#include "commands.h"
#include "package.h"
#include "pathnames.h"
#include "functions.h"
#include "confed.h"
#include "cmds.h"

/*** GLOBALS ***/

struct dfui_connection *c = NULL;
void (*state)(void) = NULL;

struct system_storage *ss = NULL;

/*** EXTERNS ***/

extern char dfly_root[];
extern int do_reboot;

/*** STATES ***/

/*
 * The installer works like a big state machine.  Each major form is
 * a state.  When the user has filled out the form satisfactorily,
 * and selects "OK", there is a transition to the next state, in a
 * mostly-linear order towards the final, "successfully installed"
 * state.  The user may also "Cancel", which generally causes a
 * transition to the previous state (but may also take them back to
 * the very first state in some cases.)
 *
 * Installer States:
 * - Welcome to DragonFly	required
 * - Begin Installation		required
 * - Select Disk		required
 * - Format Disk		optional	dd, fdisk
 * - Select Partition		required	dd, disklabel
 * - Create Subpartitions	required	disklabel, newfs
 * - Install DragonFly		required	swapon, mkdir, mount, cpdup
 * - Install Bootstrap		optional	boot0cfg
 * - Reboot			optional	reboot
 */

/*
 * state_welcome: the start state of the installer state machine.
 * Briefly describe DragonFly to the user, and present them with
 * a set of reasonable options of how to proceed.
 */
void
state_welcome(void)
{
	struct dfui_form *f;
	struct dfui_response *r;

	f = dfui_form_create(
		"welcome",
		"Welcome to DragonFly!",

		"Welcome to the DragonFly BSD Live CD."
		"\n\n"
		"DragonFly BSD is an efficient and elegant BSD Unix-"
		"derived operating system.  For more information, see"
		"\n\n"
		"http://www.dragonflybsd.org/"
		"\n\n"
		"From this CD, you can boot into DragonFly ``live'' "
		"(without installing it) to evaluate it, to install it "
		"manually, or to troubleshoot problems with an "
		"existing installation, using either a command prompt "
		"or menu-driven utilities."
		"\n\n"
		"Also, you can use this automated application to assist "
		"you in installing DragonFly BSD on this computer and "
		"configuring it once it is installed."
		"",

		"",

		"h",	"special", 	"nympha_welcome",

		"a",	"install",	"Install DragonFly BSD",
			"Install DragonFly BSD on a HDD or HDD partition on this computer", "",
		"a",	"configure",	"Configure an Installed System",
			"Configure a DragonFly BSD system once it has been installed on HDD", "",	
		"a",	"utilities",	"Live CD Utilities",
			"Utilities to work with disks, diagnostics, and the LiveCD Environment", "",
		"a",	"livecd",	"Exit to Live CD",
			"Exit this program to a login prompt with access to the LiveCD", "",
		"a",	"reboot",	"Reboot this Computer",
			"Reboot this computer (e.g. to boot into a newly installed system)", "",
		NULL
	);

	if (!dfui_be_present(c, f, &r)) abort_backend();

	if (dfui_id_is(dfui_response_get_action_id(r), "install")) {
		state = state_begin_install;
	} else if (dfui_id_is(dfui_response_get_action_id(r), "configure")) {
		ss->selected_disk = NULL;
		ss->selected_slice = NULL;
		state = state_configure_menu;
	} else if (dfui_id_is(dfui_response_get_action_id(r), "utilities")) {
		state = state_utilities_menu;
	} else if (dfui_id_is(dfui_response_get_action_id(r), "livecd")) {
		state = NULL;
	} else if (dfui_id_is(dfui_response_get_action_id(r), "reboot")) {
		state = state_reboot;
	}

	dfui_form_free(f);
	dfui_response_free(r);
}

void
state_configure_menu(void) 
{
	struct dfui_form *f = NULL;
	struct dfui_response *r = NULL;
	int done = 0;

	if (ss->selected_disk == NULL || ss->selected_slice == NULL) {
		discover_storage(ss);
		ss->selected_disk = fn_select_disk(ss,
		  "Select the disk containing the installation.",
		  "Return to Welcome Menu");
		if (ss->selected_disk == NULL) {
			state = state_welcome;
			return;
		}

		ss->selected_slice = fn_select_slice(ss->selected_disk,
		  "Select the primary partition containing the installation.",
		  "Return to Welcome Menu");
		if (ss->selected_slice == NULL) {
			state = state_welcome;
			return;
		}
	}

	if (!mount_target_system("mnt")) {
		inform("Target system could not be mounted.");
		state = state_welcome;
		return;
	}

	while (!done) {
		f = dfui_form_create(
			"configure_menu",
			"Configure an Installed System",
			"The options on this menu allow you to configure a "
			"DragonFly BSD system after it has already been "
			"installed.",
			"",
			"h",	"role", "menu",

                        "a",    "set_timezone",
                                "Select timezone",
                                "Set the Time Zone of your physical location", "",
			"a",    "set_datetime",
				"Set date and time",
				"Set the Time and Date of your machine", "",

			"a",	"root_passwd",	"Set root password",
				"Set the password that the root (superuser) account will use", "",
			"a",	"add_user",	"Add a user",
				"Add a user to the system", "",
			/*
			"a",	"cvsup_sources","Synchronize system sources", "", "",
			*/
			"a",	"assign_ip",	"Configure network interfaces",
				"Set up network interfaces (NICs, ethernet, TCP/IP, etc)", "",
			"a",    "assign_hostname_domain",
                                "Configure hostname and domain",
                                "Configure the hostname and domain for this system", "",

			"a",	"set_kbdmap",
				"Set keyboard map",
				"Set what kind of keyboard layout you have", "",
			"a",	"set_vidfont",
				"Set console font",
				"Set how the characters on your video console look", "",
			"a",	"set_scrnmap",
				"Set screen map",
				"Set how characters are translated before console display", "",

			"a",	"install_pkgs",	"Install extra software packages",
				"Install third-party software packages from the LiveCD", "",
			"a",	"remove_pkgs",	"Remove software packages",
				"Remove third-party software packages from the installed system", "",

			"a",	"cancel",	"Return to Welcome Menu", "", "",
			NULL
		);

		if (!dfui_be_present(c, f, &r)) abort_backend();

		if (dfui_id_is(dfui_response_get_action_id(r), "root_passwd")) {
			fn_root_passwd();
		} else if (dfui_id_is(dfui_response_get_action_id(r), "add_user")) {
			fn_add_user();
		} else if (dfui_id_is(dfui_response_get_action_id(r), "cvsup_sources")) {
			fn_cvsup_sources();
		} else if (dfui_id_is(dfui_response_get_action_id(r), "install_pkgs")) {
			fn_install_packages();
		} else if (dfui_id_is(dfui_response_get_action_id(r), "remove_pkgs")) {
			fn_remove_packages();
		} else if (dfui_id_is(dfui_response_get_action_id(r), "assign_ip")) {
			fn_assign_ip("mnt/");
		} else if (dfui_id_is(dfui_response_get_action_id(r), "assign_hostname_domain")) {
			fn_assign_hostname_domain("mnt/");
		} else if (dfui_id_is(dfui_response_get_action_id(r), "set_kbdmap")) {
			fn_set_kbdmap("mnt/");
		} else if (dfui_id_is(dfui_response_get_action_id(r), "set_vidfont")) {
			fn_set_vidfont("mnt/");
		} else if (dfui_id_is(dfui_response_get_action_id(r), "set_scrnmap")) {
			fn_set_scrnmap("mnt/");
		} else if (dfui_id_is(dfui_response_get_action_id(r), "set_timezone")) {
			fn_set_timezone("mnt/");
                } else if (dfui_id_is(dfui_response_get_action_id(r), "set_datetime")) {
                        fn_assign_datetime("mnt/");
		} else if (dfui_id_is(dfui_response_get_action_id(r), "cancel")) {
			state = state_welcome;
			done = 1;
		}

		dfui_form_free(f);
		dfui_response_free(r);
	}

	unmount_all_under(NULL, "%smnt", dfly_root);
}

void 
state_utilities_menu(void) 
{
	struct dfui_form *f;
	struct dfui_response *r;

	discover_storage(ss);

	f = dfui_form_create(
		"utilities_menu",
		"Live CD Utilities Menu",
		"On these submenus you will find utilities to help "
		  "you set up your Live CD environment, diagnose "
		  "and analyse this system, and work with "
		  "the devices attached to this computer.",
		"",
		"h",	"role", "menu",
		"a",	"environment", "LiveCD Environment",
			"Configure the LiveCD Environment", "",
		"a",	"diagnostics", "System Diagnostics",
			"Probe and display detailed information about this system", "",
		"a",	"diskutil", "Disk Utilities",
			"Format and check hard drives and floppy disks", "",
		"a",	"livecd", "Exit to Live CD",
			"Exit this program to a login prompt with access to the LiveCD", "",
		"a",	"reboot",
			"Reboot this Computer", "", "",
		"a",	"cancel",
			"Return to Welcome Menu", "", "",
		NULL
	);

	if (!dfui_be_present(c, f, &r)) abort_backend();

	if (dfui_id_is(dfui_response_get_action_id(r), "environment")) {
		state = state_environment_menu;
	} else if (dfui_id_is(dfui_response_get_action_id(r), "diagnostics")) {
		state = state_diagnostics_menu;
	} else if (dfui_id_is(dfui_response_get_action_id(r), "diskutil")) {
		state = state_diskutil_menu;
	} else if (dfui_id_is(dfui_response_get_action_id(r), "livecd")) {
		state = NULL;
	} else if (dfui_id_is(dfui_response_get_action_id(r), "reboot")) {
		state = state_reboot;
	} else if (dfui_id_is(dfui_response_get_action_id(r), "cancel")) {
		state = state_welcome;
	}

	dfui_form_free(f);
	dfui_response_free(r);
}

void 
state_environment_menu(void) 
{
	struct dfui_form *f;
	struct dfui_response *r;
	int done = 0;

	while (!done) {
		f = dfui_form_create(
			"environment_menu",
			"Live CD Environment Menu",
			"On this menu you will find utilities to help you "
			  "set up your Live CD environment.\n\nNote "
			  "that these functions affect only the LiveCD "
			  "environment you are currently using, and they will "
			  "not affect any system that may be installed on "
			  "this computer UNLESS you subsequently choose to "
			  "install DragonFly from this environment, in which "
			  "case they will be copied to the newly installed "
			  "system.",
			"",
			"h",	"role", "menu",

			"a",    "set_timezone",
                                "Select timezone",
                                "Set the Time Zone of your physical location", "",
			"a",    "set_datetime",
				"Set date and time",
				"Set the Time and Date of your machine", "",

			"a",	"set_kbdmap",
				"Set keyboard map",
				"Set what kind of keyboard layout you have", "",
			"a",	"set_vidfont",
				"Set console font",
				"Set how the characters on your video console look", "",
			"a",	"set_scrnmap",
				"Set screen map",
				"Set how characters are translated before console display", "",

			"a",    "assign_hostname_domain",
                                "Configure hostname and domain",
                                "Configure the hostname and domain for this system", "",
			"a",	"assign_ip",
				"Configure network interfaces",
				"Set up network interfaces (NICs, ethernet, TCP/IP, etc)", "",

			"a",	"cancel",
				"Return to Utilities Menu", "", "",
			NULL
		);

		if (!dfui_be_present(c, f, &r)) abort_backend();

		if (dfui_id_is(dfui_response_get_action_id(r), "set_kbdmap")) {
			fn_set_kbdmap("");
		} else if (dfui_id_is(dfui_response_get_action_id(r), "set_vidfont")) {
			fn_set_vidfont("");
		} else if (dfui_id_is(dfui_response_get_action_id(r), "set_scrnmap")) {
			fn_set_scrnmap("");
                } else if (dfui_id_is(dfui_response_get_action_id(r), "assign_hostname_domain")) {
			fn_assign_hostname_domain("");
		} else if (dfui_id_is(dfui_response_get_action_id(r), "assign_ip")) {
			fn_assign_ip("");
		} else if (dfui_id_is(dfui_response_get_action_id(r), "set_timezone")) {
			fn_set_timezone("");
                } else if (dfui_id_is(dfui_response_get_action_id(r), "set_datetime")) {
                        fn_assign_datetime("");

		} else if (dfui_id_is(dfui_response_get_action_id(r), "cancel")) {
			state = state_utilities_menu;
			done = 1;
		}

		dfui_form_free(f);
		dfui_response_free(r);
	}
}

void 
state_diagnostics_menu(void) 
{
	struct dfui_form *f;
	struct dfui_response *r;
	int done = 0;

	while (!done) {
		f = dfui_form_create(
			"utilities_menu",
			"Live CD Diagnostics Menu",
			"These functions can help you diagnose this system.",
			"",
			"h",	"role", "menu",
		
			"a",	"show_dmesg",
				"Display system startup messages",
				"Display system startup messages (dmesg)", "",
			"a",	"pciconf",
				"Display PCI devices",
				"Display PCI devices (pciconf)", "",
			"a",	"pnpinfo",
				"Display Plug'n'Play ISA devices",
				"Display Plug'n'Play ISA devices (pnpinfo)", "",
			"a",	"atacontrol",
				"Display ATA devices",
				"Display ATA devices (atactl)", "",
			NULL
		);

		if (is_file("%sboot/memtest86.flp.bz2"), dfly_root) {
			dfui_form_action_add(f, dfui_id_new("memtest86"),
			    dfui_info_new("Create memtest86 Floppy",
			    "Create a floppy which boots into a dedicated memory-test",
			    ""));
		}

		if (is_program("%susr/local/bin/memtest"), dfly_root) {
			dfui_form_action_add(f, dfui_id_new("memtest"),
			    dfui_info_new("Run On-Line Memory Test",
			    "Test the memory in the machine while running",
			    ""));
		}

		dfui_form_action_add(f, dfui_id_new("cancel"),
		    dfui_info_new("Return to Utilities Menu", "", ""));

		if (!dfui_be_present(c, f, &r)) abort_backend();

		if (dfui_id_is(dfui_response_get_action_id(r), "show_dmesg")) {
			fn_show_dmesg();
		} else if (dfui_id_is(dfui_response_get_action_id(r), "pciconf")) {
			fn_show_pciconf();
		} else if (dfui_id_is(dfui_response_get_action_id(r), "pnpinfo")) {
			fn_show_pnpinfo();
		} else if (dfui_id_is(dfui_response_get_action_id(r), "atacontrol")) {
			fn_show_atacontrol();
		} else if (dfui_id_is(dfui_response_get_action_id(r), "memtest")) {
			fn_memtest();
		} else if (dfui_id_is(dfui_response_get_action_id(r), "memtest86")) {
			fn_create_memtest86_floppy();
		} else if (dfui_id_is(dfui_response_get_action_id(r), "cancel")) {
			state = state_utilities_menu;
			done = 1;
		}

		dfui_form_free(f);
		dfui_response_free(r);
	}
}

void 
state_diskutil_menu(void) 
{
	struct dfui_form *f;
	struct dfui_response *r;
	int done = 0;

	while (!done) {
		f = dfui_form_create(
			"utilities_menu",
			"Disk Utilities Menu",
			"These functions let you manipulate the storage devices "
			"attached to this computer.",
			"",

			"h",	"role", "menu",

			"a",	"format_hdd",
				"Format a hard disk drive", "", "",
			"a",	"wipe_start_of_disk",
				"Wipe out the start of a disk", "", "",
			"a",	"wipe_start_of_slice",
				"Wipe out the start of a primary partition", "", "",
			"a",	"install_bootblocks",
				"Install bootblocks on disks", "", "",
			"a",	"format_msdos_floppy",
				"Format an MSDOS floppy", "", "",
			NULL
		);

		if (is_file("%sboot/cdboot.flp.bz2"), dfly_root) {
			dfui_form_action_add(f, dfui_id_new("create_cdboot_floppy"),
			    dfui_info_new("Create a CDBoot floppy",
			    "",
			    ""));
		}

		dfui_form_action_add(f, dfui_id_new("cancel"),
		    dfui_info_new("Return to Utilities Menu", "", ""));

		if (!dfui_be_present(c, f, &r)) abort_backend();

		if (dfui_id_is(dfui_response_get_action_id(r), "format_hdd")) {
			ss->selected_disk = NULL;
			ss->selected_slice = NULL;
			fn_format_disk();
		} else if (dfui_id_is(dfui_response_get_action_id(r), "wipe_start_of_disk")) {
			fn_wipe_start_of_disk();
		} else if (dfui_id_is(dfui_response_get_action_id(r), "wipe_start_of_slice")) {
			fn_wipe_start_of_slice();
		} else if (dfui_id_is(dfui_response_get_action_id(r), "install_bootblocks")) {
			fn_install_bootblocks(	"Select the disks on which "
						"you wish to install "
						"bootblocks.",
						"Return to Utilities Menu");
		} else if (dfui_id_is(dfui_response_get_action_id(r), "format_msdos_floppy")) {
			fn_format_msdos_floppy();
		} else if (dfui_id_is(dfui_response_get_action_id(r), "create_cdboot_floppy")) {
			fn_create_cdboot_floppy();
		} else if (dfui_id_is(dfui_response_get_action_id(r), "cancel")) {
			state = state_utilities_menu;
			done = 1;
		}

		dfui_form_free(f);
		dfui_response_free(r);
	}
}

/** INSTALLER STATES **/

/*
 * state_begin_install: Briefly describe the install process
 * to the user, and let them proceed (or not.)
 */
void
state_begin_install(void)
{
	struct dfui_form *f;
	struct dfui_response *r;

	f = dfui_form_create(
		"begin_install",
		"Begin Installation",

		"This experimental application will install DragonFly BSD on "
		"one of the hard disk drives attached to this computer. "
		"It has been designed to make it easy to install "
		"DragonFly BSD in the typical case. "
		"If you have special requirements that are not addressed "
		"by this installer, or if you have problems using it, you "
		"are welcome to install DragonFly BSD manually.  To do so, "
		"select Exit to Live CD, login as root, and follow "
		"the instructions given in the file /README ."
		"\n\n"
		"NOTE! As with any installation process, YOU ARE "
		"STRONGLY ENCOURAGED TO BACK UP ANY IMPORTANT DATA ON THIS "
		"COMPUTER BEFORE PROCEEDING!"
		"",

		"Some situations in which you might not wish to use this "
		"installer are:\n\n"
		"- you want to install DragonFly onto a logical/extended "
		  "partition;\n"
		"- you want to install DragonFly onto a ``dangerously "
		  "dedicated'' disk; or\n"
		"- you want full and utter control over the install process."
		"",

		"h",	"special", 	"nympha_begin_install",
		"h",	"minimum_width", "76",

		"a",	"proceed",	"Install DragonFly BSD",
					"", "",
		"a",	"cancel",	"Return to Welcome Menu",
					"", "",
		"a",	"livecd",	"Exit to Live CD",
					"", "",
		NULL
	);

	if (!dfui_be_present(c, f, &r)) abort_backend();

	if (dfui_id_is(dfui_response_get_action_id(r), "proceed")) {
		discover_storage(ss);
		state = state_select_disk;
	} else if (dfui_id_is(dfui_response_get_action_id(r), "livecd")) {
		state = NULL;
	} else if (dfui_id_is(dfui_response_get_action_id(r), "cancel")) {
		state = state_welcome;
	}

	dfui_form_free(f);
	dfui_response_free(r);
}

/*
 * state_select_disk: ask the user on which physical disk they wish
 * to install DragonFly.
 */
void
state_select_disk(void)
{
	struct disk *d;
	int num_disks = 0;

	for (d = ss->disk_head; d != NULL; d = d->next)
		num_disks++;

	if (num_disks == 0) {
		inform(	"The installer could not find any disks suitable "
			"for installation (IDE or SCSI) attached to this "
			"computer.  If you wish to install DragonFly on "
			"an unorthodox storage device, you will have to "
			"exit to a LiveCD command prompt and install it "
			"manually, using the file /README as a guide.");
		state = state_welcome;
		return;
	}

	ss->selected_disk = fn_select_disk(ss,
	    "Select a disk on which to install DragonFly.",
	    "Return to Begin Installation");
	if (ss->selected_disk == NULL) {
		state = state_begin_install;
	} else {
#if 0
		if (ss->selected_disk->capacity < DISK_MIN) {
			inform(	"WARNING: you should have a disk "
				"at least %dM in size, or "
				"you may encounter problems trying to "
				"install DragonFly.", DISK_MIN);
		}
#endif
		state = state_format_disk;
	}
}

/*
 * state_format_disk: ask the user if they wish to format the disk they
 * selected.
 */
void
state_format_disk(void)
{
	switch (dfui_be_present_dialog(c, "How Much Disk?",
		"Use Entire Disk|Use Part of Disk|Return to Select Disk",
		"Select how much of this disk you want to use for DragonFly BSD.\n\n%s",
		ss->selected_disk->desc)) {
	case 1:
		/* Entire Disk */
		if (measure_activated_swap_from_disk(ss->selected_disk) > 0) {
			switch(dfui_be_present_dialog(c,
			"Swap already active",
			"Reboot|Return to Select Disk",
			"Some subpartitions on the primary partition "
			  "of this disk are already activated as swap. "
			  "Since there is no way to deactivate swap in "
			  "DragonFly BSD once it is activated, in order "
			  "to edit the subpartition layout of this "
			  "primary partition, you must first reboot.")) {
			case 1:
				state = state_reboot;
				return;
			case 2:
				state = state_select_disk;
				return;
			default:
				abort_backend();
			}
		}

		if (fn_format_disk()) {
			state = state_create_subpartitions;
		} else {
			state = state_format_disk;
		}
		break;
	case 2:
		/* Part of Disk */
		state = state_select_slice;
		break;
	case 3:
		/* Return */
		state = state_select_disk;
		break;
	default:
		abort_backend();
		break;
	}
}

/*
 * state_select_slice: ask the user which slice they wish to install
 * DragonFly on.  In order to avoid confusing them, refer to it as
 * a primary partition, but tell them what BSD has traditionally called
 * it, too.
 */
void
state_select_slice(void)
{
	ss->selected_slice = fn_select_slice(ss->selected_disk,
		"Select the existing primary partition (also "
		  "known as a `slice' in the BSD tradition) on "
		  "which to install DragonFly BSD.\n\n"
		  "Note that if you do not have any existing "
		  "primary partitions on this disk, you must "
		  "first create some. This installer does not "
		  "currently have the ability to do this, so "
		  "you will have to exit and run fdisk (in "
		  "DOS or *BSD) or parted (in Linux) to do so.",
		"Return to Select Disk");

	if (ss->selected_slice == NULL) {
		state = state_select_disk;
	} else {
		if (measure_activated_swap_from_slice(ss->selected_disk,
		    ss->selected_slice) > 0) {
			switch(dfui_be_present_dialog(c,
			"Swap already active",
			"Reboot|Return to Select Primary Partition",
			"Some subpartitions on the selected primary "
			  "partition are already activated as swap. "
			  "Since there is no way to deactivate swap in "
			  "DragonFly BSD once it is activated, in order "
			  "to edit the subpartition layout of this "
			  "primary partition, you must first reboot.")) {
			case 1:
				state = state_reboot;
				return;
			case 2:
				state = state_select_slice;
				return;
			default:
				abort_backend();
			}
		}

		if (ss->selected_slice->capacity < DISK_MIN) {
			inform(	"WARNING: you should have a primary "
				"partition at least %dM in size, or "
				"you may encounter problems trying to "
				"install DragonFly.", DISK_MIN);
		}

		if (confirm_dangerous_action(
		    "WARNING!  ALL data in primary partition #%d,\n\n%s\n\non the "
		    "disk\n\n%s\n\n will be IRREVOCABLY ERASED!\n\nAre you "
		    "ABSOLUTELY SURE you wish to take this action?  This is "
		    "your LAST CHANCE to cancel!",
		    ss->selected_slice->number,
		    ss->selected_slice->desc,
		    ss->selected_disk->desc)) {
			if (!format_slice(ss->selected_disk->device,
			    ss->selected_slice->number)) {
				inform(	"Primary partition #%d was "
					"not correctly formatted, and may "
					"now be in an inconsistent state. "
					"We recommend re-formatting it "
					"before proceeding.",
					ss->selected_slice->number);
			} else {
				inform("Primary partition #%d was formatted.",
				    ss->selected_slice->number);
				state = state_create_subpartitions;
			}
		} else {
			inform("Action cancelled - no primary partitions were formatted.");
			state = state_select_slice;
		}
	}
}

/*
 * state_create_subpartitions: let the user specify what subpartitions they
 * want on the disk, how large each should be, and where it should be mounted.
 */
void
state_create_subpartitions(void)
{
	if (measure_activated_swap_from_slice(ss->selected_disk,
	    ss->selected_slice) > 0) {
		switch(dfui_be_present_dialog(c, "Swap already active",
			ss->selected_disk->we_formatted ?
			"Reboot|Return to Select Disk" :
			"Reboot|Return to Select Primary Partition",
			"Some subpartitions on the selected primary "
			  "partition are already activated as swap. "
			  "Since there is no way to deactivate swap in "
			  "DragonFly BSD once it is activated, in order "
			  "to edit the subpartition layout of this "
			  "primary partition, you must first reboot.")) {
		case 1:
			state = state_reboot;
			return;
		case 2:
			state = ss->selected_disk->we_formatted ?
			    state_select_disk : state_select_slice;
			return;
		default:
			abort_backend();
		}
	}

	if (fn_create_subpartitions()) {
		state = state_install_os;
	} else {
		state = ss->selected_disk->we_formatted ?
		    state_select_disk : state_select_slice;
	}
}

/*
 * state_install_os: actually put DragonFly on the disk.
 */
void
state_install_os(void)
{
	struct dfui_form *f;
	struct dfui_response *r;

	f = dfui_form_create(
		"install_os",
		"Install OS",
		"Everything is now ready to install the actual files which "
		  "comprise the DragonFly BSD operating system "
		  "on the selected partition of the selected disk.\n\n"
		  "Note that this process will take quite a while to finish. "
		  "You may wish to take a break now and come back to the "
		  "computer in a short while.",

		"",

		"h",	"role", "confirm",
		"h",	"special", "nympha_install_os",

		"a",	"ok", "Begin Installing Files", "", "",
		"a",	"cancel", "Return to Create Subpartitions", "", "",
		NULL
	);

	if (!dfui_be_present(c, f, &r)) abort_backend();

	if (dfui_id_is(dfui_response_get_action_id(r), "cancel")) {
		state = state_create_subpartitions;
	} else {
		if (fn_install_os()) {
			state = state_install_bootstrap;
		}
	}

	dfui_form_free(f);
	dfui_response_free(r);
}

/*
 * state_install_bootstrap: put boot0 bootblocks on selected disks.
 */
void
state_install_bootstrap(void)
{
	fn_install_bootblocks(
	    "You may now wish to install bootblocks on one or more disks. "
	    "If you already have a boot manager installed, you can skip "
	    "this step (but you may have to configure your boot manager "
	    "seperately.)  If you installed DragonFly on a disk other "
	    "than your first disk, you will need to put the bootblock "
	    "on at least your first disk and the DragonFly disk.",
	    "Skip this Step");
	state = state_finish_install;
}

/*
 * Finish up the install.
 */
void
state_finish_install(void)
{
	switch (dfui_be_present_dialog(c, "DragonFly BSD is Installed!",
	    "Configure this System|Reboot|Return to Welcome Menu",
	    "Congratulations!\n\n"
	    "DragonFly BSD has successfully been installed on "
	    "this computer. You may now proceed to configure "
	    "the installation. Alternately, you may wish to "
	    "reboot the computer and boot into the installed "
	    "system to confirm that it works.")) {
	case 1:
		state = state_configure_menu;
		break;
	case 2:
		state = state_reboot;
		break;
	case 3:
		state = state_welcome;
		break;
	default:
		abort_backend();
	}
}

/*
 * state_reboot: reboot the machine.
 */
void
state_reboot(void)
{
	struct dfui_form *f;
	struct dfui_response *r;

	f = dfui_form_create(
		"reboot",
		"Reboot",
		"This machine is about to be shut down. "
		"After the machine has reached its shutdown state, "
		"you may remove the CD from the CD-ROM drive tray "
		"and press Enter to reboot from the HDD.",

		"",

		"h",	"role", "confirm",

		"a",	"ok", "Reboot", "", "",
		"a",	"cancel", "Return to Welcome Menu", "", "",
		NULL
	);

	if (!dfui_be_present(c, f, &r)) abort_backend();

	if (dfui_id_is(dfui_response_get_action_id(r), "cancel")) {
		state = state_welcome;
	} else {
		do_reboot = 1;
		state = NULL;
	}

	dfui_form_free(f);
	dfui_response_free(r);
}

/*** MAIN ***/

void
flow(int transport, char *rendezvous)
{
	FILE *log;

	log = fopen(INSTALLER_TEMP "install.log", "w");
	fprintfo(log, "Installer started\n");
	fprintfo(log, "-----------------\n");

	fprintfo(log, "+ Creating DFUI connection on ``%s''\n", rendezvous);
	c = dfui_connection_new(transport, rendezvous);
	fprintfo(log, "+ Connecting on ``%s''\n", rendezvous);
	dfui_be_start(c);

	ss = system_storage_new();

	fprintfo(log, "+ Starting installer state machine\n");

	if (log != NULL)
		fclose(log);

	/*
	 * Execute the state machine here.  The global function pointer
	 * variable `state' points to the next state_* function to execute.
	 * Before it exits, this function should set `state' to the next
	 * state to make a transition to, or NULL to indicate that the
	 * state machine is finished.
	 */
	for (state = state_welcome; state != NULL; )
		state();

	system_storage_free(ss);

	dfui_be_stop(c);
}
