try: 
    import psyco
    psyco.full()
except ImportError: pass



__name__ = "importlibs"
__revision__ = "$Revision: 0.81 - 10 March 2011$"
__author__ = "Andrea Maffezzoli"
__doc__ = """This module imports other modules and defines attributes necessary to run the 'timeneuro_mod' and 'lfpts_fromNex' toolkits"""



import sys
_modules_to_import = ['os', 'traceback', 'dircache', 'shutil', 'mmap',
                      'pprint', 'time', 'string', 're', 'copy',
                      'math', 'bisect', 'itertools', 'operator', 'collections',
                      'fpformat', 'csv', 'datetime',
                      'struct', 'array', 'ctypes',
                      'numpy', 'scipy'] #'weakref', 'inspect', 'subprocess']

###########################################################
# For dynamic import of modules read also:
# http://it.diveintopython.org/regression_testing/dynamic_import.html
# To dynamically set an attribute inside a module read also:
# http://mail.python.org/pipermail/python-list/2005-March/313957.html
# Later dealing with 'no-Release' modules
###########################################################
for _mod in _modules_to_import: 
    try: globals()[_mod] = __import__(_mod)
    except ImportError: raise ImportError, "Impossible to import module %s" % _mod

###########################################################################
# At first a table of byte order, size and alignment of packed data
# from http://docs.python.org/library/struct.html
# Then the byte order on the present (running) system
###########################################################################
endianness_dict = { "native-native":    '@',
                    "native-standard":  '=',
                    "little":           '<',
                    "big":              '>',
                    "network":          '!' }
endian = endianness_dict.get(sys.byteorder, "native-standard")

###########################################################################
# Size of single items in array.array on the present archiecture
###########################################################################
long_size   = array.array('l').itemsize
int_size    = array.array('i').itemsize
short_size  = array.array('h').itemsize


###########################################################
# Importing modules I built.
# NB neurocorr and kmeans are based on scipy/numpy
###########################################################
try: from statf.statfuncs import *
except ImportError:
    raise ImportError, "Impossible to import module statf.statfuncs"
try: from statf import neurocorr
except ImportError:
    raise ImportError, "Impossible to import module statf.neurocorr"
try: from statf import kmeans
except ImportError:
    raise ImportError, "Impossible to import module statf.kmeans"
try: from statf import pca
except ImportError:
    raise ImportError, "Impossible to import module statf.pca"
try: from readbinary import readplx
except ImportError:
    raise ImportError, "Impossible to import module readbinary.readplx"
try: from readbinary import readnex
except ImportError:
    raise ImportError, "Impossible to import module readbinary.readnex"
###########################################################
# In order to import statf.signalfilter with py2exe, I had to replace
# line 2 of C:\Python26\Lib\site-packages\scipy\interpolate\polyint.py:
# - before:     from scipy import factorial   
# - now:        try: from scipy import factorial
#               except: pass
# Same problem as in:
#   http://thread.gmane.org/gmane.comp.python.scientific.user/20783    
###########################################################
try: from statf.signalprocessing import *
except ImportError:
    raise ImportError, "Impossible to import module statf.signalprocessing"


######################################################################################
# It's important to do the following check, because when making the *.exe via py2exe, 
# this exe is built in the subdirectory '.\/neurocode\/xlstat_clusters\/', but the
# settings files are in the directory '.\/neurocode\/'
######################################################################################
basepath = os.getcwd().replace('\\', '\/') + '\/'
for _dir in ('timeneuro_mod\/', 'timeneuro_mod1\/', 'lfpts_fromNex\/'):
    if basepath.endswith(_dir):  basepath = basepath.replace(_dir, '')


######################################################################
# Reading all data settings from 'burst_detection_settings.txt'.
# Read also: http://stackoverflow.com/questions/436198/what-is-an-alternative-to-execfile-in-python-3-0
######################################################################
try:
    with open(basepath + 'burst_detection_settings.txt', 'r') as settings_f:
        exec(settings_f.read()) #, globals(), locals())
    ACF_bin = getattr(globals(), 'ACF_bin', ACF_delay / 100.)
    #execfile(basepath + 'burst_detection_settings.txt', globals(), locals())
except IOError, (errno, strerror):
    raise IOError, "I/O error(%s): %s" % (errno, strerror)

###########################################################
# Importing 'no-Release' modules
###########################################################
if 'Release' not in globals():
    _optional_modules_to_import = ['pylab']
    for _mod in _optional_modules_to_import:
        try:
            globals()[_mod] = __import__(_mod); globals()['%s_loaded' % _mod] = True
        except ImportError:
            exec('%s_loaded = False' % _mod); print "Impossible to import module %s" % _mod


###########################################################
# IBI related func
###########################################################
def selectIBIs(x, y):
    """Return the not Null value between the two in input"""

    return filter(bool, [x, y])[0]

###########################################################
# Map of channels name on MEA 64 and 256
# Useful functions to apply on MEA data
# Denomination of channels units struct
#Sorting function for two Plexon neurons names (x and y in input).
#    If data come from a MEA 64 channels MEA, names are usually in the
#    format 12a [where 1: column; 2: row; a: unit], nr_12b, nr_22a,..,
#    possibly with the prefix 'nr_'; from a MEA 256 ch. we have A1a
#    [where A: column, no J or Q; etc..], A2c, ...A16b, B1a, ..B16a,
#    ..., R1b, ...R16c.
#    Such formats require distinct ways to sort neurons/channels.
#    Related tests:
#    i)   L=['nr_12a', 'nr_21a', 'nr_12b', 'nr_81b']
#         L.sort(cmp = ssort); print L
#    ii)  L=['12a', '21a', '12b', '81b']
#         L.sort(cmp = ssort); print L
#    iii) L=['A1a', 'A1c', 'A16b', 'A8c', 'B1a', 'B16a']
#         L.sort(cmp = ssort); print L
###########################################################
MEA256columns = tuple(filter(lambda c: c not in 'JQ', string.ascii_uppercase[:18]))
MEA256rows    = tuple(map(str, xrange(1, 17)))
MEA64columns  = MEA64rows = tuple(string.digits[1:-1])
UnitsNames    = dict(zip(xrange(1, 8), map(chr, xrange(97, 127))))
UnitsNames[0] = 'Unsorted'
CellsNames    = tuple(['a', 'b', 'c', 'd'])

def mea_position(str):
    """To extract MEA channel position in various formats, following the tuple:
       (row nr, column nr, cell nr) in the 8x8 grid of the MEA matrix."""

    p = int(re.compile('[0-9]{1,2}').search(str).group())
    return (p / 10, p % 10, (p / 10 - 1) * 8 + p % 10 - 1)


def mea_coord(x, augmented = False):
    """(row nr, column nr, cell nr) where cell_nr as in [a, b ; c, d]"""
    #if 65 <= ord(x[0]) <= 82 and 49 <= ord(x[1]) <= 57:
    if x[0] in MEA256columns:
        col, row = MEA256columns.index(x[0]), MEA256rows.index(x[1:-1])
    else:
        x = x[3*(x[:3] in ('nr_', 'ch_')):]
        col, row = MEA64columns.index(x[0]), MEA64columns.index(x[1])
    cell = CellsNames.index(x[-1])
    if augmented: return 2*col+cell%2, 2*row+cell//2, cell 
    return col, row, cell


def sort_neuro_names(x, y):
        
    ###########################################################
    # Reading first chars of a name to know if data comes
    # from a MEA with 64, new or old version, or 256 channels,
    # but they doesn't start with 'nr_'.
    ###########################################################
    if ord(x[0]) > 57 and ord(x[1]) <= 57:
        def num_ch(s):
            n = 1
            for c in s[1:]:
                v = ord(c)
                if v > 57: break
                n *= 10
                n += int(c)
            return n
        _s1 = cmp(x[0], y[0])
        if _s1: return _s1
        _s2 = cmp(num_ch(x), num_ch(y))
        if _s2: return _s2
        return cmp(x[-1], y[-1])
    else:
        ###########################################################
        # When channels names start with 'nr_', usually from MEA 64
        ###########################################################
        shift = 3 if x[:3] in ('nr_', 'ch_')  else 0
        _s1 = cmp(int(x[shift:shift+2]), int(y[shift:shift+2]))
        if _s1: return _s1
        return cmp(x[shift+2], y[shift+2])


def neighborsonMEA(ch):
    """Returning the neighbors on MEA of the channel in input"""

    ch = ch.lstrip('nr_').lstrip('ch_')
    c, r = ch[0], ch[1:]
    ####################
    # On MEA256
    ####################
    if c in string.ascii_uppercase: columns, rows = MEA256columns, MEA256rows
    ####################
    # On MEA64
    ####################
    else: columns, rows = MEA64columns, MEA64rows
    ci, ri, _max = columns.index(c), rows.index(r), len(columns)-1
    
    neighbors = []
    if ri > 0:
        neighbors.extend([columns[j]+rows[ri-1] for j in xrange(max((ci-1, 0)), min((ci+2, _max+1)))])
    if ci > 0: neighbors.append(columns[ci-1]+rows[ri])
    if ci < _max: neighbors.append(columns[ci+1]+rows[ri])
    if ri < _max:
        neighbors.extend([columns[j]+rows[ri+1] for j in xrange(max((ci-1, 0)), min((ci+2, _max+1)))])
    return neighbors


#############################################################
# Template of directories for each experiment
#############################################################   
templateFields = 'results plx intervals' 
expTemplate = collections.namedtuple('Experiment_directory_template', templateFields)
MEATemplate = expTemplate(('MEAdata_cluster_results', ()), 
                            ('MEAdata_plx', ('plx', 'nex')),
                            ('MEAdata_settings', ('txt', )))


###########################################################
# A function writing data on *.csv file, not returning an
# error if that file is already open
###########################################################
def csvwriterows_safe(filename, data, path = None):
    """This function data on csv file named filename, retrying
    this operation if a file with the same name is open"""
    
    if path:
        if not(os.path.exists(path)): os.makedirs(path, 0777)
        filename = path + filename
    while True:
        try:
            data_writer = csv.writer(open(filename, 'wb'), **csv_options)
            data_writer.writerows(data)
            break
        ###################################################
        # Probably the file has been left open!
        ###################################################
        except IOError, (errno, strerror):
            print ""
            print "I/O error(%s): %s" % (errno, strerror)
            print "Please close file: %s" % filename
            while True:
                ref = raw_input("When the file will be closed, digit Enter to continue")
                if ref == '': break
            pass
        except csv.Error, e:
            sys.exit('file %s: %s' % (filename, e))
            pass


##########################################################
# Settings about how to write csv files. It is needed to
# open files to write with option 'wb', and not only 'w'
##########################################################
csv_options = {'dialect': 'excel', 'delimiter': '\t', 'quoting': csv.QUOTE_NONE}


###############################
# All burst detection features
###############################
all_features = burst_features + desc_burst_features
all_features_set = set(all_features)


#############################################################
# Get all experiment to process as read on input file
# exp_to_process_file, stripping out the suffixes in input
# when requested or present.
#############################################################
def return_exp_to_process(exp_to_process_file, suffixtostrip = ()):

    experiments_to_proc = []
    f = open(basepath + exp_to_process_file, 'r')
    for line in f:
        line = line.rstrip('\n').rstrip('\r').strip()
        if not(line): continue
        if line[0] == '#': continue
        ix = line.find('#')
        if ix>0: line = line[:ix]
        line = line.strip()
        for suffix in suffixtostrip:
            if line.endswith(suffix): line = line[:-len(suffix)]
        if not(line): continue
        temp = line.split(',')
        line, selection = temp[0], set(temp[1:])
        _sep_pos = line.rfind('\\')
        exp_path, exp_name = line[:_sep_pos + 1], line[_sep_pos + 1:]
        _end_sep_pos = exp_name.rfind('#')
        if _end_sep_pos > -1:
            exp_name = exp_name[:_end_sep_pos]
            _end_sep_pos = exp_name.rfind(' ')
            if _end_sep_pos > -1: exp_name = exp_name[:_end_sep_pos]
        if not(exp_path): exp_path = basepath
        exp_path += exp_name
        experiments_to_proc.append((exp_path, exp_name, selection))
    f.close()
    return experiments_to_proc



#############################################################
# Build automatically a MEA experiment directories
# template from data placed in directory add_MEAdata   
#############################################################
def repoRelease(classinit):
    def func_decorated(thatclass, *args, **kwargs):
        classinit(thatclass, *args, **kwargs)
        if 'Release' in globals(): 
            while True: pass
    return func_decorate


#############################################################
# Add MEA data to a just-built experiment directories tree
#############################################################
def addMEAdata(classinit):
    
    data_dir = '%s%s' % (basepath, 'add_MEAdata')
    def func_decorated(thatclass, *args, **kwargs):

        exp_to_process_file = '%s%s' % (basepath, filter(bool, \
                (getattr(thatclass, 'exp_to_process_file', None), \
                'exp_to_process_file.txt'))[0])
    
        #############################################################
        # Checking if there are new MEA data  
        #############################################################
        if os.path.exists(data_dir):
            f_add_exp = open(exp_to_process_file, 'a')
            for x in os.listdir(data_dir):
                name, ext = x[:-4], x[-3:]
                _new_dir = '%s%s' % (basepath, name)
                if not(os.path.exists(_new_dir)): os.mkdir(_new_dir, 0777)
                for d, e in MEATemplate:
                    _new_subdir = '%s\\%s' % (_new_dir, d)
                    if not(os.path.exists(_new_subdir)): os.mkdir(_new_subdir, 0777)
                    if ext in e:
                        _file_path = '%s\\%s' % (data_dir, x)
                        _file_new_path = '%s\\%s' % (_new_subdir, x)
                        if not(os.path.exists(_file_new_path)):
                            shutil.move(_file_path, _file_new_path)
                        else: os.remove(_file_path)
                f_add_exp.write('\n#%s' % name)
                print 'Directory tree with files from experiment %s has been created.' % name
            f_add_exp.close()

        classinit(thatclass, *args, **kwargs)      
    
    return func_decorated


####################################################################
# CAPTURE TRACEBACK OR ERRORS AND STORING THEM ON A FILE
####################################################################
class catch_exception:
    """Capturing all error messages from traceback and redirecting them
    to a file. At regard read related docs at: 
    http://docs.python.org/library/traceback.html?highlight=traceback#traceback.print_exception
    The class want to recall the Template Method in Design Pattern: if a class inherhits the
    present one, it lets the latter to handle all try..except stuffs, and the sys.exc_info(),
    exposing all fundamental exception info, happened in the first class.
    """

    stdout      = sys.stdout
    stderr      = sys.stderr

    def catch(self, msg = None, exc_limit = None):
        """Opening the stderr file-like object (in 'w' or 'a' mode),
        storing the original sys.stderr value, updating it and
        redirecting error msgs to stream open to file 'logErrors.txt'
        Finally closing the stderr stream.
        """
        if not(getattr(self, 'stderr_file', None)):
            setattr(self, 'stderr_file', 'logError.txt')
        if not(getattr(self, 'stderr_open', None)):
            self.stderr = open(self.stderr_file, 'w')
            self.stderr_open = True
        else: self.stderr = open(self.stderr_file, 'a')

        if msg: print >> self.stderr, msg
        else:
            print >> self.stdout, 'Some error occurred!\nDetails in file %s' % self.stderr_file
            pprint.pprint(traceback.format_exception(*sys.exc_info(), limit = exc_limit), stream = self.stderr)
            if 'Release' not in globals():
                pprint.pprint(traceback.format_exception(*sys.exc_info(), limit = exc_limit))       
        
        self.stderr.close()


###############################################################################
# Classes and functions implementing useful utilities
###############################################################################
class NullDevice:
    """Class of a file-like to print on a NULL device"""

    def write(self, s): pass


def doNothing(self): pass


class getObjrefcount(object):
    """From http://blog.doughellmann.com/2009/10/pymotw-sys-part-3-memory-management-and.html"""
    
    def refcount(self):

        try: return sys.getrefcount(self)
        except: return None


class getObjsize(object):
    """From http://blog.doughellmann.com/2009/10/pymotw-sys-part-3-memory-management-and.html"""
    
    def __sizeof__(self):

        try:
            _size = object.__sizeof__(self)
            for v in self.__dict__.values():
                try: _size += sys.getsizeof(v)
                except: pass
            for v in self.__class__.__dict__.values():
                try: _size += sys.getsizeof(v)
                except: pass
        except: _size = None
        finally: return _size


###############################################################################
#
#           Useful decorators (implemented as function or classes)
#
###############################################################################
####################################################################
# Decorator CAPTURING TRACEBACK ERRORS MSG AND STORING THEM ON FILE
####################################################################
def catchException(initClass, exc_limit = None):
    
    stdout, stderr = sys.stdout, sys.stderr    
    def func_decorated(thatClass, *args, **kwargs):
        try: initClass(thatClass, *args, **kwargs)
        except:
            print >> stdout, "Some error occurred!\nDetails in file %s" % thatClass.stderr_file
            _stderrFile = getattr(thatClass, "stderr_file", "logError.txt")
            _stderr = open(_stderrFile, 'a')
            errmsg = traceback.format_exception(*sys.exc_info(), limit = exc_limit)
            moreinfo = getattr(thatClass, "moreinfo", "")
            print >>_stderr, moreinfo
            pprint.pprint(errmsg, stream = _stderr)
            print >>_stderr, ''
            if 'Release' not in globals(): pprint.pprint(errmsg)        
            _stderr.close()
    return func_decorated


###############################################################################
# Decorating the 'Release', not testing, version with a no-end loop
###############################################################################
def repoRelease(classinit):
    def func_decorated(thatclass, *args, **kwargs):
        classinit(thatclass, *args, **kwargs)
        if 'Release' in globals(): 
            while True: pass
    return func_decorated

###############################################################################
# A class based implementation of 'repoRelease' above from:
# http://funkyworklehead.blogspot.com/2008/12/how-to-decorate-init-or-another-python.html
# In this case decore the __init__ by calling such class: @classDecor()
###############################################################################
class classDecor:   
    def __call__(self, f):
        def wrap(init_self,*args,**kwargs):
            print 'Entry point in decorator'
            init_self.inserted_during_decoration = 1
            f(init_self,*args,**kwargs)
            print 'Exit point in decorator'
        return wrap


###############################################################################
# Useful decorators from:
# http://stacktrace.it/2009/05/una-scatola-degli-attrezzi-euleriana/
###############################################################################
def cronometro(funzione):
    def funzione_decorata(*args, **kwargs):
        inizio = time.time()
        valore_ritorno = funzione(*args, **kwargs)
        print '%s executed in %.3fs.' % (funzione.func_name, time.time()-inizio)
        return valore_ritorno
    return funzione_decorata

def memoize(funzione):
    cache = {}
    def funzione_decorata(*args):
        try: return cache[args]
        except KeyError:
            ret = funzione(*args)
            cache[args] = ret
            return ret
    return funzione_decorata

###############################################################################
# From: http://wiki.python.org/moin/PythonDecoratorLibrary#Memoize
###############################################################################
class memoized(object):
   """Decorator that caches a function's return value each time it is called.
   If called later with the same arguments, the cached value is returned, and
   not re-evaluated.
   """
   def __init__(self, func):
      self.func = func
      self.cache = {}
   def __call__(self, *args):
      try:
         return self.cache.setdefault(args,self.func(*args))
      except TypeError:
         # uncachable -- for instance, passing a list as an argument.
         # Better to not cache than to blow up entirely.
         return self.func(*args)
   def __repr__(self):
      """Return the function's docstring."""
      return self.func.__doc__


###############################################################################
# My tuned timer, used as decorator
###############################################################################
def timer(func):
    def func_decorated(*args, **kwargs):
        start = time.clock()
        func(*args, **kwargs)
        print 'Processing done in %.3fs.' % (time.clock()-start)
    return func_decorated


"""
######################################
# Import sqlite3 module if necessary
######################################
class dbms_storage(object, catch_exception):
    
    sqlalchemy_on, db_engine, engine, connection, meta = False, None, None, None, None

    def __init__(self):
        
        def doNothing(string):
            if string: print string; pass

        if 'Release' not in globals() and 'dbconn' in globals():
            try:
                import sqlalchemy
                sqlalchemy_on = True
                try:
                    import MySQLdb
                    self.db_engine = 'mysql'
                except ImportError:
                    try:
                        import sqlite3
                        self.db_engine = 'sqlite'
                    except ImportError:
                        self.connect = doNothing('Impossible to import a DBMS wrapper library')
            except ImportError:
                self.connect = doNothing('Impossible to import sqlalchemy')
        else: self.connect = doNothing('')               

    def connect(self):
        
        try:
            ##########################################################
            # Usually sqlalchemy.create_engine(
            # 'driver://username:password@host:port/database')
            # # http://www.sqlalchemy.org/docs/05/dbengine.html
            # or use a creator:
            # def connect():
            #   return psycopg.connect(user='scott', host='localhost')
            #   db = create_engine('postgres://', creator=connect)
            # with options:
            #   db = create_engine('postgres://...', encoding='latin1', echo=True)
            ##########################################################
            user = 'root'
            pwd = ''
            host = 'localhost'
            dbase = 'testdb'
            self.engine = sqlalchemy.create_engine('%s://%s:%s@%s/%s' % (self.db_engine, user, pwd, host, dbase), echo = True)
            self.connection = engine.connect()
            print 'DB conncetion available:\n sqlalchemy version: %s\n engine: %s' % (sqlalchemy.__version__, self.db_engine)
            self.meta = sqlalchemy.MetaData()
            self.meta.bind = engine
            table_exp = sqlalchemy.Table('experiments', self.meta, autoload=True)
            print 'Created connection to %s' % meta
            trans = self.connection.begin()
            try:
                res = self.connection.execute(sqlalchemy.sql.select([table_exp]))
                #for i in res: print i
                trans.commit()
            except:
                print 'some error captured by sqlalchemy'
                trans.rollback()
        except:
            self.catch(exc_limit = 1)


#################################
# In case of implementing a GUI with Tkinter
#################################
try:
    from Tkinter import *
    import tkMessageBox
    from tkFileDialog import *
except ImportError: pass #raise ImportError, "Impossible to import Tkinter modules"

#################################
# To enable time restriction use 
#################################    
#if os.name == 'nt' and time.localtime().tm_year >= 2010:
#   raise Exception, "Unexpected error: DLL compiler not found"
"""
