__author__ = "pbmanis"
"""
ccstim
Generate current-clamp (or voltage-clamp) stimulus waveforms from a dictionary
used for vectory play modes in current clamp
(prior version was called 'makestim')

Can generate several types of pulses

"""

import numpy as np


def ccstim(stim, dt, pulsetype="square"):
    """
    Create stimulus pulse waveforms of different types.
    
    Parameters
    ----------
    stim : dict
        a dictionary with keys [required]
        delay (delay to start of pulse train, msec [all]
        duration: duration of pulses in train, msec [all]
        Sfreq: stimulus train frequency (Hz) [timedSpikes]
        PT: post-train test delay [all]
        NP: number of pulses in the train [timedSpikes, exp]
        amp: amplitude of the pulses in the train [all]
        hypamp: amplitude of prehyperpolarizing pulse [hyp]
        hypdur: duration of prehyperpolarizing pulse [hyp]
        spikeTimes" times of spikes [timedSpikes]

    dt : time (microseconds) [required]
        step time, in microseconds. Required parameter

    pulsetype : string (default: 'square')
        Type of pulse to generate: one of square, hyp, timedspikes or exp
        square produces a train of "square" (retangular) pulses levels 0 and ampitude
        hyp is like square, but precedes the pulse train with a single prepulse
        of hypamp and hypdur
        timedspikes is like square, excpet the pulses are generated at times specified
        in the spikeTimes key in the stim dictionary
        exp: pulses with an exponential decay.
    
    TO DO: 
        add pulsetypes, including sine wave, rectified sine wave, etc.

    Returns
    -------
    list containing [waveform (numpy array),
                    maxtime(float),
                    timebase (numpy array)]
    """

    assert dt is not None
    assert "delay" in stim.keys()
    delay = int(np.floor(stim["delay"] / dt))
    if pulsetype in ["square", "hyp", "exp"]:
        ipi = int(np.floor((1000.0 / stim["Sfreq"]) / dt))
    pdur = int(np.floor(stim["duration"] / dt))
    posttest = int(np.floor(stim["PT"] / dt))
    if pulsetype not in ["timedSpikes"]:
        NP = int(stim["NP"])
    else:
        NP = len(stim["spikeTimes"])
    tstims = [0] * NP
    if pulsetype == "hyp":
        assert "hypamp" in stim.keys()
        assert "hypdur" in stim.keys()
        hypdur = int(np.floor(stim["hypdur"] / dt))
        delay0 = delay  # save original delay

    if pulsetype in ["square", "hyp"]:
        maxt = dt * (stim["delay"] + (ipi * (NP + 2)) + posttest + pdur * 2)
        if pulsetype == "hyp":
            maxt = maxt + dt * stim["hypdur"]
            delay = delay + hypdur
        w = np.zeros(int(np.floor(maxt / dt)))
        for j in range(0, NP):
            t = (delay + j * ipi) * dt
            w[delay + ipi * j : delay + (ipi * j) + pdur] = stim["amp"]
            tstims[j] = delay + ipi * j
        if stim["PT"] > 0.0:
            send = delay + ipi * j
            for i in range(send + posttest, send + posttest + pdur):
                w[i] = stim["amp"]
        if pulsetype == "hyp":  # fill in the prepulse now
            for i in range(delay0, delay0 + hypdur):
                w[i] = stim["hypamp"]

    if pulsetype == "timedSpikes":
        maxt = np.max(stim["spikeTimes"]) + stim["PT"] + stim["duration"] * 2
        w = np.zeros(int(np.floor(maxt / dt)))
        for j in range(len(stim["spikeTimes"])):
            st = delay + int(np.floor(stim["spikeTimes"][j] / dt))
            t = st * dt
            w[st : st + pdur] = stim["amp"]
            tstims[j] = st

        if stim["PT"] > 0.0:
            for i in range(st + posttest, st + posttest + pdur):
                w[i] = stim["amp"]

    if pulsetype == "exp":
        maxt = dt * (stim["delay"] + (ipi * (NP + 2)) + posttest + pdur * 2)
        w = np.zeros(int(np.floor(maxt / dt)))

        for j in range(0, NP):
            for i in range(0, len(w)):
                if delay + ipi * j + i < len(w):
                    w[delay + ipi * j + i] = w[delay + ipi * j + i] + stim["amp"] * (
                        (1.0 - np.exp(-i / (pdur / 3.0)))
                        * np.exp(-(i - (pdur / 3.0)) / pdur)
                    )
            tstims[j] = delay + ipi * j
        if stim["PT"] > 0.0:
            send = delay + ipi * j
            for i in range(send + posttest, len(w)):
                w[i] += (
                    stim["amp"]
                    * (1.0 - np.exp(-i / (pdur / 3.0)))
                    * np.exp(-(i - (pdur / 3.0)) / pdur)
                )

    return (w, maxt, tstims)
