#!python

#############################################################################
##
## $Author: Sergi Rubio Manrique, srubio@cells.es $
##
## $Revision: 2008 $
##
## copyleft :    ALBA Synchrotron Controls Section, CELLS
##               Bellaterra
##               Spain
##
#############################################################################
##
## This file is part of Tango Control System
##
## Tango Control System is free software; you can redistribute it and/or
## modify it under the terms of the GNU General Public License as published
## by the Free Software Foundation; either version 3 of the License, or
## (at your option) any later version.
##
## Tango Control System is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, see <http://www.gnu.org/licenses/>.
###########################################################################

import inspect
import fandango
import fandango.log as log

from fandango.functional import *
try:
    from fandango.tango import *
    from fandango.servers import *
except:
    print('Unable to import Tango modules')

from fandango.linos import *
from fandango.objects import *
from fandango.objects import getCode


__doc__ = """

Usage:

  fandango [qt] [-v] [-l/--list] [-h/--help] [-x/--extra] [-n/--new-line] \
     [fandango_method_0] [fandango_method_1/argument_0] [key:=argument_key] 
  
  Execute python and fandango methods from shell
  Use "-h" to print version and commands available
  Exceptions are not shown unless "-v" argument is added

  Objects and Generators will not be allowed as arguments, \
     only python primitives, lists and tuples
  
  The --extra option will load PyTangoArchiving and Panic methods
  
Examples:

  fandango --help [method] # prints this help or method documentation
  
  fandango --help regexp # prints available methods matching regexp
 
  fandango --quiet # do not print results
  
  fandango -v / --verbose # print exceptions and additional info
  
  fandango -l / --list # return results as a single column list  

  fandango findModule fandango

  python $(fandango findModule fandango)/interface/CopyCatDS.py 1
  
  fandango get_tango_host
  
  fandango qt #Launch interactive Qt console
  
  fandango --help get_matching_devices | less
  
  fandango get_matching_devices "tango/admin/*"
  
  fandango get_matching_attributes "lab/ct/plctest16/*_af"
  
  fandango --help| grep propert
  
  fandango get_matching_device_properties "lab/ct/plctest16" "*modbus*"
  
  fandango read_attribute "tango/admin/pctest/status"
  
  for attr in $(fandango get_matching_attributes "tango/admin/pctest/*"); \
    do echo "$attr : $(fandango read_attribute ${attr})" ; done
  
"""

"""
A launcher like this can't be created as a function, because generic imports 
with wildcards are allowed only at module level.

If it is problem is solved in the future, then will be moved to fandango.linos
"""

if __name__ == '__main__':
  import sys
  cmd, r = '', None
  try:
    #comms = sys.argv[sys.argv.index('-c')+1:]
    comms,opts = fandango.sysargs_to_dict(split=True,cast=False,lazy=False)
    verbose = 'v' in opts or 'verbose' in opts

    if verbose:
        print('fandango {} \npython {}\n'.format(
            fandango.RELEASE,sys.version.split()[0].strip()))

    if len(comms) and comms[0] == 'help':
        #enables help option, shifts commands
        opts[comms[0]],comms = 1, comms[1:]

    if opts.get('x',opts.get('extra',0)):
        try: 
            import PyTangoArchiving
            from PyTangoArchiving import Reader
            from PyTangoArchiving.files import LoadArchivingConfiguration
            locals()['pta'] = locals()['PyTangoArchiving'] = PyTangoArchiving
        except: pass
        try: 
            import panic
        except: pass
    
    if ('h' in opts or 'help' in opts):
        target = comms[0] if len(comms) else (opts.get('help') or '')
        
        # MANY OF THESE EVALS FAIL WHEN DONE OUTSIDE __main__
        #if target and isString(target):
        #cmd = 'inspect.getsource(%s)'%target
            
        #else:
        #cmd = ("sorted([m for m in "
            #"[('%s.%s' % (getModuleName(o),k)).strip('.') "
                #" for k,o in locals().items() if not k.startswith('_')"
                #" and hasattr(o,'__call__')]"
            #"if not m.startswith('_')])")
        
        #cmd = ("['%s.%s'.format(getModuleName(o),k) "
                #"for k,o in locals().items()]")
        cmd = ("kmap(getModuleName,(o for k,o in locals().items() "
                "if hasattr(o,'__call__') and not k.startswith('_')"
                " and not k.startswith('<')),sort=False)")
        r = evalX(cmd,_locals=locals())
        r = sorted(set('{}.{}'.format(m,o.__name__).strip('.') for 
                o,m in r if not m.startswith('_')))

        if target not in r and '*' not in target:
            target = '*.'+target+'$'
            
        match = [k for k in r if fn.clmatch(target,k)]
        if len(match) == 1:
            from fandango.objects import getCode
            code = evalX('getCode('+match[0]+')',_locals=locals())
            r = '%s\n\n%s' % (code.header,code.source)
        else:
            #for c in ('"""',"'''"):
                #if c in r:
                    #r = c.join(r.split(c)[:2])+c        
            r = '\nMatching methods:\n\n' + list2str(match,'\n')
            
    ###########################################################################  
    elif len(comms):
        if 'qt' in comms[:1] or opts.get('qt'):
            # LAUNCHING QT SHELL
            args = comms['qt' in comms:]
            print('Launching the interactive Qt console ... %s' % args)
            import fandango.qt
            fandango.qt.QEvaluator.main(args)
        
        elif '(' in comms[0]:
            ## SINGLE STRING COMMAND
            cmd = ' '.join(comms) #for cmd in comms:
            if verbose: print('single-lined: {}'.format(cmd))
            r = evalX(cmd,_locals=locals())
            #if verbose: print(r)
          
    ###########################################################################
        else:
            ## MULTIPLE ARGS COMMAND
            str2evalstr = lambda c: (
                str(c) if isNumber(c) or isBool(c) 
                else ("'%s'" if "'" not in c else '"%s"')%c)
            
            kwargs = [c for c in comms[1:] if c.count(':=')==1]
            args = [c for c in comms[1:] if c not in kwargs]
            
            kwargs = ','.join("'%s':%s"%(k,str2evalstr(v)) for k,v in 
                            (c.split(':=') for c in kwargs))
            args = ','.join(str2evalstr(c) for c in args)
            cmd = '%s(*[%s],**{%s})'%(comms[0],args,kwargs)

            if verbose: print('multi-arg: {}'.format(cmd))
            r = evalX(cmd,_locals=locals())
        
    else:
        print(__doc__)
        
    if r is not None:
        linesep = '\n' if opts.get('l') else ';'
        s = obj2str(r,sep=' ',linesep=linesep)
        if verbose: 
            print('result raw: ',r)
            print('result str: ',s)
        if opts.get('l') and '\n' not in s:
            s = '\n'.join(s.split())
        if not opts.get('quiet'):
            print(s)

  except:
    if 'v' in opts or 'verbose' in opts:
        print(cmd)
        import traceback
        traceback.print_exc()

    sys.exit(1)
    
  sys.exit(0)
