#!/usr/bin/env python

# python3 status: compatible

# system libraries
import sys, os

try:    sys.path.extend(['.', '%s/abin' % os.getenv('HOME')])
except: pass

# AFNI libraries
from afnipy import option_list as OPT
from afnipy import afni_util as UTIL
from afnipy import lib_subjects as SUBJ
from afnipy import lib_vars_object as VO
from afnipy import lib_surf_clustsim as CLUST

# ----------------------------------------------------------------------
# globals

g_command_help = """
=============================================================================
slow_surf_clustsim.py    - generate a tcsh script to run clustsim on surface

------------------------------------------

   examples: ~1~

   1. basic: give 3 required inputs, all else is default ~2~

      While a blur of 4.0 is the default, it is included for clarity.

        slow_surf_clustsim.py -save_script surf.clustsim        \\
            -uvar spec_file sb23_lh_141_std.spec                \\
            -uvar surf_vol sb23_SurfVol_aligned+orig            \\
            -uvar blur 4.0                                      \\
            -uvar vol_mask mask_3mm+orig                        \\


   2. more advanced, but still based on EPI analysis ~2~

      Specify p-values, blur size and number of iterations, along with the
      script name and results directory, use 10000 iterations, instead of
      the default 1000.

        slow_surf_clustsim.py -save_script surf.clustsim        \\
            -uvar spec_file sb23_lh_141_std.spec                \\
            -uvar surf_vol sb23_SurfVol_aligned+orig            \\
            -uvar vol_mask mask_3mm+orig                        \\
            -uvar pthr_list 0.05 0.01 0.002 0.001 0.0002 0.0001 \\
            -uvar blur 8.0                                      \\
            -uvar niter 10000                                   \\
            -save_script csim.10000                             \\
            -uvar results_dir clust.results.10000


   3. basic, but on the surface (so no vol_mask is provided) ~2~

        slow_surf_clustsim.py -save_script surf.sim.3           \\
            -on_surface yes                                     \\
            -uvar blur 3.0                                      \\
            -uvar spec_file sb23_lh_141_std.spec                \\
            -uvar surf_vol sb23_SurfVol_aligned+orig

      One can also add a surface mask via '-uvar surf_mask smask_lh.gii'.


   Note: it is appropriate to use a volume mask on the same grid as the data to
         be analyzed, which is to say either the EPI grid (for functional
         analysis) or perhaps the anatomical grid (for anatomical analysis,
         such as of thickness measures).

   Note: the niter values should match between this program and
         quick.alpha.vals.py.

------------------------------------------

   applying the results: ~1~

      The result of processing should be one z.max.* file for each uncorrected
      p-value input to the program (or each default).  These files contain the
      maximum cluster sizes (in mm^2), per z-score/p-value, and are named using
      the corresponding p-value, e.g. z.max.area.0.001 corresponds to p=0.001.

      To get the cluster size required for some uncorrected p-value, run 
      quick.alpha.vals.py on the z.max.area file corresponding to the desired
      p-value, and note the cluster area required for the chosen corrected p.

      For example, running this:

           quick.alpha.vals.py -niter 1000 z.max.area.0.001

      might show that a minimum cluster size of 113 mm^2 would correspond to a
      corrected p=0.05.

      Use of -niter should match that from slow_surf_clustsim.py.

------------------------------------------

   script outline: ~1~

        set control variables
        create and enter results directory
        convert p-value list (pthr_list) to z-scores (zthr_list)
        create dummy time series of length itersize
        for each iter ( iteration list )
            3dcalc: generate noise volume
            3dVol2Surf: map noise to surface
            SurfSmooth: blur to FWHM
            for each index ( itersize list )
                for each zthr ( zthr_list )
                    SurfClust: make clust file clust.out.$iter.$index.$zthr
        extract lists of maximum areas

------------------------------------------

   terminal options: ~1~

      -help                     : show this help
      -hist                     : show module history
      -show_default_cvars       : list default control variables
      -show_default_uvars       : list default user variables
      -show_valid_opts          : list valid options
      -ver                      : show current version

   other options: ~1~
      -on_surface yes/no        : if yes, start from noise on the surface
                                  (so no volume data is involved)
      -print_script             : print script to terminal
      -save_script FILE         : save script to given file
      -uvar value ...           : set the user variable
                                  (use -show_default_uvars to see user vars)
      -verb LEVEL               : set the verbosity level

-----------------------------------------------------------------------------
R Reynolds    June 2011
=============================================================================
"""


class MyInterface:
   """interface class for MyLibrary (whatever that is)
     
      This uses lib_1D.py as an example."""
   def __init__(self, verb=1):
      # main variables
      self.status          = 0                       # exit value
      self.valid_opts      = None
      self.user_opts       = None

      # general variables
      self.verb            = verb

      # initialize valid_opts
      self.valid_opts = self.get_valid_opts()

   def get_valid_opts(self):
      vopts = OPT.OptionList('valid opts')

      # short, terminal arguments
      vopts.add_opt('-help', 0, [], helpstr='display program help')
      vopts.add_opt('-hist', 0, [], helpstr='display the modification history')
      vopts.add_opt('-show_default_cvars',0,[],helpstr='show default cvars')
      vopts.add_opt('-show_default_uvars',0,[],helpstr='show default uvars')
      vopts.add_opt('-show_valid_opts',0,[],helpstr='display all valid options')
      vopts.add_opt('-ver', 0, [],helpstr='display the current version number')

      vopts.add_opt('-cvar', -2, [], helpstr='set control variable')
      vopts.add_opt('-uvar', -2, [], helpstr='set user variable to value')

      # general options
      vopts.add_opt('-on_surface', 1, [],
                    acplist=['yes', 'no'],
                    helpstr='work directly on the surface (yes/no)')
      vopts.add_opt('-print_script', 0, [],
                    helpstr='print script to terminal window')
      vopts.add_opt('-save_script', 1, [],
                    helpstr='write script to given file')
      vopts.add_opt('-verb', 1, [],helpstr='set the verbose level (default=1)')

      return vopts

   def process_options(self):

      argv = sys.argv

      if len(argv) == 0:        # non-gui out
         print(g_command_help)
         return 1

      # process any optlist_ options
      self.valid_opts.check_special_opts(argv)

      # process terminal options without the option_list interface
      # (so that errors are not reported)

      # if no arguments are given, apply -help
      if len(argv) <= 1 or '-help' in argv:
         print(g_command_help)
         return 1

      if '-hist' in argv:
         print(CLUST.g_history)
         return 1

      if '-show_default_cvars' in argv:
         CLUST.g_ctrl_defs.show('')
         return 1

      if '-show_default_uvars' in argv:
         CLUST.g_user_defs.show('')
         return 1

      if '-show_valid_opts' in argv:
         self.valid_opts.show('', 1)
         return 1

      if '-ver' in argv:
         print(CLUST.g_version)
         return 1

      # ============================================================
      # read options specified by the user
      self.user_opts = OPT.read_options(argv, self.valid_opts)
      uopts = self.user_opts            # convenience variable
      if not uopts: return -1           # error condition

      # ------------------------------------------------------------
      # init subject options struct

      self.cvars = VO.VarsObject('control vars from command line')
      self.uvars = VO.VarsObject('user vars from command line')

      val, err = uopts.get_type_opt(int, '-verb')
      if val != None and not err: self.verb = val

      SUBJ.set_var_str_from_def('cvars', 'verb', ['%d'%self.verb], self.cvars,
                                 defs=CLUST.g_ctrl_defs)

      # first process all setup options
      errs = 0
      for opt in uopts.olist:
         # skip -verb (any terminal option should block getting here)
         if opt.name == '-verb':                continue

         # and skip and post-setup options (print command, save, etc.)
         elif opt.name == '-print_script':      continue
         elif opt.name == '-save_script':       continue

         # now go after "normal" options

         elif opt.name == '-on_surface':
            val, err = uopts.get_string_list('', opt=opt)
            if val == None or err: return -1
            if self.cvars.set_var_with_defs(opt.name[1:],val,CLUST.g_ctrl_defs,
                        as_type=1, oname='cvars', verb=self.verb) < 0:
               errs += 1
               continue

         # cvar requires at least 2 parameters, name and value
         elif opt.name == '-cvar':
            val, err = uopts.get_string_list('', opt=opt)
            if val == None or err: return -1
            # go after verb, in particular
            if val[0] == 'verb':
               try: self.verb = int(val[1])
               except:
                  print("** failed to set 'verb' level")
                  errs += 1
                  continue
            # and set it from the form name = [value_list]
            if SUBJ.set_var_str_from_def('cvars', val[0], val[1:], self.cvars,
                        CLUST.g_ctrl_defs, verb=self.verb) < 0:
               errs += 1
               continue

         # uvar requires at least 2 parameters, name and value
         elif opt.name == '-uvar':
            val, err = uopts.get_string_list('', opt=opt)
            if val == None or err: return -1
            # and set it from the form name = [value_list]
            if SUBJ.set_var_str_from_def('uvars', val[0], val[1:], self.uvars,
                        CLUST.g_user_defs, verb=self.verb) < 0:
               errs += 1
               continue

         else:
            print('** unknown option %s' % opt.name)
            errs += 1

      if self.verb > 2:
         print('-' * 75)
         self.uvars.show('post-init uvars', name=0)
         self.cvars.show('post-init cvars', name=0)
         print('-' * 75)

      if errs:    return -1
      else:       return  0     # no error, and continue on return

   def execute(self):

      if not self.ready_for_action(): return 1

      if self.verb > 1: print('-- processing...')

      uopts = self.user_opts

      if uopts.find_opt('-print_script'): self.print_script()

      opt = uopts.find_opt('-save_script')
      if opt != None:
         val, err = uopts.get_string_opt('', opt=opt)
         if val != None and not err: self.save_script(val)

      return 0

   def ready_for_action(self):
      """perform any final tests before execution"""

      ready = 1

      return ready

   def print_script(self):
      """create script and print to terminal"""

      ctest, cmd = self.get_script()
      print(cmd)

   def save_script(self, fname):

      ctest, cmd = self.get_script()
      if cmd == '': return

      if ctest.write_script(fname):
         print('** failed to write slow_surf_clustsim script to disk')

   def get_script(self):
      """return the SurfClust object and script
         (print warnings and errors to screen)"""

      ctest = CLUST.SurfClust(self.cvars, self.uvars, argv=sys.argv)

      nwarn, wstr = ctest.get_warnings()
      status, mesg = ctest.get_script()

      if status:        # only show errors
         print('%s\nERRORS:\n\n%s\n' % (75*'*', mesg))
         cmd = ''
      else:
         if wstr: print('%s\n**** Warnings:\n\n%s\n%s\n' % (75*'-',wstr,75*'-'))
         cmd = '### surf clust script:\n\n%s\n' % mesg

      return ctest, cmd

   def test(self, verb=3):
      print('------------------------ initial tests -----------------------')
      self.verb = verb

      print('------------------------ reset files -----------------------')

      print('------------------------ should fail -----------------------')

      print('------------------------ more tests ------------------------')

      return None

def main():
   me = MyInterface()
   if not me: return 1
   if me.status: return me.status

   rv = me.process_options()
   if rv > 0:  return 0 # terminal success
   if rv < 0:  return 1 # terminal failure
   # else rv == 0, so continue

   rv = me.execute()
   if rv > 0: return 1

   return me.status

if __name__ == '__main__':
   sys.exit(main())


