from importlibs import *



__author__ = "Andrea Maffezzoli"
__version__ = "$Revision: 0.21 - 21 Oct 2009$"
__name__ = "storage_functions"
__all__ = ["burst_data_storage", "pd_cluster_data"] #"store_pd_data_by_cluster", 



        
def pd_cluster_data(mean_sem_x_cluster,
                    cluster_data, 
                    bins,
                    cluster_nr_str = '',
                    no_sem = True,
                    data_name = '',
                    bin_divide = False,
                    time_normalization = False,
                    ensemble_normalization = True,
                    all_normalization = True):
    """The matrix matrix mean_sem_x_cluster is empty the first time
    (so for the first cluster) the present function is called by the
    same caller's instance.
    In input we get:
    - cluster_data, whcih are of course the data of all neurons in a cluster;
    - array bins, needed by the histogram and which has to be a numpy.array;
    - the string cluster_nr_str, or the string of the ordinal number of the cluster;
    - the boolean no_sem, defining if to add to mean_sem_x_cluster the SEM column;
    - the string data_name, giving the particular denomination of the cluster data;
    - the boolean bin_divide, saying if to divide data by the histogram bin length;
    - the boolean ensemble_normalization, saying if to perform data ensemble sum or mean;
    - the string all_normalization, stating if to divide by the number of all samples; 
    - the string time_normalization, defining the kind of normalization to apply, 
      choicing between tuples:
      - ('duration', the duration value), so dividing by experimental duration;
      - ('bursts_nr', the bursts nr), so dividing by the burst events nr;
    This function fills the matrix mean_sem_x_cluster with the following
    arrays (or columns data in the final file) for (cluster_nr)-th cluster:
    - the mean of all arrays in cluster_data;
    - the SEM (Standard Error of the Mean) of previous data;
    - the mean of all normalized arrays in cluster_data: such normalization
      is performed by the function shape_normalize in module statfuncs.py 
      and it returns cluster_data_shape_norm;
    - the SEM previous data;
    - the probability distribution (or cumulative distribution) function
      of previous data.
    """
    
    def fill_of_blank(l = 2): return [' '] * l

    def _bin_division(x, to_blank = False):
        
        if to_blank: return fill_of_blank()
        if bin_divide: return numpy.divide(x, bin_divide)
        return numpy.asarray(x)

    def _time_normalize(x, to_blank = False):
        
        if to_blank: return fill_of_blank()
        if not(time_normalization): return numpy.asarray(x)
        elif time_normalization[0] in ('duration', 'bursts_nr'):
            if time_normalization[1] == 0: return x 
            return numpy.divide(x, float(time_normalization[1]))

    def _ensemble_normalize(x, to_blank = False):
        
        if to_blank: return fill_of_blank()
        _f = numpy.mean if ensemble_normalization else numpy.sum
        return _f(x, axis = 0, dtype = numpy.float32).tolist()
    
    def _all_normalize(x, to_blank = False):
        """Giving a pdf if all_normalization is True"""

        if to_blank: return fill_of_blank()
        if all_normalization: return shape_normalize(x)
        return x
    
    def _mean(x, to_blank = False):

        if to_blank: return fill_of_blank()
        return numpy.mean(x, axis = 0, dtype = numpy.float32).tolist()

    def _sem(x, to_blank = False):
        
        if to_blank: return fill_of_blank()
        return numpy.divide(numpy.std(x, axis = 0), math.sqrt(len(x))).tolist()
    
    def _pd(x, norm = True, to_blank = False):
        
        if to_blank: return fill_of_blank()
        if norm: return numpy.cumsum(shape_normalize(x)).tolist()
        return numpy.cumsum(x).tolist()
    
    #####################################################################
    # Before to calculate the cumulative distribution 'numpy.cumsum',
    # apply shape_normalize to cluster_data_shape_norm_mean, otherwise 
    # the cum.distribution will not join an unitary value, because of
    # strictly all-zeros array, which, if present, lower the mean value
    # of the array cluster_data_shape_norm_mean.
    #####################################################################
    is_blank = not(int(cluster_data.__len__()))
        
    ########################################################
    # Checking if cluster_data in input is an 1-D array
    ########################################################
    cluster_data       = _time_normalize(_bin_division(cluster_data, to_blank = is_blank), to_blank = is_blank)
    cluster_data_mean  = _ensemble_normalize(cluster_data, to_blank = is_blank)
    cluster_data_shape = _all_normalize(cluster_data, to_blank = is_blank)

    ########################################################
    # Filling of original data statistics
    ########################################################
    mean_sem_x_cluster.extend([['bin'] + bins.tolist()[1:]] + \
                               [[''.join([data_name, cluster_nr_str])] + \
                               cluster_data_mean])
    if not(no_sem):
        mean_sem_x_cluster.extend([[''.join(['sem ', data_name, cluster_nr_str])] + \
                               _sem(cluster_data, to_blank = is_blank)])

    ########################################################
    # Filling of normalized data statistics
    ########################################################
    mean_sem_x_cluster.extend([[''.join([data_name, 'n', cluster_nr_str])] + \
                               _mean(cluster_data_shape, to_blank = is_blank)])
    if not(no_sem):
        mean_sem_x_cluster.extend([[''.join(['sem ', 'n', data_name, cluster_nr_str])] + \
                               _sem(cluster_data_shape, to_blank = is_blank)])
    
    #####################################################################
    # Filling of cumulative data statistics
    # NB: Not normalizing by now to test, otherwise use the following:
    #####################################################################
    mean_sem_x_cluster.extend([[''.join([data_name, 'c', cluster_nr_str])] + \
                               _pd(cluster_data_mean, norm = True, to_blank = is_blank)]) 
    
    mean_sem_x_cluster.extend([fill_of_blank()])


class burst_data_storage(object):
    """This class is dedited to the storage of all bursts data extracted
    during the performing of the single neuron burst detection (not those
    from Network burst detection and neither clustering data results)
    """

    ####################
    # Get data to store
    ####################
    def __init__(self,
                br_neuron, 
                br_data,
                results_path, 
                exp_name = '',
                **csv_options):  
        
        self.all_bursts_data = list(itertools.chain(*[sorted(br_data[k], key = operator.itemgetter(1)) \
                                                      for k in sort_neuro_names(br_neuron)]))
        self.results_path = results_path
        self.exp_name = exp_name
        self.csvname = self.results_path + self.exp_name + 'bursts_data.csv'

    def csv_format(self, start_time = None, end_time = None, *args, **kws):
        """Saving all bursts data for each neuron in *_bursts_data.csv file"""
    
        if start_time is not None and end_time is not None:
            csvname = '%s%s_bursts_data_%d-%d.csv' % (self.results_path, self.exp_name, start_time, end_time)
            bursts_data = [i for i in self.all_bursts_data if start_time <= i[1] <= end_time]
        else:
            csvname = self.csvname
            bursts_data = self.all_bursts_data
        csvwriterows_safe(csvname, [['neuron', 'burst start time'] + all_features] + bursts_data)
   
    def sqlite_format(self, *args, **kws):
        """Storing all bursts data extracted for each neuron,
        the same stored in the *_bursts_data.csv file"""
        
        #######################################################################
        # Open the sqlite file and deleting data eventually written previously
        #######################################################################
        f = open(self.csvname[:-3] + 'sql', 'wb')
        f.close()
        
        ###################################################################
        # Starting a connection to the database and getting a cursor to it 
        ###################################################################
        conn = sqlite3.connect(filename) 
        c = conn.cursor()
      
        ##############################################################
        # Create table where we will write bursts data and store them
        ##############################################################
        c.execute('create table bursts_data (neuron text, start_time real,' + \
                    ' real, '.join(all_features) + ' real)') 
        insert_str = ''.join(['insert into bursts_data values (?, ?, ?', \
                                ',?' * (len(all_features) - 1), ')'])
        c.executemany(insert_str, self.all_bursts_data)

        ##################################################################
        # Save or commit the changes, close the cursor and the connection
        ##################################################################
        conn.commit()
        c.close()
        conn.close()
