/***********************************************************************
 * plug_vol2surf.c		- plugin interface to vol2surf computation
 *
 * Provide an interface to the global v2s_plugin_opts structure.
 *
 * - R. Reynolds
 ***********************************************************************
*/

#include "afni.h"
#include "vol2surf.h"

#ifndef ALLOW_PLUGINS
#  error "Plugins not properly set up -- see machdep.h"
#endif

static char * PV2S_main( PLUGIN_interface * );

static char g_help[] = 
    " This plugin provides an interface to the vol2surf options used by afni\n"
    " for the display of the Overlay dataset values on the surfaces in suma.\n"
    " \n"
    " For each Local Domain Parent that afni has received from suma, vol2surf\n"
    " computes a node color overlay and sends it to suma.  This is computed\n"
    " by mapping afni's Overlay data across the 1 or 2 surfaces of each given\n"
    " LDP (Local Domain Parent).\n"
    " \n"
    " By default, each LDP gets a color list based on:\n"
    " \n"
    "   o  if only 1 surface for this LDP     : intersection of the surface\n"
    "      and afni's Overlay sub-brick, subject to afni's color bar and\n"
    "      thresholding (afni voxels that do not survive the threshold take\n"
    "      no part in the mapping)\n"
    " \n"
    "   o  if 2 or more surfaces for this LDP : intersection of the midpoint\n"
    "      of the first 2 surfaces received from suma (for this LDP) and the\n"
    "      Overlay sub-brick in afni, again subject to afni's color bar and\n"
    "      thresholding\n"
    " \n"
    " These defaults are for surfaces that have _not_ been chosen via the\n"
    " plugin interface.  Surfaces chosen via the plugin are, of course,\n"
    " mapped as the user specifies.  Note that to recreate the defaults, use\n"
    " the 'mask' function for 1 surface, and the midpoint function for more\n"
    " than 1.  The 'num steps' and 'seg index' will not be applicable.\n"
    " \n"
    " ---\n"
    " \n"
    " The minimum choices that must be made are on the 'function' line:\n"
    " 'use vol2surf?' (should be 'yes' :), and 'map func' (should be valid).\n"
    " \n"
    " ** Note that as a plugin, surfaces will be unknown at initialization\n"
    "    time.  Therefore the interface is via afni's surface index list.\n"
    "    To see the surface index list, set the debug level to 2, and then\n"
    "    press 'Set+Keep'.  Part of the text output to the _terminal window_\n"
    "    will be '+d valid surface indices and labels: ...'.\n"
    " \n"
    " ** For detailed infomation, please try the command: '3dVol2Surf -help'.\n"
    " \n"
    " ----------------------------- options --------------------------------\n"
    " function:\n"
    " \n"
    "   use vol2surf? : choose 'yes' to make the plugin options active\n"
    "   map func      : choose a function to apply to values along node pair\n"
    "                   segments, embedded in the AFNI volume\n"
    "   seg index     : apply all values along a each segment, or only those\n"
    "                   from unique voxels\n"
    "   num steps     : the number of evenly spaced points each segment will\n"
    "                   be divided into\n"
    " \n"
    " surf pair 0:\n"
    " \n"
    "   apply?        : if no, skip this surface (pair)\n"
    "   surf_A        : choose the afni surface index for surface A\n"
    "   use B?        : form segments by joining nodes from surface A with\n"
    "                   nodes on surface B (an alternate choice is to use\n"
    "                   only surface A, along with its normals)\n"
    "   surf_B        : choose the afni surface index for surface B\n"
    " \n"
    " surf pair 1:\n"
    " \n"
    "   apply?        : if no, skip this surface (pair)\n"
    "   surf_A        : choose the afni surface index for surface A\n"
    "   use B?        : form segments by joining nodes from surface A with\n"
    "                   nodes on surface B (an alternate choice is to use\n"
    "                   only surface A, along with its normals)\n"
    "   surf_B        : choose the afni surface index for surface B\n"
    " \n"
    " normals:\n"
    " \n"
    "   use normals?  : segments are formed from each node from surface A\n"
    "                   along its normal, and of length 'norm len'\n"
    "   norm len      : this will be the length of each segment\n"
    "   norm dir      : choose what to do with the normal directions:\n"
    "                     check   : let vol2surf try to decide\n"
    "                     keep    : keep the default directions\n"
    "                     reverse : reverse the default directions\n"
    " \n"
    " offsets:\n"
    " \n"
    "   NOTE - segments are considered directed from the node on surface A\n"
    "          in the direction of surface B (or the normal), so if f_p1 is\n"
    "          positive each segment gets shorter (going toward surface B),\n"
    "          but if f_pn is positive each segment gets longer (still moving\n"
    "          in the direction from surface A to surface B)\n"
    " \n"
    "   f_p1_mm       : move each segment starting point this number of\n"
    "                   millimeters toward surface B\n"
    "   f_pn_mm       : move each segment ending point this number of\n"
    "                   millimeters farther from surface A\n"
    "   f_p1_fr       : move each segment starting point this fraction of the\n"
    "                   distance toward surface B\n"
    "   f_pn_fr       : move each segment ending point this fraction of the\n"
    "                   distance farther from surface A\n"
    " \n"
    " out of range:\n"
    " \n"
    "   oob nodes?    : whether to still output results for those nodes which\n"
    "                   are out-of-bounds (i.e. outside the bounding box of\n"
    "                   the AFNI overlay dataset)\n"
    "   oob value     : if out-of-bounds nodes are output, this value will be\n"
    "                   given to them\n"
    "   oom nodes?    : whether to still output results for those nodes which\n"
    "                   are out-of-mask (i.e. masked by the threshold brick)\n"
    "   oob value     : if out-of-mask nodes are output, this value will be\n"
    "                   given to them\n"
    " output:\n"
    " \n"
    "   first node    : starting index for nodes to output\n"
    "   last node     : ending index for nodes to output (0 means 'nodes-1')\n"
    "                   (sorry, that means you cannot output from 0 to 0)\n"
    "   outfile.1D    : also send the results to this file (in 1D format)\n"
    "   outfile.niml  : also send the results to this file (in niml format)\n"
    " \n"
    " debug level:\n"
    " \n"
    "   level         : provide this level of debug output (0-5)\n"
    "   node          : provide additional debug output for this node\n"
    " \n"
    " \n"
    " Author: R Reynolds\n"
    " \n"
    " ----------------------------------------------------------------------\n"
    "   History:\n"
    " \n"
    "   1.0  9 September 2004 [rickr]\n"
    "     - initial version\n"
    " \n"
    "   1.1  16 September 2004 [rickr]\n"
    "     - init gp_index to -1 (set it in afni)\n"
    "     - allow the user to keep or reverse normal directions\n"
    " \n"
    "   1.2  29 September 2004 [rickr]\n"
    "     - now set global ready if all is well\n"
    "     - clear norms if not in use\n"
    "     - name all local functions PV2S_*\n"
    "     - if debug > 0, display chosen surfaces in terminal\n"
    "     - if debug > 1, display all possible surfaces in terminal\n"
    " \n"
    "   1.3  08 October 2004 [rickr]\n"
    "     - Global changes for new LDP processing:\n"
    "     - added second surface pair to GUI\n"
    "     - made small help and hint changes\n"
    "     - fixed receive order of fr and mm offsets (mm was fr)\n"
    "     - verify that surface pairs have matching LDPs\n"
    "     - added PV2S_disp_afni_surfaces() to list all surfaces w/indices\n"
    " \n"
    "   1.4  25 October 2004 [rickr]\n"
    "     - make sure the surface pairs are actually different\n"
    "     - make sure surfaces have the same number of nodes\n"
    "     - process all parameters, but only complain if 'ready'\n"
    "     - always pass along debug/dnode\n"
    " \n"
    "   1.5  11 December 2004 [rickr]\n"
    "     - describe the default vol2surf operation in this Help\n"
	;

#define P_MAP_NAMES_NVALS      12	/* should match enum for global maps */
#define P_NY_NVALS              2
#define P_KEEP_NVALS            3
#define P_STEP_NVALS            2

static char * gp_ny_list[]   = { "no", "yes" };
static char * gp_keep_list[] = { "check", "keep", "reverse" };
static char * gp_step_list[] = { "voxel", "node" };

typedef struct
{
    v2s_plugin_opts  * vpo;
    char             * hist;
    char            ** maps;
} pv2s_globals;

static pv2s_globals globs;	/* these are just pointers to afni globals */

/* local functions */
static int PV2S_check_surfaces(PLUGIN_interface * plint, int sa, int sb,
			       char *mesg, int debug);
static int PV2S_disp_afni_surfaces(PLUGIN_interface * plint);
static int PV2S_init_plugin_opts(pv2s_globals * g);
static int PV2S_process_args(PLUGIN_interface * plint,pv2s_globals * g,
			     char *mesg);

/* for ease of error reporting */
#define PV2S_BAIL_VALUE(buf,str,val)                                   \
        do { sprintf((buf),  "-------------------------------------\n"  \
                             "%s\n"                                     \
                             "bad value = %d\n"                         \
                             "-------------------------------------",   \
                             (str), (val) ); } while (0)

DEFINE_PLUGIN_PROTOTYPE			/* for C++ compilation */

PLUGIN_interface * PLUGIN_init( int ncall )
{
    PLUGIN_interface * plint;
    void             * void_vpo;

ENTRY("vol2surf: PLUGIN_init");

    if ( ncall > 0 ) RETURN(NULL);	/* only one interface */

    /* might be temporary */
    if ( PLUTO_set_v2s_addrs(&void_vpo, &globs.maps, &globs.hist) )
    {
	fprintf(stderr,"** plug_v2s: failed to init globals\n");
	RETURN(NULL);
    }

    /* using a void pointer so we don't have to put vol2surf.h in afni.h */
    globs.vpo = (v2s_plugin_opts *)void_vpo;

    PV2S_init_plugin_opts(&globs);

    plint = PLUTO_new_interface("Vol2Surf",
		"configure afni's volume to surface options",
		g_help, PLUGIN_CALL_VIA_MENU, PV2S_main);

    PLUTO_add_hint     (plint, "configure vol2surf options");
    PLUTO_set_sequence (plint, "A:afnicontrol:surf");
    PLUTO_set_runlabels(plint, "Set+Keep", "Set+Close");

    /* first input : do we use vol2surf? */
    PLUTO_add_option( plint, "function", "op_st", TRUE );
    PLUTO_add_hint  ( plint, "decide whether to use vol2surf" );
    PLUTO_add_string( plint, "use vol2surf? ", P_NY_NVALS, gp_ny_list, 0 );
    PLUTO_add_hint  ( plint, "use vol2surf (or basic intersection)" );
    PLUTO_add_string( plint, "map func",P_MAP_NAMES_NVALS,globs.maps,0);
    PLUTO_add_hint  ( plint, "choose a filter to apply to segment values" );
    PLUTO_add_string( plint, "seg index",P_STEP_NVALS,gp_step_list,0);
    PLUTO_add_hint  ( plint, "along segments, count only distinct voxels?");
    PLUTO_add_number( plint, "num steps", 0, 100, 0, 2, 1 );
    PLUTO_add_hint  ( plint, "number of steps to divide each segment into" );

    /* choose surface indices for surf pair 0                   */
    /*   - note that we do not yet have surfaces to choose from */
    PLUTO_add_option( plint, "surf pair 0", "surf pair 0", TRUE );
    PLUTO_add_hint  ( plint, "choose first surface index(es)" );
    PLUTO_add_string( plint, "apply? ", P_NY_NVALS, gp_ny_list, 1 );
    PLUTO_add_hint  ( plint, "decide whether to apply surface(s)" );
    PLUTO_add_number( plint, "surf_A", 0, 50, 0, 0, 1 );
    PLUTO_add_hint  ( plint, "choose surface A index" );
    PLUTO_add_string( plint, "use B? ", P_NY_NVALS, gp_ny_list, 0 );
    PLUTO_add_hint  ( plint, "decide whether to use surface B" );
    PLUTO_add_number( plint, "surf_B", 0, 50, 0, 1, 1 );
    PLUTO_add_hint  ( plint, "choose surface B index" );

    /* choose surface indices for surf pair 1                   */
    /*   - note that we do not yet have surfaces to choose from */
    PLUTO_add_option( plint, "surf pair 1", "surf pair 1", TRUE );
    PLUTO_add_hint  ( plint, "choose second surface index(es)" );
    PLUTO_add_string( plint, "apply? ", P_NY_NVALS, gp_ny_list, 0 );
    PLUTO_add_hint  ( plint, "decide whether to apply surface(s)" );
    PLUTO_add_number( plint, "surf_A", 0, 50, 0, 2, 1 );
    PLUTO_add_hint  ( plint, "choose surface A index" );
    PLUTO_add_string( plint, "use B? ", P_NY_NVALS, gp_ny_list, 0 );
    PLUTO_add_hint  ( plint, "decide whether to use surface B" );
    PLUTO_add_number( plint, "surf_B", 0, 50, 0, 3, 1 );
    PLUTO_add_hint  ( plint, "choose surface B index" );

    /* menu for using normals */
    PLUTO_add_option( plint, "normals", "normals", FALSE );
    PLUTO_add_hint  ( plint, "control use of normals (instead of surf_B)" );
    PLUTO_add_string( plint, "use normals? ", P_NY_NVALS, gp_ny_list, 0 );
    PLUTO_add_hint  ( plint, "should normals be used to simulate surf_B?" );
    PLUTO_add_number( plint, "norm len", -100, 100, 1, 10, 1 );
    PLUTO_add_hint  ( plint, "what (signed) length should the normals be?" );
    PLUTO_add_string( plint, "norm dir", P_KEEP_NVALS, gp_keep_list, 0 );
    PLUTO_add_hint  ( plint, "check normal direction, or keep or reverse it" );

    /* segment offsets */
    PLUTO_add_option( plint, "offsets", "offsets", FALSE );
    PLUTO_add_hint  ( plint,"offset segment endpoints, directed from p1 to pn");
    PLUTO_add_number( plint, "f_p1_mm", -100, 100, 1, 0, 1 );
    PLUTO_add_hint  ( plint, "move p1 towards pn, in millimeters" );
    PLUTO_add_number( plint, "f_pn_mm", -100, 100, 1, 0, 1 );
    PLUTO_add_hint  ( plint, "move pn farther from p1, in millimeters" );
    PLUTO_add_number( plint, "f_p1_fr", -100, 100, 1, 0, 1 );
    PLUTO_add_hint  ( plint, "move p1 towards pn, by this segment fraction" );
    PLUTO_add_number( plint, "f_pn_fr", -100, 100, 1, 0, 1 );
    PLUTO_add_hint  ( plint, "move pn farther from p1, by this fraction" );

    /* out of bounds or mask */
    PLUTO_add_option( plint, "out of range", "oor", FALSE );
    PLUTO_add_hint  ( plint, "what to do when out of bounds or mask" );
    PLUTO_add_string( plint, "oob nodes?", P_NY_NVALS, gp_ny_list, 0 );
    PLUTO_add_hint  ( plint, "keep out-of-bounds nodes? (outside the dataset)");
    PLUTO_add_number( plint, "oob value", 0, 0, 0, -2, 1 );
    PLUTO_add_hint  ( plint, "value to apply when out of dataset bounds" );
    PLUTO_add_string( plint, "oom nodes?", P_NY_NVALS, gp_ny_list, 0 );
    PLUTO_add_hint  ( plint, "keep out-of-mask nodes? (below threshold)");
    PLUTO_add_number( plint, "oom value", 0, 0, 0, -1, 1 );
    PLUTO_add_hint  ( plint, "value for masked out nodes" );

    /* choose node processing range */
    PLUTO_add_option( plint, "output", "output", FALSE );
    PLUTO_add_hint  ( plint, "select node range and output files" );
    PLUTO_add_number( plint, "first node", 0, 0, 0, 0, 1 );
    PLUTO_add_hint  ( plint, "starting node to process (zero based)" );
    PLUTO_add_number( plint, "last node", 0, 0, 0, 0, 1 );
    PLUTO_add_hint  ( plint, "end node to process (zero based)" );
    PLUTO_add_string( plint, "outfile.1D", 0, NULL, 14 );
    PLUTO_add_hint  ( plint, "name for 1D output file - will overwrite!" );
    PLUTO_add_string( plint, "outfile.niml", 0, NULL, 14 );
    PLUTO_add_hint  ( plint, "name for niml output file - will overwrite!" );

    /* debugging level */
    PLUTO_add_option( plint, "debug level", "debug level", FALSE );
    PLUTO_add_hint  ( plint, "choose debug level (and debug node)" );
    PLUTO_add_number( plint, "level", 0, 5, 0, 0, 1 );
    PLUTO_add_hint  ( plint, "debug level - how much to print to terminal" );
    PLUTO_add_number( plint, "node", 0, 0, 0, -1, 1 );
    PLUTO_add_hint  ( plint, "particular node to print debug infomation for" );

    RETURN(plint);
}

char * PV2S_main ( PLUGIN_interface * plint )
{
    pv2s_globals * g;
    static char    message[2048];	/* use this to output failures */

ENTRY("PV2S_main");

    g = &globs;			/* to have only one global access */
    message[0] = '\0';		/* init to empty string */

    g->vpo->ready = 0;

    if ( (PV2S_process_args(plint, g, message) != 0) && message[0] )
	RETURN(message);

    RETURN(NULL);
}

/* base defaults to local and duplicate to global */
static int PV2S_init_plugin_opts(pv2s_globals * g)
{
ENTRY("PV2S_init_plugin_opts");
    memset(g->vpo, 0, sizeof(*g->vpo));

    g->vpo->ready =  0;		/* flag as "not ready to go" */
    g->vpo->use0  =  0;
    g->vpo->use1  =  0;
    g->vpo->s0A   = -1;
    g->vpo->s0B   = -1;
    g->vpo->s1A   = -1;
    g->vpo->s1B   = -1;

    g->vpo->sopt.gp_index      = -1;
    g->vpo->sopt.dnode         = -1;
    g->vpo->sopt.outfile_1D    = NULL;
    g->vpo->sopt.outfile_niml  = NULL;

    RETURN(0);
}



static int PV2S_process_args(PLUGIN_interface * plint, pv2s_globals * g,
			     char * mesg)
{
    THD_session     * ss;
    v2s_plugin_opts   lvpo;
    v2s_opts_t      * sopt;
    float             fval;
    char            * tag, * str;
    int               val, ready = 0;

ENTRY("PV2S_process_args");

    /* do we have a valid 3D view and session? */
    if ( !IM3D_OPEN(plint->im3d) || !plint->im3d->ss_now )
    {
	sprintf(mesg, "----------------------------------------------\n"
		      "strange failure: invalid 3D view or session???\n"
		      "----------------------------------------------");
	RETURN(-1);
    }

    /* to a quick check to be sure we are talking with suma */
    ss = plint->im3d->ss_now;
    if (ss && ss->su_num < 1)
    {
	sprintf(mesg, "-----------------------------------------\n"
		      "no surfaces: is afni 'talking' with suma?\n"
		      "-----------------------------------------");
	RETURN(1);
    }

    /* copy current values, and make local changes while checking */
    lvpo = *g->vpo;
    sopt = &lvpo.sopt;		/* just for typing */

    while ( (tag = PLUTO_get_optiontag(plint)) != NULL )
    {
	if ( sopt->debug > 2 )
	    fprintf(stderr,"+d received option tag: %s\n", tag);

	if ( ! strcmp(tag, "op_st") )
	{
	    str = PLUTO_get_string(plint);
	    val = PLUTO_string_index(str, P_NY_NVALS, gp_ny_list);

	    if ( (val < 0) || (val >= P_NY_NVALS) )
	    {
		PV2S_BAIL_VALUE(mesg,"bad NY vals", val);
		RETURN(1);
	    }
	    ready = val;		/* this is the interface to "ready" */

	    /* now get map */
	    str = PLUTO_get_string(plint);
	    val = PLUTO_string_index(str, P_MAP_NAMES_NVALS, g->maps);
	    if ( ready && val == E_SMAP_INVALID )
	    {
		sprintf( mesg,  "--------------------------------\n"
				"please choose a mapping function\n"
				"--------------------------------" );
		RETURN(1);
	    }
	    else if (ready && ((val < E_SMAP_INVALID) || (val >= E_SMAP_FINAL)))
	    {
		PV2S_BAIL_VALUE(mesg, "illegal 'map func'", val);
		RETURN(1);
	    }
	    sopt->map = val;

	    /* now get step index */
	    str = PLUTO_get_string(plint);
	    val = PLUTO_string_index(str, P_STEP_NVALS, gp_step_list);
	    sopt->f_index = val > 0 ? 1 : 0;	/* be sure */

	    val = (int)PLUTO_get_number(plint);	/* num steps */
	    if (ready && ((val <= 0) || (val >= V2S_STEPS_TOOOOO_BIG)))
	    {
		PV2S_BAIL_VALUE(mesg, "steps too big", val);
		RETURN(1);
	    }
	    sopt->f_steps = val;
	}
	else if ( ! strcmp(tag, "surf pair 0") )
	{
	    lvpo.use0 = 0;
	    str = PLUTO_get_string(plint);
	    if ( PLUTO_string_index(str, P_NY_NVALS, gp_ny_list) != 0 )
	    {
		lvpo.use0 = 1;
		lvpo.s0A = (int)PLUTO_get_number(plint);
		lvpo.s0B = -1;	/* first assume to not use surf_B */
		str = PLUTO_get_string(plint);
		if ( PLUTO_string_index(str, P_NY_NVALS, gp_ny_list) != 0 )
		    lvpo.s0B = (int)PLUTO_get_number(plint);
	    }
   	}
	else if ( ! strcmp(tag, "surf pair 1") )
	{
	    lvpo.use1 = 0;
	    str = PLUTO_get_string(plint);
	    if ( PLUTO_string_index(str, P_NY_NVALS, gp_ny_list) != 0 )
	    {
		lvpo.use1 = 1;
		lvpo.s1A = (int)PLUTO_get_number(plint);
		lvpo.s1B = -1;	/* first assume to not use surf_B */
		str = PLUTO_get_string(plint);
		if ( PLUTO_string_index(str, P_NY_NVALS, gp_ny_list) != 0 )
		    lvpo.s1B = (int)PLUTO_get_number(plint);
	    }
   	}
	else if ( ! strcmp(tag, "normals") )
	{
	    str = PLUTO_get_string(plint);
	    if ( PLUTO_string_index(str, P_NY_NVALS, gp_ny_list) != 0 )
	    {
		sopt->use_norms = 1;
		sopt->norm_len = PLUTO_get_number(plint);

		str = PLUTO_get_string(plint);
		val = PLUTO_string_index(str, P_KEEP_NVALS, gp_keep_list);
		if      ( val == 1 ) sopt->norm_dir = V2S_NORM_KEEP;
		else if ( val == 2 ) sopt->norm_dir = V2S_NORM_REVERSE;
		else                 sopt->norm_dir = V2S_NORM_DEFAULT;
	    }
	    else
		sopt->use_norms = 0;
	}
	else if ( ! strcmp(tag, "offsets") )
	{
	    int test = 0;

	    sopt->f_p1_mm = PLUTO_get_number(plint);
	    sopt->f_pn_mm = PLUTO_get_number(plint);
	    sopt->f_p1_fr = PLUTO_get_number(plint);
	    sopt->f_pn_fr = PLUTO_get_number(plint);

	    /* check for consistency */
	    if ( sopt->f_p1_fr != 0 || sopt->f_pn_fr != 0 ) test |= 1;
	    if ( sopt->f_p1_mm != 0 || sopt->f_pn_mm != 0 ) test |= 2;
	    if ( ready && test > 2 )  /* i.e. == 3 */
   	    {
		sprintf( mesg,  "---------------------------------\n"
				"use only one pair of f*_mm, f*_fr\n"
				"to change normal lengths     \n"
				"---------------------------------" );
		RETURN(1);
	    }
	}
	else if ( ! strcmp(tag, "oor") )
	{
	    /* out of bounds ... */
	    str  = PLUTO_get_string(plint);
	    val  = PLUTO_string_index(str, P_NY_NVALS, gp_ny_list);
	    fval = PLUTO_get_number(plint);
	    if ( val != 0 )
	    {
		sopt->oob.show  = 1;
		sopt->oob.value = fval;
	    }
	    else
		sopt->oob.show  = 0;

	    /* out of mask ... */
	    str  = PLUTO_get_string(plint);
	    val  = PLUTO_string_index(str, P_NY_NVALS, gp_ny_list);
	    fval = PLUTO_get_number(plint);
	    if ( val != 0 )
	    {
		sopt->oom.show  = 1;
		sopt->oom.value = fval;
	    }
	    else
		sopt->oom.show  = 0;
	}
	else if ( ! strcmp(tag, "output") )
	{
	    sopt->first_node = (int)PLUTO_get_number(plint);
	    sopt->last_node  = (int)PLUTO_get_number(plint);
	    if ( ready &&  sopt->first_node > sopt->last_node )
	    {
		sprintf( mesg,  "-----------------------------\n"
				"illegal node range values:   \n"
				"first (%d) > last (%d)       \n"
				"-----------------------------",
				sopt->first_node, sopt->last_node );
		RETURN(1);
	    }

	    /* get output filenames */
	    if ( sopt->outfile_1D )   free(sopt->outfile_1D);
	    if ( sopt->outfile_niml ) free(sopt->outfile_niml);
	    sopt->outfile_1D = sopt->outfile_niml = NULL;

	    str = PLUTO_get_string(plint);
	    if ( strlen(str) > 0 )
	    {
		sopt->outfile_1D = (char *)calloc(strlen(str)+1,sizeof(char));
		strcpy(sopt->outfile_1D, str);
	    }

	    str = PLUTO_get_string(plint);
	    if ( strlen(str) > 0 )
	    {
		sopt->outfile_niml = (char *)calloc(strlen(str)+1,sizeof(char));
		strcpy(sopt->outfile_niml, str);
	    }
	}
	else if ( ! strcmp(tag, "debug level") )
	{
	    sopt->debug = (int)PLUTO_get_number(plint);
	    sopt->dnode = (int)PLUTO_get_number(plint);
	}
	else
	{
	    sprintf( mesg,  "---------------------------\n"
			    "Unknown option tag : %s\n"
			    "---------------------------", tag );
	    RETURN(1);
	}
    }

    if ( sopt->debug > 1 )
    {
	disp_v2s_plugin_opts( "plug_vol2surf options done : ", &lvpo );
	disp_v2s_opts_t( "  surface options : ", sopt );
    }

    /* should we just run away?  always adjust debugging first... */
    g->vpo->sopt.debug = lvpo.sopt.debug;
    g->vpo->sopt.dnode = lvpo.sopt.dnode;

    if ( lvpo.use0 == 0 && lvpo.use1 == 0 ) ready = 0;

    if ( ! ready )
    {
	if ( sopt->debug > 0 )
	    PV2S_disp_afni_surfaces(plint);
        RETURN(1);
    }

    if ( ! v2s_is_good_map(sopt->map, 1) )
    {
	sprintf( mesg,  "-------------------------------------------\n"
			"mapping function is invalid in this context\n"
			"index %d, name '%s'\n"
			"-------------------------------------------",
		sopt->map,
		(sopt->map < E_SMAP_INVALID || sopt->map >= E_SMAP_FINAL) ?
		"out-of-range" : g->maps[sopt->map] );
	RETURN(1);
    }

    /* verify surface consistency */
    if ( lvpo.use0 )
    {
	if ( PV2S_check_surfaces(plint, lvpo.s0A, lvpo.s0B, mesg, sopt->debug) )
	    RETURN(1);
	if ( lvpo.s0A == lvpo.s0B ) 
	{
	    sprintf( mesg,  "--------------------------------\n"
			    "error: for pair 0, surfA = surfB\n"
			    "--------------------------------" );
	    RETURN(1);
	}
    }
    if ( lvpo.use1 )
    {
	if ( PV2S_check_surfaces(plint, lvpo.s1A, lvpo.s1B, mesg, sopt->debug) )
	    RETURN(1);
	if ( lvpo.s1A == lvpo.s1B ) 
	{
	    sprintf( mesg,  "--------------------------------\n"
			    "error: for pair 1, surfA = surfB\n"
			    "--------------------------------" );
	    RETURN(1);
	}
    }

    /* if the user wan't normals, they can only supply one surface per pair */
    if ( sopt->use_norms && ((lvpo.use0 && lvpo.s0B >= 0) ||
			     (lvpo.use1 && lvpo.s1B >= 0)) )
    {
	sprintf( mesg,  "----------------------------------------\n"
			"cannot use normals while using surface B\n"
			"----------------------------------------" );
	RETURN(1);
    }

    if ( sopt->debug > 0 )
	PV2S_disp_afni_surfaces(plint);

    /* for now, only output nodes and a single data column */
    sopt->skip_cols = V2S_SKIP_ALL ^ V2S_SKIP_NODES;

    if ( ready )		/* then copy changes over old values */
    {
	*g->vpo = lvpo;
	g->vpo->ready = 1;
    }

    RETURN(0);
}

static int PV2S_check_surfaces(PLUGIN_interface * plint, int sa, int sb,
			       char * mesg, int debug)
{
    THD_session * ss;

ENTRY("PV2S_check_surfaces");

    ss = plint->im3d->ss_now;

    if ( ss->su_num < 1 )
    {
	PV2S_BAIL_VALUE(mesg, "Not enough surfaces in session.\n", ss->su_num);
	RETURN(1);
    }

    /* verify that the surface indices are valid */
    if ( sa < 0 )
    {
	PV2S_BAIL_VALUE(mesg, "surf_A has invalid index", sa);
	RETURN(1);
    }

    if ( sa >= ss->su_num )
    {
	PV2S_BAIL_VALUE(mesg, "surf_A beyond valid index", ss->su_num - 1);
	RETURN(1);
    }

    if ( sb >= ss->su_num )
    {
	PV2S_BAIL_VALUE(mesg, "surf_B beyond valid index", ss->su_num - 1);
	RETURN(1);
    }

    if ( sb >= 0 )
    {
	/* then check that surf_A and surf_B share LDP */
	if (strncmp(ss->su_surf[sa]->idcode_ldp,ss->su_surf[sb]->idcode_ldp,32))
	{
	    char * la = ss->su_surf[sa]->label_ldp;
	    char * lb = ss->su_surf[sb]->label_ldp;
	    if ( ! *la ) la = "undefined";
	    if ( ! *lb ) lb = "undefined";
	    sprintf(mesg, "---------------------------------------\n"
			  "Error: Surf_A and Surf_B have different\n"
			  "       local domain parents\n"
			  "LDP #%d = '%s', LDP #%d = '%s'\n"
	                  "---------------------------------------",
			  sa, la, sb, lb);
	    RETURN(1);
	}

	/* and that they have the same number of nodes */
	if ( ss->su_surf[sa]->num_ixyz != ss->su_surf[sb]->num_ixyz )
	{
	    sprintf(mesg, "------------------------------------------------\n"
			  "Big problem: Surf_A and Surf_B have different\n"
			  "    numbers of nodes!  They cannot share an LDP.\n"
			  "    SurfA: '%s', %d nodes\n"
			  "    SurfB: '%s', %d nodes\n"
	                  "------------------------------------------------",
			  ss->su_surf[sa]->label, ss->su_surf[sa]->num_ixyz,
			  ss->su_surf[sb]->label, ss->su_surf[sb]->num_ixyz);
	    RETURN(1);
	}
    }

    if ( debug > 0 && ss->su_surf )
    {
	if ( ss->su_surf[sa] )      /* we have checked sa >= 0, above */
	    fprintf(stderr,"+d surf_A label: '%s'\n",
		*ss->su_surf[sa]->label ? ss->su_surf[sa]->label : "not set");
	else
	    fprintf(stderr,"** surf_A (#%d) pointer not set??\n", sa);

	if ( sb < 0 )
	    fprintf(stderr,"-d surf_B not in use\n");
	else if ( ss->su_surf[sb] )
	    fprintf(stderr,"+d surf_B label: '%s'\n",
		*ss->su_surf[sb]->label ? ss->su_surf[sb]->label : "not set");
	else
	    fprintf(stderr,"** surf_B (#%d) pointer not set??\n", sb);
    }

    RETURN(0);
}

static int PV2S_disp_afni_surfaces(PLUGIN_interface * plint)
{
    THD_session * ss;
    char        * ldp, * label;
    int           c;

ENTRY("disp_afni_surfaces");

    ss = plint->im3d->ss_now;

    if ( ss->su_surf <= 0 )
	RETURN(0);

    fprintf(stderr,"-d --------------------------------------\n");
    fprintf(stderr,"   afni surface indices, labels and LDPs:\n");
    for ( c = 0; c < ss->su_num; c++ )
    {
	label = ss->su_surf[c]->label;
	ldp   = ss->su_surf[c]->label_ldp;
	if ( ! *label ) label = "undefined";
	if ( ! *ldp   ) ldp   = "undefined";

	fprintf(stderr,"   index %2d, label '%s', LDP '%s'\n",
		    c, label, ldp );
    }

    RETURN(0);
}
