#!/usr/bin/env python

# python3 status: started

# afni_python_wrapper.py : interface for calling python libs from the shell
#                          (based on afni_util.py)

# history (never bothered before...)
#
#    1.11  Feb 10, 2022 : try to import from afnipy first
#                         (biowulf still has old afnipy/*.py files in abin)
#

import sys, os

# ======================================================================
# get the main import done here

# ------------------------------------------------------------
# only proceed with imports in the context of main,
# in case this happens to imported
if __name__ == '__main__':
   argv = sys.argv
   narg = len(argv)

   # decide which module to import (default is afni_util)
   argbase = 1
   module = 'afni_util'

   # ------------------------------------------------------------
   # if enough options are passed, choose which module to import
   if narg > 2:
      if '-module' == argv[argbase]:
         module = argv[argbase+1]
         argbase += 2

   # terminal option: module is set, see if user wants a listing
   # if -module_dir, import differently
   # (give priority to any existing afnipy directory  [10 Feb 2022 rickr])

   if '-module_dir' in argv:
      try:
         exec('import afnipy.%s as MM' % module)
      except:
         try:
            exec('import %s as MM' % module)
         except:
            print("** failed to import module '%s'" % module)
            sys.exit(1)

      print('dir(%s):\n   %s' % (module, '\n   '.join(dir(MM))))
      sys.exit(0)

   # ------------------------------------------------------------
   # if there are options to process, actually try to import a module

   if narg > 2:
      # import the module in question and proceed
      # (now try to import from afnipy first [10 Feb 2022 rickr])
      try:
         exec('from afnipy.%s import *' % module)
      except:
         try:
            exec('from %s import *' % module)
         except:
            print("** failed to import module '%s'" % module)
            sys.exit(1)

   # we have a module imported, or plan help.n.exit, proceed...


# ----------------------------------------------------------------------

_g_main_help = """
afni_python_wrapper.py: use to call afnipy functions from the shell

   By default, this loads module afni_util, and attempts use functions from it.
   To use a function from a different module, apply -module.

   MODULE will subsequently to the imported module.


   options:

      -help             : show this help

      -module MODULE    : specify the python module to import

         By default, functions to process are imported from afni_util.  To
         import a different module, apply this option.

         Example:

            afni_python_wrapper.py -module afni_base ...

      -module_dir       : show the elements returned by dir()

         This option is useful to get a list of all module functions.

         Examples:

            afni_python_wrapper.py -module_dir
            afni_python_wrapper.py -module afni_base -module_dir

      -eval STRING      : evaluate STRING in context of MODULE
                          (i.e. STRING can be function calls or other)

         This option is used to simply execute the code in STRING.

         Examples for eval:

            afni_python_wrapper.py -eval "show_process_stack()"
            afni_python_wrapper.py -eval "show_process_stack(verb=2)"
            afni_python_wrapper.py -eval "show_process_stack(pid=1000)"

            # display out.ss_review.FT.txt as a json dictionary
            afni_python_wrapper.py -eval                  \\
                'write_data_as_json(read_text_dictionary( \\
                "out.ss_review.FT.txt")[1])'
            afni_python_wrapper.py -eval                  \\
                'write_data_as_json(read_text_dictionary( \\
                "out.ss_review.FT.txt", compact=1)[1])'

      -exec STRING      : execute STRING in context of MODULE

         This option is used to simply execute the code in STRING.

         Examples for exec:

            afni_python_wrapper.py -exec "y = 3+4 ; print y"
            afni_python_wrapper.py -exec "import PyQt4"
            afni_python_wrapper.py -exec "show_process_stack()"

      -funchelp FUNC    : print the help for module function FUNC

         Print the FUNC.__doc__ text, if any.

         Example:

            afni_python_wrapper.py -funchelp wrap_file_text

      -print STRING     : print the result of executing STRING

         Akin to -eval, but print the results of evaluating STRING.

         Examples for print:

            afni_python_wrapper.py                      \\
                -print "get_last_history_ver_pack('DSET+tlrc')"
            afni_python_wrapper.py                      \\
                -print "get_last_history_version('DSET+tlrc')"
            afni_python_wrapper.py -print 'gaussian_at_fwhm(3,5)'
            afni_python_wrapper.py -print 'gaussian_at_hwhm_frac.__doc__'

      -lprint STRING    : line print: print result list, one element per line

         The 'l' stands for 'line' (or 'list').  This is akin to -print,
         but prints a list with one element per line.

         Examples for lprint:

            # show alt+z slice timing for 20 slices and TR=2s
            afni_python_wrapper.py \\
                -lprint "slice_pattern_to_timing('alt+z', 20, 2)"

      -listfunc [SUB_OPTS] FUNC LIST ... : execute FUNC(LIST)

         With this option, LIST is a list of values to be passed to FUNC().
         Note that LIST can be simply '-' or 'stdin', in which case the
         list values are read from stdin.

         This is similar to eval, but instead of requiring:
            -eval "FUNC([v1,v2,v3,...])"
         the list values can be left as trailing arguments:
            -listfunc FUNC v1 v2 v3 ...
         (where LIST = v1 v2 v3 ...).

         SUB_OPTS sub-options:

                -float  : convert the list to floats before passing to FUNC()
                -print  : print the result
                -join   : print the results join()'d together
                -joinc  : print the results join()'d together with commas
                -joinn  : print the results join()'d together with newlines

         Examples for listfunc:

           afni_python_wrapper.py -listfunc        min_mean_max_stdev 1 2 3 4 5
           afni_python_wrapper.py -listfunc -print min_mean_max_stdev 1 2 3 4 5
           afni_python_wrapper.py -listfunc -join  min_mean_max_stdev 1 2 3 4 5

           afni_python_wrapper.py -listfunc -join -float demean 1 2 3 4 5

           afni_python_wrapper.py -listfunc -join shuffle       \\
                                  `count -digits 4 1 124`

           count -digits 4 1 124 | afni_python_wrapper.py -listfunc \\
                                  -join shuffle -

           afni_python_wrapper.py -listfunc glob2stdout 'EPI_run1/8*'

           afni_python_wrapper.py -listfunc -joinc list_minus_glob_form *HEAD

           afni_python_wrapper.py -listfunc -join -float linear_fit \\
                                  2 3 5 4 8 5 8 9

         Also, if LIST contains -list2, then 2 lists can be input to do
         something like:
            -eval "FUNC([v1,v2,v3], [v4,v5,v6])"

         Examples with -list2:

            afni_python_wrapper.py -listfunc -print -float ttest 1 2 3 4 5 \\
                                                 -list2 2 2 4 6 8

            afni_python_wrapper.py -listfunc -print -float ttest_paired   \\
                                1 2 3 4 5 -list2 2 4 5 6 8

            afni_python_wrapper.py -listfunc -join -float linear_fit      \\
                                `cat y.1D` -list2 `cat x.1D`

           afni_python_wrapper.py -listfunc -join list_intersect \\
                `cat fileA` -list2 `cat fileB`                   \\
                | tr ' ' '\\n'

           # same, but use -joinn instead of tr, for newline separation
           afni_python_wrapper.py -listfunc -joinn list_intersect \\
                `cat fileA` -list2 `cat fileB`

           afni_python_wrapper.py -listfunc -joinn list_diff    \\
                `cat fileA` -list2 `cat fileB`

   Author: R Reynolds  Feb, 2020  (moved from afni_util.py)
"""

def process_listfunc(argv, argbase=1):
   """see the -help description"""

   if argv[argbase] != '-listfunc': return 1

   if len(argv) <= (2+argbase) :
      print('** -listfunc usage requires at least 3 args')
      return 1

   do_join = 0
   do_joinc = 0 # join with commas
   do_joinn = 0 # join with newlines
   do_float = 0
   do_print = 0
   argbase += 1

   while argv[argbase] in ['-join', '-joinc', '-joinn', '-print', '-float']:
      if argv[argbase] == '-join':
         do_join = 1
         argbase += 1
      elif argv[argbase] == '-joinc':
         do_joinc = 1
         argbase += 1
      elif argv[argbase] == '-joinn':
         do_joinn = 1
         argbase += 1
      elif argv[argbase] == '-print':
         do_print = 1
         argbase += 1
      elif argv[argbase] == '-float':
         do_float = 1
         argbase += 1
      else: break # should not happen

   # note function
   func = eval(argv[argbase])

   # get args, and check for -list2
   # (allow for - to read all data into array)
   args1 = argv[argbase+1:]
   if len(args1) == 1:
      if args1[0] == '-' or args1[0] == 'stdin':
         fvals = read_text_file()
         args1 = []
         for fv in fvals: args1.extend(fv.split())

   args2 = []
   if '-list2' in args1:
      l2ind = args1.index('-list2')
      args2 = args1[l2ind+1:]
      args1 = args1[0:l2ind]

   if do_float:
      try: vals1 = [float(v) for v in args1]
      except:
         print('** list1 is not all float')
         return 1
      try: vals2 = [float(v) for v in args2]
      except:
         print('** list2 is not all float')
         return 1
   else:
      vals1 = args1
      vals2 = args2

   if len(vals2) > 0: ret = func(vals1, vals2)
   else:              ret = func(vals1)
   
   if   do_join:  print(' '.join(str(v) for v in ret))
   elif do_joinc: print(','.join(str(v) for v in ret))
   elif do_joinn: print('\n'.join(str(v) for v in ret))
   elif do_print: print(ret)
   # else do nothing special
   return 0

def show_function_help(flist):
   from afnipy import afni_util as UTIL
   for func in flist:
      print(UTIL.section_divider('help for: %s' % func))
      try:
         fn = eval(func)
         print(fn.__doc__)
      except:
         print("** not a valid function '%s'" % func)

def process_args():
   argv = sys.argv
   if '-help' in argv or len(argv) < 2:
      print(_g_main_help)
      return 0

   global argbase

   # ------------------------------------------------------------
   # main module has already been imported in global scope

   # ------------------------------------------------------------
   if argv[argbase] == '-eval':
      eval(' '.join(argv[argbase+1:]))
      return 0
   elif argv[argbase] == '-exec':
      exec(' '.join(argv[argbase+1:]))
      return 0
   elif argv[argbase] == '-funchelp':
      from afnipy import afni_util as UTIL
      for func in argv[argbase+1:]:
         print(UTIL.section_divider('help for: %s' % func))
         try:
            fn = eval(func)
            print(fn.__doc__)
         except:
            print("** not a valid function '%s'" % func)
      # show_function_help(argv[argbase+1:])
      return 0
   elif argv[argbase] == '-lprint':
      ret = eval(' '.join(argv[argbase+1:]))
      print('\n'.join(['%s'%rent for rent in ret]))
      return 0
   elif argv[argbase] == '-print':
      print(eval(' '.join(argv[argbase+1:])))
      return 0
   elif argv[argbase] == '-listfunc':
      return process_listfunc(argv, argbase)
   else:
      print('** please see "afni_python_wrapper.py -help" for usage')
      return 1


if __name__ == '__main__':
   argv = sys.argv
   narg = len(argv)
   if '-help' in argv or narg < 2:
      print(_g_main_help)
      sys.exit(0)

   # ------------------------------------------------------------
   # from here on, we need at least 2 options
   if narg <= 2:
      print(_g_main_help)
      sys.exit(0)

   # we have a module imported, proceed...
   sys.exit(process_args())
