#!/usr/bin/python

"""
Test the basic membrane physiology of cell types.

Basic Usage:   python test_cells.py celltype species [--cc | --vc]

This script generates a cell of the specified type and species, then tests the
cell with a series of current/voltage pulses to produce I/V, F/I, and spike
latency analyses.

"""

import argparse
import os, sys
from neuron import h
import pyqtgraph as pg
import cnmodel
import cnmodel.cells as cells
from cnmodel.protocols import IVCurve, VCCurve

debugFlag = True
ax = None
h.celsius = 22
default_durs = [10.0, 100.0, 20.0]
cclamp = False

cellinfo = {
    "types": [
        "bushy",
        "bushycoop",
        "tstellate",
        "tstellatenav11",
        "dstellate",
        "dstellateeager",
        "sgc",
        "cartwheel",
        "pyramidal",
        "octopus",
        "tuberculoventral",
        "mso",
    ],
    "morphology": ["point", "waxon", "stick"],
    "nav": ["std", "jsrna", "nav11", "nacncoop"],
    "species": ["guineapig", "cat", "rat", "mouse"],
    "pulse": ["step", "pulse"],
}

# Format for ivranges is list of tuples. This allows finer increments in selected ranges, such as close to rest
ccivrange = {
    "mouse": {
        "bushy": {"pulse": [(-1, 1.2, 0.05)]},
        "bushycoop": {"pulse": [(-0.5, 0.7, 0.02)]},
        "tstellate": {"pulse": [(-1.0, 1.01, 0.05), (-0.015, 0, 0.005)]},
        "tstellatenav11": {"pulse": [(-1, 1.0, 0.1)]},
        "dstellate": {"pulse": [(-0.3, 0.301, 0.015)]},
        "octopus": {"pulse": [(-1.0, 1.0, 0.05)]},
        "sgc": {"pulse": [(-0.3, 0.6, 0.02)]},
        "cartwheel": {"pulse": [(-0.5, 0.5, 0.05)]},
        "pyramidal": {
            "pulse": [(-0.3, 0.3, 0.025), (-0.040, 0.025, 0.005)]
        },  # , 'prepulse': [(-0.25, -0.25, 0.25)]},
        "tuberculoventral": {"pulse": [(-0.35, 1.0, 0.05), (-0.040, 0.01, 0.005)]},
    },
    "guineapig": {
        "bushy": {"pulse": [(-1, 1.2, 0.05)]},
        "tstellate": {"pulse": [(-0.15, 0.15, 0.01)]},
        "dstellate": {"pulse": [(-0.25, 0.25, 0.025)]},
        "dstellateeager": {"pulse": [(-0.6, 1.0, 0.025)]},
        "octopus": {"pulse": [(-2.0, 6.0, 0.2)]},
        "sgc": {"pulse": [(-0.3, 0.6, 0.02)]},
        "mso": {"pulse": [(-1, 1.2, 0.05)]},
    },
    "rat": {
        "pyramidal": {
            "pulse": [(-0.3, 0.3, 0.025), (-0.040, 0.025, 0.005)]
        }  # 'prepulse': [(-0.25, -0.25, 0.25)]},
    },
}

# scales holds some default scaling to use in the cciv plots
# argument is {cellname: (xmin, xmax, IVymin, IVymax, FIspikemax,
# offset(for spikes), crossing (for IV) )}
## the "offset" refers to setting the axes back a bit
scale = {
    "bushy": (-1.0, -160.0, 1.0, -40, 0, 40, "offset", 5, "crossing", [0, -60]),
    "bushycoop": (-1.0, -160.0, 1.0, -40, 0, 40, "offset", 5, "crossing", [0, -60]),
    "tstellate": (-1.0, -160.0, 1.0, -40, 0, 40, "offset", 5, "crossing", [0, -60]),
    "tstellatenav11": (
        -1.0,
        -160.0,
        1.0,
        -40,
        0,
        40,
        "offset",
        5,
        "crossing",
        [0, -60],
    ),
    "tstellatedend": (-1.0, -160.0, 1.0, -40, 0, 40, "offset", 5, "crossing", [0, -60]),
    "dstellate": (-1.0, -160.0, 1.0, -40, 0, 40, "offset", 5, "crossing", [0, -60]),
    "dstellateeager": (
        -1.0,
        -160.0,
        1.0,
        -40,
        0,
        40,
        "offset",
        5,
        "crossing",
        [0, -60],
    ),
    "sgc:": (-1.0, -160.0, 1.0, -40, 0, 40, "offset", 5, "crossing", [0, -60]),
    "cartwheel": (-1.0, -160.0, 1.0, -40, 0, 40, "offset", 5, "crossing", [0, -60]),
    "pyramidal": (-1.0, -160.0, 1.0, -40, 0, 40, "offset", 5, "crossing", [0, -60]),
    "tuberculoventral": (
        -1.0,
        -160.0,
        1.0,
        -40,
        0,
        40,
        "offset",
        5,
        "crossing",
        [0, -60],
    ),
    "octopus": (-1.0, -160.0, 1.0, -40, 0, 40, "offset", 5, "crossing", [0, -60]),
    "mso": (-1.0, -160.0, 1.0, -40, 0, 40, "offset", 5, "crossing", [0, -60]),
}


class Tests:
    """
    Class to select cells for tests
    """

    def __init__(self):
        pass

    def selectCell(self, args):
        """
        Parameters
        ----------
        args : argparse args from command line
        
        Returns
        -------
        cell
            Instantiated cell of the selected celltype
        """
        h.celsius = float(args.temp)
        #
        # Spiral Ganglion cell tests
        #
        if args.celltype == "sgc":  # morphology is always "point" for SGCs
            cell = cells.SGC.create(
                debug=debugFlag,
                species=args.species,
                nach=args.nav,
                ttx=args.ttx,
                modelType=args.type,
            )

        #
        # Bushy tests
        #
        elif args.celltype == "bushy" and args.morphology == "point":
            cell = cells.Bushy.create(
                model="RM03",
                species=args.species,
                modelType=args.type,
                ttx=args.ttx,
                nach=args.nav,
                debug=debugFlag,
            )
        #            cell.soma().klt.gbar = 0.0003

        elif args.celltype == "bushy" and args.morphology == "waxon":
            cell = cells.Bushy.create(
                model="RM03",
                species=args.species,
                modelType=args.type,
                nach=args.nav,
                ttx=args.ttx,
                debug=debugFlag,
            )
            cell.add_axon()

        elif args.celltype == "bushy" and args.morphology == "stick":
            cell = cells.Bushy.create(
                model="RM03",
                species=args.species,
                modelType=args.type,
                morphology="cnmodel/morphology/bushy_stick.hoc",
                decorator=True,
                nach=args.nav,
                ttx=args.ttx,
                debug=debugFlag,
            )
            h.topology()

        elif args.celltype == "bushycoop" and args.morphology == "point":
            cell = cells.Bushy.create(
                model="RM03",
                species=args.species,
                modelType=args.type,
                ttx=args.ttx,
                nach=args.nav,
                debug=debugFlag,
            )

        #
        # T-stellate tests
        #
        elif args.celltype == "tstellate" and args.morphology == "point":
            cell = cells.TStellate.create(
                model="RM03",
                species=args.species,
                modelType=args.type,
                nach=args.nav,
                ttx=args.ttx,
                debug=debugFlag,
            )

        elif args.celltype == "tstellate" and args.morphology == "stick":
            cell = cells.TStellate.create(
                model="RM03",
                species=args.species,
                modelType=args.type,
                nach=args.nav,
                ttx=args.ttx,
                debug=debugFlag,
                morphology="cnmodel/morphology/tstellate_stick.hoc",
                decorator=True,
            )

        elif (
            args.celltype == "tstellatenav11" and args.morphology == "point"
        ):  # note this uses a different model...
            print("test_cells: Stellate NAV11")
            cell = cells.TStellateNav11.create(
                model="Nav11",
                species=args.species,
                modelType=None,
                ttx=args.ttx,
                debug=debugFlag,
            )

        elif (
            args.celltype == "tstellatenav11" and args.morphology == "stick"
        ):  # note this uses a different model...
            cell = cells.TStellateNav11.create(
                model="Nav11",
                species=args.species,
                modelType=None,
                morphology="cnmodel/morphology/tstellate_stick.hoc",
                decorator=True,
                ttx=args.ttx,
                debug=debugFlag,
            )
            h.topology()

        #
        # Octopus cell tests
        #
        elif args.celltype == "octopus" and args.morphology == "point":
            cell = cells.Octopus.create(
                species=args.species,
                modelType="RM03",  # args.type,
                nach=args.nav,
                ttx=args.ttx,
                debug=debugFlag,
            )

        elif (
            args.celltype == "octopus" and args.morphology == "stick"
        ):  # Go to spencer et al. model
            cell = cells.Octopus.create(
                modelType="Spencer",
                species=args.species,
                morphology="cnmodel/morphology/octopus_spencer_stick.hoc",
                decorator=True,
                nach=args.nav,
                ttx=args.ttx,
                debug=debugFlag,
            )
            h.topology()

        #
        # D-stellate tests
        #
        elif args.celltype == "dstellate":
            cell = cells.DStellate.create(
                debug=debugFlag, species=args.species, ttx=args.ttx, modelType=args.type
            )

        elif args.celltype == "dstellateeager":
            cell = cells.DStellateEager.create(
                debug=debugFlag, ttx=args.ttx, modelType=args.type
            )

        #
        # DCN pyramidal cell tests
        #
        elif args.celltype == "pyramidal":
            cell = cells.Pyramidal.create(
                modelType=args.type, ttx=args.ttx, debug=debugFlag
            )

        #
        # DCN tuberculoventral cell tests
        #
        elif args.celltype == "tuberculoventral" and args.morphology == "point":
            cell = cells.Tuberculoventral.create(
                species="mouse",
                modelType="TVmouse",
                ttx=args.ttx,
                nach=args.nav,
                debug=debugFlag,
            )

        elif args.celltype == "tuberculoventral" and args.morphology == "stick":
            cell = cells.Tuberculoventral.create(
                species="mouse",
                modelType="TVmouse",
                morphology="cnmodel/morphology/tv_stick.hoc",
                decorator=True,
                ttx=args.ttx,
                debug=debugFlag,
            )
            h.topology()

        #
        # DCN cartwheel cell tests
        #
        elif args.celltype == "cartwheel":
            cell = cells.Cartwheel.create(
                modelType=args.type, ttx=args.ttx, debug=debugFlag
            )

        #
        # MSO principal neuron tests
        #
        elif args.celltype == "mso" and args.morphology == "point":
            cell = cells.MSO.create(
                model="RM03",
                species=args.species,
                modelType=args.type,
                ttx=args.ttx,
                nach=args.nav,
                debug=debugFlag,
            )

        else:
            raise ValueError(
                "Cell Type %s and configurations nav=%s or config=%s are not available"
                % (args.celltype, args.nav, args.morphology)
            )

        print(cell.__doc__)
        self.cell = cell

    def run_test(self, sites, ptype, args):
        """
        Run either vc or cc test, and plot the result
        
        Parameters
        ----------
        args : argparse args from command line
        
        """
        self.cell.set_temperature(float(args.temp))
        print(self.cell.status)
        V0 = self.cell.find_i0(showinfo=True)
        #        self.cell.cell_initialize()
        print(
            "Currents at nominal Vrest= %.2f I = 0: I = %g "
            % (V0, self.cell.i_currents(V=V0))
        )
        self.cell.print_mechs(self.cell.soma)
        instant = self.cell.compute_rmrintau(auto_initialize=False, vrange=None)
        print(
            "    From Inst: Rin = {:7.1f}  Tau = {:7.1f}  Vm = {:7.1f}".format(
                instant["Rin"], instant["tau"], instant["v"]
            )
        )
        if args.cc is True:
            # define the current clamp electrode and default settings
            self.iv = IVCurve()
            self.iv.run(
                ccivrange[args.species][args.celltype],
                self.cell,
                durs=default_durs,
                sites=sites,
                reppulse=ptype,
                temp=float(args.temp),
            )
            ret = self.iv.input_resistance_tau()
            print(
                "    From IV: Rin = {:7.1f}  Tau = {:7.1f}  Vm = {:7.1f}".format(
                    ret["slope"], ret["tau"], ret["intercept"]
                )
            )
            self.iv.show(cell=self.cell)

        elif args.rmp is True:
            print("temperature: ", self.cell.status["temperature"])
            self.iv = IVCurve()
            self.iv.run(
                {"pulse": (0, 0, 1)},
                self.cell,
                durs=default_durs,
                sites=sites,
                reppulse=ptype,
                temp=float(args.temp),
            )
            self.iv.show(cell=self.cell, rmponly=True)

        elif args.vc is True:
            # define the voltage clamp electrode and default settings
            self.vc = VCCurve()
            self.vc.run((-120, 40, 5), self.cell)
            self.vc.show(cell=self.cell)

        else:
            raise ValueError("Nothing to run. Specify one of --cc, --vc, --rmp.")


def main():
    parser = argparse.ArgumentParser(
        description=(
            "test_cells.py:",
            " Biophysical representations of neurons (mostly auditory), test file",
        )
    )
    parser.add_argument("celltype", action="store")
    parser.add_argument("species", action="store", default="guineapig")
    parser.add_argument("--type", action="store", default=None)
    parser.add_argument(
        "--temp", action="store", default=22.0, help=("Temp DegC (22 default)")
    )
    # species is an optional option....
    parser.add_argument(
        "-m",
        action="store",
        dest="morphology",
        default="point",
        help=("Set morphology: %s " % [morph for morph in cellinfo["morphology"]]),
    )
    parser.add_argument(
        "--nav",
        action="store",
        dest="nav",
        default=None,
        help=("Choose sodium channel: %s " % [ch for ch in cellinfo["nav"]]),
    )
    parser.add_argument(
        "--ttx",
        action="store_true",
        dest="ttx",
        default=False,
        help=("Use TTX (no sodium current"),
    )
    parser.add_argument(
        "-p",
        action="store",
        dest="pulsetype",
        default="step",
        help=("Set CCIV pulse to step or repeated pulse"),
    )
    clampgroup = parser.add_mutually_exclusive_group()
    clampgroup.add_argument(
        "--vc", action="store_true", help="Run in voltage clamp mode"
    )
    clampgroup.add_argument(
        "--cc", action="store_true", help="Run in current clamp mode"
    )
    clampgroup.add_argument(
        "--rmp", action="store_true", help="Run to get RMP in current clamp mode"
    )

    args = parser.parse_args()
    if args.celltype not in cellinfo["types"]:
        print("cell: %s is not in our list of cell types" % (args.celltype))
        print("celltypes: ", cellinfo["types"])
        sys.exit(1)

    path = os.path.dirname(cnmodel.__file__)
    #    h.load_file("stdrun.hoc")
    #    h.load_file(os.path.join(path, "custom_init.hoc")) # replace init with one that gets closer to steady state

    # print 'Species: ', args.species
    # print 'Morphology configuration: ', args.morphology
    sites = None
    if args.pulsetype == "step":
        ptype = None
    else:
        ptype = "pulses"
    if args.morphology in cellinfo["morphology"]:
        print("Morphological configuration %s is ok" % args.morphology)

    t = Tests()
    t.selectCell(args)
    app = pg.mkQApp()
    t.run_test(sites, ptype, args)

    if sys.flags.interactive == 0:
        pg.QtGui.QApplication.exec_()


if __name__ == "__main__":
    main()
