/*
 *	File selection menu
 *	Copyright
 *		(C) 1992 Joseph H. Allen
 *
 *	This file is part of JOE (Joe's Own Editor)
 */
#include "config.h"
#include "types.h"

#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif

#include "b.h"
#include "blocks.h"
#include "menu.h"
#include "path.h"
#include "tty.h"
#include "utils.h"
#include "va.h"
#include "w.h"

typedef struct tab TAB;

extern int smode;		/* ??? */

struct tab {
	char *path;		/* current directory */
	char *pattern;		/* search pattern */
	int len;		/* no. entries in files */
	char **files;		/* array of file names */
	char **list;
	char *type;		/* file type array */
	int prv;
	char *orgpath;
	char *orgnam;
};

#define F_DIR		1	/* type codes for file type array */
#define F_NORMAL	2
#define F_EXEC		4

/* Read matching files from a directory
 *  Directory is given in tab.path
 *  Pattern is given in tab.pattern
 *
 * Returns with -1 if there was an error
 * Otherwise returns index to file with inode given in prv
 * len and files are set with the file names
 * type is set with the file types
 */

static int get_entries(TAB *tab, int prv)
{
	int a;
	int which = 0;
	char *oldpwd = pwd();
	char **files;

	if (chpwd(tab->path))
		return -1;
	files = (char **) rexpnd(tab->pattern);
	if (!files) {
		chpwd(oldpwd);
		return -1;
	}
	if (!aLEN(files)) {
		chpwd(oldpwd);
		return -1;
	}
	tab->len = aLEN(files);
	varm(tab->files);
	tab->files = files;
	vasort(files, tab->len);
	if (tab->type)
		joe_free(tab->type);
	tab->type = (char *) joe_malloc(tab->len);
	for (a = 0; a != tab->len; a++) {
		struct stat buf;
		mset(&buf, 0, sizeof(struct stat));

		stat(files[a], &buf);
		if (buf.st_ino == prv)
			which = a;
		if ((buf.st_mode & S_IFMT) == S_IFDIR)
			tab->type[a] = F_DIR;
		else if (buf.st_mode & (0100 | 0010 | 0001))
			tab->type[a] = F_EXEC;
		else
			tab->type[a] = F_NORMAL;
	}
	chpwd(oldpwd);
	return which;
}

static void insnam(BW *bw, char *path, char *nam)
{
	P *p = pdup(bw->cursor);

	p_goto_bol(p);
	p_goto_eol(bw->cursor);
	bdel(p, bw->cursor);
	if (sLEN(path)) {
		binsm(bw->cursor, sv(path));
		p_goto_eol(bw->cursor);
		if (path[sLEN(path) - 1] != '/') {
			binsm(bw->cursor, sc("/"));
			p_goto_eol(bw->cursor);
		}
	}
	binsm(bw->cursor, sv(nam));
	p_goto_eol(bw->cursor);
	prm(p);
	bw->cursor->xcol = piscol(bw->cursor);
}

/* Given a menu structure with a tab structure as its object,
 * a pattern and path set in the tab structure:
 *
 * Load the menu with a list of file names and set the file name in
 * the prompt window to the directory the menu was read in from.
 * If flg is set, treload attempts to position to the previous directory
 * which was visited.
 *
 * Returns with -1 if there was an error
 * Returns with 0 for success
 */

static int treload(MENU *m, int flg)
{
	TAB *tab = (TAB *) m->object;	/* The menu */
	W *w = m->parent;	/* Window menu is in */
	BW *bw = (BW *) w->win->object;	/* The prompt window */
	int x;
	int which;
	struct stat buf;

	if ((which = get_entries(tab, tab->prv)) < 0)
		return -1;
	if (tab->path && tab->path[0])
		stat(tab->path, &buf);
	else
		stat(".", &buf);
	tab->prv = buf.st_ino;
	if (!flg)
		which = 0;

	tab->list = vatrunc(tab->list, aLEN(tab->files));

	for (x = 0; tab->files[x]; ++x) {
		char *s = vsncpy(NULL, 0, sv(tab->files[x]));

		tab->list = vaset(tab->list, x, s);
		if (tab->type[x] == F_DIR)
			tab->list[x] = vsadd(tab->list[x], '/');
		else if (tab->type[x] == F_EXEC)
			tab->list[x] = vsadd(tab->list[x], '*');
	}
	ldmenu(m, tab->list, which);
	insnam(bw, tab->path, tab->pattern);
	return 0;
}

static void rmtab(TAB *tab)
{
	vsrm(tab->orgpath);
	vsrm(tab->orgnam);
	varm(tab->list);
	vsrm(tab->path);
	vsrm(tab->pattern);
	varm(tab->files);
	if (tab->type)
		joe_free(tab->type);
	joe_free(tab);
}
/*****************************************************************************/
/****************** The user hit return **************************************/
/*****************************************************************************/
static int tabrtn(MENU *m, int cursor, TAB *tab)
{
	if (tab->type[cursor] == F_DIR) {	/* Switch directories */
		char *orgpath = tab->path;
		char *orgpattern = tab->pattern;
		char *e = endprt(tab->path);

		if (!strcmp(tab->files[cursor], "..") && sLEN(e)
		    && !(e[0] == '.' && e[1] == '.' && (!e[2] || e[2] == '/')))
			tab->path = begprt(tab->path);
		else {
			tab->path = vsncpy(NULL, 0, sv(tab->path));
			tab->path = vsncpy(sv(tab->path), sv(m->list[cursor]));
		}
		vsrm(e);
		tab->pattern = vsncpy(NULL, 0, sc("*"));
		if (treload(m, 0)) {
			msgnw(m->parent, "Couldn't read directory ");
			vsrm(tab->pattern);
			tab->pattern = orgpattern;
			vsrm(tab->path);
			tab->path = orgpath;
			return -1;
		} else {
			vsrm(orgpattern);
			vsrm(orgpath);
			return 0;
		}
	} else {		/* Select name */
		BW *bw = m->parent->win->object;

		insnam(bw, tab->path, tab->files[cursor]);
		rmtab(tab);
		m->object = NULL;
		m->abrt = NULL;
		wabort(m->parent);
		return 0;
	}
}
/*****************************************************************************/
/****************** The user hit backspace ***********************************/
/*****************************************************************************/
static int tabbacks(MENU *m, int cursor, TAB *tab)
{
	char *orgpath = tab->path;
	char *orgpattern = tab->pattern;
	char *e = endprt(tab->path);

	if (sLEN(e))
		tab->path = begprt(tab->path);
	else {
		wabort(m->parent);
		return 0;
	}
	vsrm(e);
	tab->pattern = vsncpy(NULL, 0, sc("*"));

	if (treload(m, 1)) {
		msgnw(m->parent, "Couldn't read directory ");
		vsrm(tab->pattern);
		tab->pattern = orgpattern;
		vsrm(tab->path);
		tab->path = orgpath;
		return -1;
	} else {
		vsrm(orgpattern);
		vsrm(orgpath);
		return 0;
	}
}
/*****************************************************************************/
static int tababrt(BW *bw, int cursor, TAB *tab)
{
	insnam(bw, tab->orgpath, tab->orgnam);
	rmtab(tab);
	return -1;
}
/*****************************************************************************/
/****************** Create a tab window **************************************/
/*****************************************************************************/
int cmplt(BW *bw)
{
	MENU *new;
	TAB *tab;
	P *p, *q;
	char *cline, *tmp;
	long a, b;

	tab = (TAB *) joe_malloc(sizeof(TAB));
	new = mkmenu(bw->parent, NULL, tabrtn, tababrt, tabbacks, 0, tab, NULL);
	if (!new) {
		joe_free(tab);
		return -1;
	}

	tab->files = NULL;
	tab->type = NULL;
	tab->list = NULL;
	tab->prv = 0;
	tab->len = 0;

	p = pdup(bw->cursor);
	p_goto_bol(p);
	q = pdup(bw->cursor);
	p_goto_eol(q);
	tmp = brvs(p, (int) (q->byte - p->byte));
	cline = parsens(tmp, &a, &b);
	vsrm(tmp);
	prm(p);
	prm(q);

	tab->pattern = namprt(cline);
	tab->path = dirprt(cline);
	tab->orgnam = vsncpy(NULL, 0, sv(tab->pattern));
	tab->orgpath = vsncpy(NULL, 0, sv(tab->path));
	tab->pattern = vsadd(tab->pattern, '*');
	vsrm(cline);

	if (treload(new, 0)) {
		wabort(new->parent);
		ttputc(7);
		return -1;
	} else if (sLEN(tab->files) == 1)
		return tabrtn(new, 0, tab);
	else if (smode || isreg(tab->orgnam))
		return 0;
	else {
		char *com = mcomplete(new);

		vsrm(tab->orgnam);
		tab->orgnam = com;
		wabort(new->parent);
		smode = 2;
		ttputc(7);
		return 0;
	}
}
