import logging
import numpy as np
import pyqtgraph.multiprocess as mp

from .population import Population
from .. import cells


class SGC(Population):
    """A population of spiral ganglion cells.
    
    The cell distribution is uniform from 2kHz to 64kHz, evenly divided between
    spontaneous rate groups.
    """

    type = "sgc"

    def __init__(self, species="mouse", model="dummy", **kwds):
        # Completely fabricated cell distribution: uniform from 2kHz to 40kHz,
        # evenly divided between SR groups. We only go up to 40kHz because the
        # auditory periphery model does not support >40kHz.
        freqs = self._get_cf_array(species)
        fields = [("cf", float), ("sr", int)]  # 0=low sr, 1=mid sr, 2=high sr
        super(SGC, self).__init__(
            species, len(freqs), fields=fields, model=model, **kwds
        )
        self._cells["cf"] = freqs
        # old version:
        # evenly distribute SR groups
        # self._cells['sr'] = np.arange(len(freqs)) % 3
        # new version:
        # draw from distributions matching approximate SR distribution
        sr_vals = self.get_sgcsr_array(freqs, species="mouse")
        self._cells["sr"] = sr_vals

    def set_seed(self, seed):
        self.next_seed = seed

    def create_cell(self, cell_rec):
        """ Return a single new cell to be used in this population. The 
        *cell_rec* argument is the row from self.cells that describes the cell 
        to be created.
        """
        return cells.SGC.create(
            species=self.species,
            cf=cell_rec["cf"],
            sr=cell_rec["sr"],
            **self._cell_args
        )

    def connect_pop_to_cell(self, pop, index):
        # SGC does not support any inputs
        assert len(self.connections) == 0

    def set_sound_stim(self, stim, parallel=False):
        """Set a sound stimulus to generate spike trains for all (real) cells
        in this population.
        """
        real = self.real_cells()
        logging.info("Assigning spike trains to %d SGC cells..", len(real))
        if not parallel:
            for i, ind in enumerate(real):
                # logging.info("Assigning spike train to SGC %d (%d/%d)", ind, i, len(real))
                cell = self.get_cell(ind)
                cell.set_sound_stim(stim, self.next_seed)
                self.next_seed += 1

        else:
            seeds = range(self.next_seed, self.next_seed + len(real))
            self.next_seed = seeds[-1] + 1
            tasks = zip(seeds, real)
            trains = [None] * len(tasks)
            # generate spike trains in parallel
            with mp.Parallelize(
                enumerate(tasks),
                trains=trains,
                progressDialog="Generating SGC spike trains..",
            ) as tasker:
                for i, x in tasker:
                    seed, ind = x
                    cell = self.get_cell(ind)
                    train = cell.generate_spiketrain(stim, seed)
                    tasker.trains[i] = train
            # collected all trains; now assign to cells
            for i, ind in enumerate(real):
                cell = self.get_cell(ind)
                cell.set_spiketrain(trains[i])
