/*
** 1998-07-05 -	The Split command, handy for, er, splitting files into parts. Mucho GUI.
** 1999-03-07 -	Altered for the new selection/generic handler. Hum, I really should finish
**		this command off some day...
*/

#include "gentoo.h"

#include <fcntl.h>

#include "errors.h"
#include "sizeutil.h"
#include "fileutil.h"
#include "dirpane.h"
#include "dialog.h"
#include "overwrite.h"
#include "guiutil.h"

#include "cfg_gui.h"
#include "cmd_generic.h"
#include "cmd_split.h"

#define	CMD_ID	"split"

#define	THRESH_KB	(1.0)
#define	THRESH_MB	(0.5)
#define	THRESH_GB	(0.5)

/* ----------------------------------------------------------------------------------------- */

typedef struct {
	GtkWidget	*vbox;		/* This really is required. */
	GtkWidget	*label;		/* Tells the user what's up. */
	GtkWidget	*mode;		/* Option menu for selecting mode of split. */
	GtkWidget	*mvbox;		/* Sub-vbox for the mode-specific widgets. */
	GtkWidget	*nhbox;		/* Hbox for the naming issue. */
	GtkWidget	*nformat;	/* Entry widget for name formatter. */
	GtkObject	*nbadj;		/* Adjustment for index base. */
	GtkWidget	*nbase;		/* Spin widget for index base. */
	GtkObject	*nsadj;		/* Adjustment for index step. */
	GtkWidget	*nstep;		/* Spin widget for index step. */

	GtkWidget	*ssframe;	/* Frame for "split to fixed size". */
	GtkWidget	*sssize;	/* Combo holding the wanted size. */
	GtkWidget	*snframe;	/* Frame for "split to fixed number". */

	MainInfo	*min;
	gint		curr_mode;	/* 0 for fixed size, 1 for fixed amount. */
} SplitInfo;

/* ----------------------------------------------------------------------------------------- */

/* 1998-09-01 -	Pretty much rewrote this function, now that the sizeutil module exists. */
static void spt_body(MainInfo *min, DirPane *src, DirRow *row, GtkWindow *win, gpointer user)
{
	char		*name, tmp1[2 * MAXNAMLEN], *tptr = tmp1, siz1[32], siz2[32];
	SplitInfo	*spi = (SplitInfo *) user;

	gtk_window_set_title(win, "Split");

	name = DP_ROW_NAME(row);
	tptr += g_snprintf(tptr, sizeof tmp1, "Split \"%s\"\nFile is ", name);
	sze_put_size(DP_ROW_LSTAT(row).st_size, siz1, sizeof siz1, SZE_BYTES);
	sze_put_size(DP_ROW_LSTAT(row).st_size, siz2, sizeof siz2, SZE_AUTO);
	if(strcmp(siz1, siz2))
		sprintf(tptr, "%s (%s)", siz1, siz2);
	else
		strcat(tptr, siz1);
	gtk_label_set_text(GTK_LABEL(spi->label), tmp1);
	gtk_entry_set_text(GTK_ENTRY(spi->nformat), name);
	gtk_entry_append_text(GTK_ENTRY(spi->nformat), ".%03d");
}

/* 1998-09-01 -	Construct destination file name for piece number <piece> in a split. */
static void get_name(SplitInfo *spi, DirPane *dst, gchar *buf, int piece)
{
	gchar	*ptr = buf;
	gint	base = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spi->nbase)),
		step = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spi->nstep)),
		num  = base + step * piece;

	ptr += sprintf(ptr, "%s/", dst->dir.path);
	sprintf(ptr, gtk_entry_get_text(GTK_ENTRY(spi->nformat)), num);
}

/* 1998-09-01 -	Split file on row <row> of <src>, producing a bunch of segment files in <dst>.
**		Each segment will have the same size.
** 1998-09-12 -	Didn't have any closing of the output files. I really should be shot. Also
**		fixed the protection bits for the output; now same as the source file.
*/
static int size_split(SplitInfo *spi, DirPane *src, DirPane *dst, DirRow *row)
{
	gchar	inname[PATH_MAX], outname[PATH_MAX];
	gsize	seg_size, to_go, chunk;
	gint	ifd, ofd, piece = 0;
	OvwRes	ores;

	ovw_overwrite_begin(spi->min, "\"%s\" exists - continue with split?", 0UL);

	to_go = DP_ROW_LSTAT(row).st_size;
	if((seg_size = sze_get_size(gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(spi->sssize)->entry)))) > 0)
	{
		g_snprintf(inname, sizeof inname, "%s/%s", src->dir.path, DP_ROW_NAME(row));
		if((ifd = open(inname, O_RDONLY)) > 0)
		{
			while(to_go > 0)
			{
				get_name(spi, dst, outname, piece++);
				ores = ovw_overwrite_file(spi->min, outname, dp_full_name(src, DP_ROW_INDEX(src, row)));
				chunk = (to_go > seg_size) ? seg_size : to_go;
				if(ores == OVW_CANCEL)
					break;
				else if(ores == OVW_SKIP)
				{
					if(lseek(ifd, chunk, SEEK_CUR) != (off_t) -1)
						to_go -= chunk;
					else
						break;
				}
				else
				{
					if((ofd = open(outname, O_WRONLY | O_CREAT | O_TRUNC, DP_ROW_LSTAT(row).st_mode | S_IWUSR)) >= 0)
					{
						if(!fut_copy(ifd, ofd, chunk, 1 << 18))
							break;
						to_go -= chunk;
						close(ofd);
					}
					else
						break;
				}
			}
			close(ifd);
		}
	}
	ovw_overwrite_end(spi->min);

	return to_go == 0;
}

static int spt_action(MainInfo *min, DirPane *src, DirPane *dst, DirRow *row, gpointer user)
{
	SplitInfo	*spi = (SplitInfo *) user;
	int		ret = 0;

	if(spi->curr_mode == 0)		/* Fixed-size split? */
		ret = size_split(spi, src, dst, row);
	else				/* Fixed amount split. */
		dlg_dialog_simple("This splitting mode has not been\nimplemented yet! Sorry!", "Beta software", "Next Version...", NULL, NULL);

	if(ret)
		dp_unselect(src, DP_ROW_INDEX(src, row));

	return ret;
}

/* ----------------------------------------------------------------------------------------- */

/* 1998-07-09 -	Build widgetry needed to support splitting to fixed part size. */
static int build_ss(SplitInfo *spi)
{
	GtkWidget	*hbox, *vbox, *label;
	GList		*list = NULL;

	list = g_list_append(list, "  1457000 bytes (3.5\" floppy)");
	list = g_list_append(list, "100431360 bytes (Zip disk)");

	if((spi->ssframe = gtk_frame_new("Fixed Size Split")) != NULL)
	{
		if((vbox = gtk_vbox_new(FALSE, 0)) != NULL)
		{
			hbox = gtk_hbox_new(FALSE, 0);
			label = gtk_label_new("Segment Size");
			gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
			gtk_widget_show(label);
			if((spi->sssize = gtk_combo_new()) != NULL)
			{
				gtk_combo_set_popdown_strings(GTK_COMBO(spi->sssize), list);
				gtk_box_pack_start(GTK_BOX(hbox), spi->sssize, TRUE, TRUE, 0);
				gtk_widget_show(spi->sssize);
				gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0);
				gtk_widget_show(hbox);

				gtk_container_add(GTK_CONTAINER(spi->ssframe), vbox);
				gtk_widget_show(vbox);
				gtk_box_pack_start(GTK_BOX(spi->vbox), spi->ssframe, TRUE, TRUE, 0);
				gtk_widget_show(spi->ssframe);

				return 1;
			}
			gtk_widget_destroy(vbox);
		}
		gtk_widget_destroy(spi->ssframe);
	}
	return 0;
}

/* ----------------------------------------------------------------------------------------- */

/* 1998-09-01 -	Callback for mode option menu. */
static gint evt_mode_select(GtkWidget *wid, gpointer data)
{
	SplitInfo	*spi = (SplitInfo *) data;

	spi->curr_mode = (gint) gtk_object_get_user_data(GTK_OBJECT(wid));

	return TRUE;
}

/* ----------------------------------------------------------------------------------------- */

int cmd_split(MainInfo *min, DirPane *src, DirPane *dst, CmdArg *ca)
{
	static SplitInfo	spi;
	const gchar		*mode[] = { "Fixed size, variable number of parts",
					    "Fixed number of parts, variable sizes",
					    NULL };
	GtkWidget	*hbox, *label;
	GtkWidget	*hsep;

	spi.min	      = min;
	spi.curr_mode = 0;
	if((spi.vbox = gtk_vbox_new(FALSE, 0)) != NULL)
	{
		if((spi.label = gtk_label_new("Split")) != NULL)
		{
			hbox = gtk_hbox_new(FALSE, 0);
			label = gtk_label_new("Mode");
			gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
			gtk_widget_show(label);
			if((spi.mode = gtk_option_menu_new()) != NULL)
			{
				gtk_box_pack_start(GTK_BOX(spi.vbox), spi.label, FALSE, FALSE, 0);
				gtk_widget_show(spi.label);
				if((hsep = gtk_hseparator_new()) != NULL)
				{
					gtk_box_pack_start(GTK_BOX(spi.vbox), hsep, FALSE, FALSE, 4);
					gtk_widget_show(hsep);
				}
				gtk_option_menu_set_menu(GTK_OPTION_MENU(spi.mode), gui_build_menu(mode, GTK_SIGNAL_FUNC(evt_mode_select), &spi));
				gtk_box_pack_start(GTK_BOX(hbox), spi.mode, TRUE, TRUE, 0);
				gtk_widget_show(spi.mode);
				gtk_box_pack_start(GTK_BOX(spi.vbox), hbox, FALSE, FALSE, 0);
				gtk_widget_show(hbox);
				if((spi.nhbox = gtk_hbox_new(FALSE, 0)) != NULL)
				{
					label = gtk_label_new("Name Format");
					gtk_box_pack_start(GTK_BOX(spi.nhbox), label, FALSE, FALSE, 2);
					gtk_widget_show(label);
					spi.nformat = gtk_entry_new();
					gtk_box_pack_start(GTK_BOX(spi.nhbox), spi.nformat, TRUE, TRUE, 0);
					gtk_widget_show(spi.nformat);
					label = gtk_label_new("Base");
					gtk_box_pack_start(GTK_BOX(spi.nhbox), label, FALSE, FALSE, 0);
					gtk_widget_show(label);
					spi.nbadj   = gtk_adjustment_new(0, 0, 31, 1, 16, 16);
					spi.nbase   = gtk_spin_button_new(GTK_ADJUSTMENT(spi.nbadj), 0, 0);
					gtk_box_pack_start(GTK_BOX(spi.nhbox), spi.nbase, FALSE, FALSE, 0);
					gtk_widget_show(spi.nbase);
					label = gtk_label_new("Step");
					gtk_box_pack_start(GTK_BOX(spi.nhbox), label, FALSE, FALSE, 0);
					gtk_widget_show(label);
					spi.nsadj = gtk_adjustment_new(1, 1, 31, 1, 16, 16);
					spi.nstep = gtk_spin_button_new(GTK_ADJUSTMENT(spi.nsadj), 0, 0);
					gtk_box_pack_start(GTK_BOX(spi.nhbox), spi.nstep, FALSE, FALSE, 0);
					gtk_widget_show(spi.nstep);
					gtk_box_pack_start(GTK_BOX(spi.vbox), spi.nhbox, FALSE, FALSE, 2);
					gtk_widget_show(spi.nhbox);
					if(build_ss(&spi))
						return cmd_generic(min, CGF_NOALL | CGF_NODIRS, spt_body, spt_action, NULL, &spi);
				}
			}
			gtk_widget_destroy(spi.label);
		}
		gtk_widget_destroy(spi.vbox);
	}
	return 0;
}
