"""
Check that the features of `NeuronGroup` are available and correct.
"""

from brian2 import *
from brian2.tests.features import FeatureTest, InaccuracyError


class NeuronGroupIntegrationLinear(FeatureTest):
    category = "NeuronGroup"
    name = "Linear integration"
    tags = ["NeuronGroup", "run"]

    def run(self):
        self.tau = tau = 1 * second
        self.v_init = linspace(0.1, 1, 10)
        self.duration = 100 * ms
        G = self.G = NeuronGroup(10, "dv/dt=-v/tau:1")
        self.G.v = self.v_init
        run(self.duration)

    def results(self):
        v_correct = self.v_init * exp(-self.duration / self.tau)
        return v_correct, self.G.v[:]

    def check(self, maxrelerr, correct_end):
        v_correct, v_end = correct_end
        err = amax(abs(v_end - v_correct) / v_correct)
        if err > maxrelerr:
            raise InaccuracyError(err)


class NeuronGroupIntegrationEuler(FeatureTest):
    category = "NeuronGroup"
    name = "Euler integration"
    tags = ["NeuronGroup", "run"]

    def run(self):
        self.tau = tau = 1 * second
        self.v_init = linspace(0.1, 1, 10)
        self.duration = 100 * ms
        G = self.G = NeuronGroup(10, "dv/dt=-v**1.1/tau:1")
        self.G.v = self.v_init
        run(self.duration)

    def results(self):
        return self.G.v[:]

    compare = FeatureTest.compare_arrays


class NeuronGroupLIF(FeatureTest):
    category = "NeuronGroup"
    name = "Leaky integrate and fire"
    tags = ["NeuronGroup", "Threshold", "Reset", "run", "SpikeMonitor"]

    def run(self):
        self.tau = tau = 10 * ms
        self.duration = 1000 * ms
        G = self.G = NeuronGroup(1, "dv/dt=(1.2-v)/tau:1", threshold="v>1", reset="v=0")
        M = self.M = SpikeMonitor(self.G)
        run(self.duration)

    def results(self):
        return self.M.t[:]

    compare = FeatureTest.compare_arrays


class NeuronGroupLIFRefractory(FeatureTest):
    category = "NeuronGroup"
    name = "Refractory leaky integrate and fire"
    tags = ["NeuronGroup", "Threshold", "Reset", "Refractory", "run", "SpikeMonitor"]

    def run(self):
        self.tau = tau = 10 * ms
        self.duration = 1000 * ms
        G = self.G = NeuronGroup(
            1,
            "dv/dt=(1.2-v)/tau:1 (unless refractory)",
            threshold="v>1",
            reset="v=0",
            refractory=1 * ms,
        )
        M = self.M = SpikeMonitor(self.G)
        run(self.duration)

    def results(self):
        return self.M.t[:]

    compare = FeatureTest.compare_arrays


if __name__ == "__main__":
    #    ft = NeuronGroupIntegrationLinear()
    #    ft.run()
    #    res = ft.results()
    #    ft.check(res)

    ft = NeuronGroupIntegrationEuler()
    ft.run()
    res = ft.results()
