#	list menu routines
#
#	a hodgepodgery where we let the user go wild...
#
#	The routines here enable users to load or create new definitions
#	for lists,  We do a fair amount of error checking, and give the
#	user three different [1] ways of defining a new list.
#
#	[1] with the forthcoming addition of parsing an existing
#	record to derive a list definition from it...
#
#	This really should be in a package of its own, a lot of it
#	is improv, or scat, perl.
#
$list_template_file = '';

#	main list menu
%list_menu_cmds = (
	'?',    '$arg = "l"; eval $cmds{"help"}',
	'c',    '&list_create_edit',
	'i',    '&list_create_interactive',
	'l',    '&list_load',
	'!',    '&quick_shell',
	' ',    '&pop_level',
	'@',    'eval $cmds{"eval_perl_expression"}',
	'-',    '&push_previous_menu',
);

#	for the interactive menu
%list_creation_cmds = (
	'?',    '$arg = "i"; eval $cmds{"help"}',
        'd',    '&list_creation_input("DESCRIPTION")',
        'g',    '&list_creation_input("GLOBAL_SEARCH")',
        'l',    '&list_creation_input("LOCAL_VAR")',
        'm',    '&list_creation_input("MAIN_KEY")',
        'r',    '&list_creation_input("DYNAMIC_REF")',
        't',    '&list_creation_input("LIST_TITLE")',
        '0',    '&list_show_fields', 
        'R',    '$unresolved_list = ""; &list_reset_wd',
        'U',    '&list_interactive_update',
	'!',    '&quick_shell',
	' ',    '&pop_level',
	'@',    'eval $cmds{"eval_perl_expression"}',
);

#	register our list creation input routine to keys 1..9
foreach $i (1..9) {
    $list_creation_cmds{$i} = "&list_creation_data_field($i)";
}

#	as with every other SoftListian menu...
$list_menu_cmds{pack("c", 27)} = '&main_menu';           # ESC
$list_creation_cmds{pack("c", 27)} = '&main_menu';       # ESC


#	list_menu - create and switch among lists
sub list_menu {
	push (@menu_stack, "&list_menu");
        while ($menu_stack[$#menu_stack] =~ "&list_menu") {
		print TTYOUT <<"+++";
$clear_str
                List Menu $init_message 

	current list: $list_syms{"LIST_TITLE"}

        ?       help

	c       create a new list (editor)
        i       interactively create a new list
        l       load and use a list

+++

            print TTYOUT "\tyour choice >> "; &flush;
	    read (TTYIN, $ans, 1);
	    eval ($list_menu_cmds{$ans}) || 
				print TTYOUT "hey, no such option: $ans\n";
	}
	1;
}


#	list_create_edit - use an editor to define list template
sub list_create_edit {
    print TTYOUT <<"EOS";
create...
$clear_str
	Creating a List...

        You may create an entirely new list here.  Just type in a
short name such as "PhoneList" or "coffee types" or "CD Collection".
Any spaces will get converted to "_" (you will end up with "coffee_types",
"CD_Collection", etc.).

        You'll use an editor ("$ENV{'EDITOR'}") to define a list template.

        Don't worry, documentation on how to define the list will be
there with you in the editor.

        If you don't want to do this, just press the RETURN key...
EOS
    &list_get_list_name(".template");
    if ($list_template_file =~ /^\.template$/) {
	print TTYOUT "...skipping..."; sleep 1;
	return 1;
    }
    local ($pushd_arg[0]) = $DIRS{'CATALOG'};
    &pushd(*main_dir, *pushd_arg);

    open(LIST_TEMPLATE, "> $DIRS{'CATALOG'}/$list_template_file") ||
	    warn "can't open template file...\n";

		print LIST_TEMPLATE <<"Instructions";
##	List Template file: $list_template_file
##
##      Note that all lines starting with "##" (such as this one) are
##	just comments containing documentation.  All comment lines are
##	ignored when the list is defined.  Most of this file is
##	documentation.  You will be making changes to define a
##	LIST_TITLE, a MAIN_KEY, one or more DATA_FIELDS, and
##	possibly how to handle GLOBAL_SEARCH and DESCRIPTION.
##	This is all that you need to define most lists.  Additional
##	customization is possible and is outlined farther on in
##	the file, but most of the time you can simply save your list
##	and quit the editor before that point.


##	LIST_TITLE
##
##	Lists have a title that you will see in menus.  This can and
##	should be a longer, more descriptive phrase than the one you used
##	for the name of the template.  For this and every other field,
##	leave the first word (i.e. "LIST_TITLE") alone.  SoftList
##	needs it to do its work.
##
##	A sample title line would look like:
##	LIST_TITLE Current Versions Of Software
##
##	Go ahead and change the list title:

LIST_TITLE  Change me to the list title


##	A note on naming of keystrokes and data fields:
##
##	The fields that you make up will show up in the Add and
##	Search menus.  You should use unique, lowercase keystrokes for
##	the fields you define.  SoftList uses uppercase letters in the
##	Add and Search menus.  This gives you more freedom to choose
##	keystrokes, and also helps to separate SoftList operations
##	from choice of fields.  SoftList reserves some other keystrokes
##	for its own use.  These are talked about in the "Custom Fields"
##	section.
##
##	This field also takes a letter for which keystroke to use
##	in menus.  It's a good idea to use the first letter of the field
##	as the keystroke (like 'n' for Name, 'p' for Phone Number, etc.).
##	If you specify an uppercase letter, SoftList will coerce it
##	to be lowercase.


##	MAIN_KEY
##
##	Lists have one field that will be used for quick lookups.
##	For an address list, this would often be the "Name" field.
##      This is called the MAIN_KEY.  Another way you might want to
##	think about this is: "the primary data field".
##
##	A few possible MAIN_KEY fields:
##	MAIN_KEY l location
##	MAIN_KEY a address
##	MAIN_KEY d date
##
##	pick your MAIN_KEY field here (edit the keystroke and field name):

MAIN_KEY a address

##	if you used the 'g' or 'd' key, you will need to change
##	the GLOBAL_SEARCH or DESCRIPTION field below.


##	DATA_FIELD
##
##	Now we will specify additional data fields.  The DATA_FIELD uses
##	the same format as the MAIN_KEY.  You may "comfortably" use
##	a total of 8 data fields.  Beyond that, you will run out of
##	screen space for your menus on a 24 line terminal.  Here are
##	some sample DATA_FIELD lines.  Just uncomment them if you want
##	to use them (leading space is fine, it'll be ignored):
##
##	DATA_FIELD c contact
##	DATA_FIELD c city
##	DATA_FIELD n name
##	DATA_FIELD m model
##	DATA_FIELD b band
##	DATA_FIELD a author
##
##	specify one or more data fields:

DATA_FIELD d dogs
DATA_FIELD c cats

##	if you used the 'g' or 'd' key, you will need to change
##	the GLOBAL_SEARCH or DESCRIPTION field below.


##	GLOBAL_SEARCH
##
##	SoftList gives you a way of searching for a pattern in
##	records, regardless of field.  This global search is
##	seen in the Search Menu, and by default it is:
##	GLOBAL_SEARCH g globally
##
##	If you want to use the 'g' keystroke for one of the data
##	fields, then you need to change the one used for the
##	global search function.  You may uncomment one of the
##	sample lines, *or* change the definition below.  In
##	case you do both, the last one wins.

GLOBAL_SEARCH g globally


##	some alternatives:
##	GLOBAL_SEARCH * globally
##	GLOBAL_SEARCH a all
##	GLOBAL_SEARCH e everywhere
##	GLOBAL_SEARCH e entire record


##	DESCRIPTION
##
##	SoftList records may have a short description or summary
##	attached to them.  In the Add Menu, there is an item
##	for adding the description or summary to a record.  It is
##	defined as:
##	DESCRIPTION d description
##
##	If you wish to use the 'd' keystroke for one of the data
##	fields, then you need to change the one used for the description
##	function.  You may uncomment one of the sample lines, *or*
##	change the definition below.  In case you do both, the last one wins.

DESCRIPTION d description

##	some alternatives:
##	DESCRIPTION a attachment
##	DESCRIPTION < additional
##	DESCRIPTION < description
##	DESCRIPTION c comments
##	DESCRIPTION s summary
##	DESCRIPTION m more info


##	You may be done!
##
##	That is all you need to do for most lists!  Here's a quick
##	checklist to see if you can save this file and successfully
##	get a list definition, or if you will need to change another
##	thing or two that is farther on in this file.
##
##	* you specified a LIST_TITLE such as:
##	LIST_TITLE Famous Cats in History
##
##	* you specified a MAIN_KEY, such as:
##	MAIN_KEY f feline name
##
##	* you should specify only one MAIN_KEY.  If you specify more
##	than one, then the last one in the file wins.
##
##	* you specified one or more DATA_FIELD's, such as:
##	DATA_FIELD a age
##	DATA_FIELD b breed
##
##	* Important: if you used the keystroke(s) 'g' or 'd' for one of
##	the data fields, then you should have changed the definition
##	for GLOBAL_SEARCH or DESCRIPTION so that there isn't a conflict.
##
##	* Also Important: did you make a data field called "dynamic"?
##	If so, you will need to change the default way that the
##	field DYNAMIC_REF is handled.  It is the first filed mentioned
##	in the Custom Fields section.
##
##	If this all looks fine, then you may save this file and exit the
##	editor now, and you should get a new list.  If you want to
##      look into some customization, read on...


##	Custom Fields
##
##	At this time, there is an extra field that can be used as part of
##	a list template.  Later, support will be added that will enable
##	you to specify alternate Add, Search, Help, Spool routines, and more.


##	DYNAMIC_REF
##
##	SoftList gives each record the ability to refer to some file
##	or to perform some action.  Presumably this would be used to
##	demonstrate or elaborate on the record.  For example. in a list
##	of different breeds of cats, we might have a line like this in
##	a record:
##
##	dynamic: _X11_BIN/xv _BITMAPS/russian_blue.gif
##
##	From the Search Menu, this reference would be detected, and
##	depending on how the paths _X11_BIN and _BITMAPS resolved,
##	and availablity of files, we would have the option of seeing
##	a bitmap of the Russian Blue breed.  In a nutshell, we're
##	setting aside a field which can be used after a search to
##	obtain relevant information outside of SoftList.  The default
##	definition of this field is:
##	DYNAMIC_REF dynamic
##
##	If you used a data field with the name of "dynamic", then
##	you will want to change the word used here.  A record may
##	have multiple dynamic references, but they must all be referred
##	to the same way within any one type of list.

DYNAMIC_REF dynamic

##	some alternatives:
##	DYNAMIC_REF dref
##	DYNAMIC_REF references
##	DYNAMIC_REF external
##	DYNAMIC_REF link


##	Keys Reserved by SoftList...
##
##	These characters are used by SoftList in menus:
##
##	ESCape	- used to push the Main Menu
##	Space	- used to pop a menu
##	!	- quick shell
##	-	- push previous menu
##	?	- help
##
##	You should stick to using lowercase letters or numbers when
##	defining menus.
Instructions
    
    close (LIST_TEMPLATE);
    system ("$ENV{'EDITOR'} $DIRS{'CATALOG'}/$list_template_file");

    &list_load_template($list_template_file);
    &popd(*main_dir_stack);
    1;
}


#	list_load_template - load a template and make a definition from it
sub list_load_template {
    local($list_template_file) = @_;
    local(%template_filter);

    local($which_field, $field_data);

    # the scope of these next two extends to the routines we'll call
    local($dfield_count) = 0;	# number of data fields seen
    local($locvar_count) = 0;	# number of local variable fields seen

    open(LIST_TEMPLATE, "< $list_template_file") ||
	warn "can't open $list_template_file: $!\n";

    # we really want to deal with a %list_data array directly.  The
    # data we get from a template is in a form that's convenient for
    # users to edit.  Here we convert it to %list_data, without going
    # through a myriad of if-then-elsif's...

    %template_filter = (
	"LIST_TITLE",	"&list_load_template_simple",
	"MAIN_KEY",	"&list_load_template_simple",
	"DATA_FIELD",   "&list_load_template_data",
	"GLOBAL_SEARCH","&list_load_template_simple",
	"DYNAMIC_REF",  "&list_load_template_simple",
	"DESCRIPTION",  "&list_load_template_simple",
	"LOCAL_VAR",    "&list_load_template_locvar",
    );


    # start writing out our %list_data array...
    $list_def_file = &basename($list_template_file, ".template");
    $list_def_file .= ".pl";

    &list_start_def_file($list_def_file);

    # skip most of the lines which are either comments or newlines.  What
    # is left over from that is very likely to be the name of a field.
    while(<LIST_TEMPLATE>) {
	next if (/##/);
	next if (/^$/);
        chop;

	s/^\s+//;		# kill leading ws
        ($which_field, $field_data) = split(/[ \t]/, $_, 2);
	$field_data =~ s="=\\"=g; # you should be able to type '"'

        if ($template_filter{$which_field}) {
	    $fstr = "$template_filter{$which_field}(\"$which_field\",
					\"$field_data\")";
	    eval $fstr;
	}
    }

    &list_write_def_file;
    

    close(LIST_TEMPLATE);
    %list_data = ();	# kill off old definition
    %list_syms = ();
    %list_data = %working_definition;

    # parse_definition will holler about conflicting keystrokes...
    &parse_definition;
    1;
}

#	load template helper routines...
sub list_load_template_simple {
    local($which_field, $field_data) = @_;
    $working_definition{$which_field} = $field_data;
    1;
}

sub list_load_template_data {
    local($which_field, $field_data) = @_;
    $dfield_count++;
    $working_definition{"$which_field $dfield_count"} = $field_data;
    1;
}

sub list_load_template_locvar {
    local($field_data) = @_;
    1;
}


#	list_create_interactive - create a template via menu input
sub list_create_interactive {
    print TTYOUT "

        Going to interactively define a list.  Use the '?' key for help...";

    &list_get_list_name(".pl");
    if ($list_name =~ /^$/) {
	$list_name = "unnamed";
    }
    if ($list_name =~ "unnamed") {
	&list_reset_wd;
    }
    &list_create_interactive_menu;
    1;
}

sub list_create_interactive_menu {
    push (@menu_stack, "&list_create_interactive_menu");
    $unresolved_list = '';
    while ($menu_stack[$#menu_stack] =~ "&list_create_interactive_menu") {
	print TTYOUT <<"+++";
$clear_str
                Creating a List ($list_name)...

        ?       help
	
        t       title of list          "$working_definition{'LIST_TITLE'}"
        m       main key field         "$working_definition{'MAIN_KEY'}"
        1-9     edit/add data field
        0       scroll through data fields
        r       dynamic reference      "$working_definition{'DYNAMIC_REF'}"
        g       global search label    "$working_definition{'GLOBAL_SEARCH'}"
        d       description label      "$working_definition{'DESCRIPTION'}"

        R       reset fields
        U       update, generate the list

+++

        print TTYOUT "\tyour choice >> "; &flush;
	read (TTYIN, $ans, 1);
	if ($ans =~ / /) {
	    $unresolved_list = "true";
	}
	print TTYOUT "$ans..."; &flush;
	eval ($list_creation_cmds{$ans}) || 
				print TTYOUT "hey, no such option: $ans\n";

    }
    1;
}

sub list_creation_input {
    local($which_field) = @_;

    @command_history = @perl_history;
    &get_input("\n\n\tenter info for $which_field >> ");
    $working_definition{"$which_field"} = $command_history[$#command_history];
    @perl_history = @command_history;
    1;
}


sub list_creation_data_field {
    local($which_field) = @_;
    local($tmp_str);
    @command_history = @perl_history;

    # first, a sanity check...we really don't want to deal with
    # skipping around from DATA_FIELD 1 to DATA_FIELD 5 if there
    # isn't a 2, 3, and 4!  We also don't want to annoy the user with some
    # "don't hit the '5' key with only two fields you goofball!" message.
    # A solution is to check how many fields we *really* have so far,
    # spit out a message that we are adjusting and that we understand
    # that the user really meant to press a key closer to 1 and that
    # they just slipped (:-), and get on with it...

    $actual_fields = grep(/DATA_FIELD/, keys %working_definition);
    if (($which_field - 1) > $actual_fields) {
	print TTYOUT <<"EOS";


        hmm...there are only $actual_fields data fields so far, adjusting...
EOS
    	$which_field = $actual_fields + 1;
    }

    $force_input = $working_definition{"DATA_FIELD $which_field"};
    &get_input("\n\n\tenter info for DATA_FIELD $which_field >> ");
    $tmp_str = $command_history[$#command_history];
    if ($tmp_str !~ /^$/) {
#	&inspect_field(*tmp_str);
        $working_definition{"DATA_FIELD $which_field"} = $tmp_str;
    } else { 
        &arrive("nothing entered..."); sleep 1; &depart;
    }
    @perl_history = @command_history;
    1;
}


#	list_load - load a template or definition (.pl) %list_data file
#
#	what happens here is that we take whichever file is newer
#
#	This isn't bulletproof, but it should handle a Super Soaker...
sub list_load {
    local ($pushd_arg[0]) = $DIRS{'CATALOG'};
    local($try_template) = 0;
    &pushd (*main_dir_stack, *pushd_arg);


    $list_file = &file_prefix($list_file);
    &assure_set(*list_file, "unnamed");
    $force_input = $list_file;
    &file_str_prompt("\tlist name >> ");
    $list_file = $filename_history[$#filename_history];

    # later on, we will put something in here to remap the '?' key so
    # that it spits out which lists are available...

    if ($list_file =~ /^$/) {
	print TTYOUT "...no file, skipping..."; sleep 1;
	&popd(*main_dir_stack);
	return 1;
    }
 
    #	what is the filename extension they type in?
    $the_ext = &extension($list_file);

    # once again, canonicalize..
    $list_file = &file_prefix($list_file);
    $lf_pl = $list_file . ".pl";
    $lf_tmpl = $list_file . ".template";

    $most_recent = &newest_file($lf_pl, $lf_tmpl);

    # ok, at this point, we have one of the following situations:
    #
    # no file	                           - punt
    # user asked specifically for .pl and:
    # template only, or template newer than definition - make a definition
    # user asked specifically for .template:
    # overwrite the .pl after confirmation?

    # a definition?
    if ($most_recent =~ $lf_pl) {
	print TTYOUT "...loading definition $lf_pl..."; &flush;
	eval `cat $lf_pl`;
	&parse_definition;
	&popd(*main_dir_stack);
	return 1;
    }

    # a newer template, or definition doesn't exist yet?
    if ($most_recent =~ $lf_tmpl) {
	print TTYOUT "...loading template $lf_tmpl..."; &flush;
        &list_load_template($lf_tmpl);
	&popd(*main_dir_stack);
	return 1;
    }

    &popd (*main_dir_stack);	
    1;
}

sub list_interactive_update {
    %list_data = %working_definition;
    &parse_definition;
    &list_start_def_file($list_name);
    &list_write_def_file;
    1;
}

sub list_reset_wd {
    if ($unresolved_list =~ /^$/) {
	%working_definition = %list_data;
	$num_data_fields = 0;
    }
    1;
}

sub list_show_fields {
    print TTYOUT "\n\n\t0 to keep scrolling, any other key returns to menu\n";
    print TTYOUT "\tDATA_FIELD ";
    local($ans) = "0";
    local($tmp_str);
    local($scroll_field) = 0;

    $actual_fields = grep(/DATA_FIELD/, keys %working_definition);
    while ($ans =~ "0") {
	$scroll_field++;
	if ($scroll_field > $actual_fields) {
	    $scroll_field = 1;
	}
	
	# not strictly needed...just avoiding a long line...
	$tmp_str = $working_definition{"DATA_FIELD $scroll_field"};
	&arrive("$scroll_field is \"$tmp_str\"...");
        read (TTYIN, $ans, 1);
        &depart;
    }
    1;
}

sub list_get_list_name {
    local($type) = @_;
    local ($pushd_arg[0]) = $DIRS{'CATALOG'};
    &pushd (*main_dir_stack, *pushd_arg);
    $list_template_file = &basename($list_template_file, $type);

    if ((! defined($list_template_file)) ||
	$list_template_file =~ /^.template$/) {
	$list_template_file = "unnamed";
    } else {
	$list_template_file = &basename($list_template_file, ".template");
    }
    $force_input = $list_template_file;

    @command_history = @perl_history;
    &get_input("\tenter the list name to create >> ");
    $list_template_file = $command_history[$#command_history];
    $list_template_file =~ y/0-9a-zA-Z-_/_/cs;
    # a little sanity check in case the user typed the .template suffix
    $list_template_file = &basename($list_template_file, ".template");
    $list_name = $list_template_file;
    $list_template_file = "$list_template_file.template";
    &popd (*main_dir_stack);	
    1;
}

sub inspect_field {
#    local(*the_field) = @_;

 #   print "in inspect field we see $the_field\n"; sleep 2;


    1;
}

sub list_start_def_file {
    local($the_file) = @_;

    print TTYOUT "writing to $the_file....\n"; sleep 2;
    open(LIST_DEF, "> $the_file") ||
 	warn "can't open $the_file: $!\n";
    $date_str = `date`;

    print LIST_DEF "
# $the_file - list definition file
# this file created by SoftList on $date_str

\%list_data = (
";
    1;
}


sub list_write_def_file {
    while (($key, $val) = each %working_definition) {
	print LIST_DEF "\t\"$key\", \"$val\",\n";
    }
    print LIST_DEF "\n);";
    close(LIST_DEF); 
    1;
}
1;
