"""

Description: This file defines the class that acts as a template for creating the soma and the dendritic arbor.
             It is based on Rumbell et al. (2016), which in turn borrows from Traub et al. (2003).
             These are available in ModelDB with accession numbers 184497 and 20756.

Edit History: Created by Nilapratim Sengupta in July-August 2023.

"""

# Import statements
from neuron import h

# Defining the class
class SomaDendritesTemplate:

    def __init__(self):
        
        # Defining number of sections
        self.noOfComp = 69
        self.noOfAux1 = 4
        self.noOfAux2 = 8

        # Creating sections
        # self.comp = [h.Section(name='comp') for _ in range(self.noOfComp)]
        self.comp = [h.Section(name=f'comp[{loopCounter}]') for loopCounter in range(self.noOfComp)]
        # self.aux10to13 = [h.Section(name='aux10to13') for _ in range(self.noOfAux1)]
        self.aux10to13 = [h.Section(name=f'aux10to13[{loopCounter}]') for loopCounter in range(self.noOfAux1)]
        self.aux38 = h.Section(name='aux38')
        # self.aux2to9 = [h.Section(name='aux2to9') for _ in range(self.noOfAux2)]
        self.aux2to9 = [h.Section(name=f'aux2to9[{loopCounter}]') for loopCounter in range(self.noOfAux2)]

        # Deleting comp[0] since model starts from comp[1] which is the soma
        h.delete_section(sec=self.comp[0])

        # Defining topology of the pyramidal cell
        # Apical
        for loopCounter in range(61, 69):
            self.comp[loopCounter].connect(self.comp[loopCounter - 8](1))
        for loopCounter in range(53, 61):
            self.comp[loopCounter].connect(self.comp[loopCounter - 8](1))
        for loopCounter in range(49, 53):
            self.comp[loopCounter].connect(self.comp[44](1))
        for loopCounter in range(45, 49):
            self.comp[loopCounter].connect(self.comp[43](1))
        for loopCounter in range(43, 45):
            self.comp[loopCounter].connect(self.comp[loopCounter - 2](1))
        for loopCounter in range(41, 43):
            self.comp[loopCounter].connect(self.comp[40](1))
        self.comp[40].connect(self.comp[39](1))
        self.comp[39].connect(self.comp[38](1))
        self.comp[38].connect(self.aux38(1))
        self.aux38.connect(self.comp[1](0.5))
        # Oblique Apical
        for loopCounter in range(4):
            self.aux10to13[loopCounter].connect(self.comp[38](0.5))
            self.comp[loopCounter+10].connect(self.aux10to13[loopCounter](1))
            self.comp[loopCounter+22].connect(self.comp[loopCounter+10](1))
            self.comp[loopCounter+34].connect(self.comp[loopCounter+22](1))
        # Basal
        for loopCounter in range(8):
            self.aux2to9[loopCounter].connect(self.comp[1](0.5))
            self.comp[loopCounter+2].connect(self.aux2to9[loopCounter](1))
            self.comp[loopCounter+14].connect(self.comp[loopCounter+2](1))
            self.comp[loopCounter+26].connect(self.comp[loopCounter+14](1))

        # Defining subsets within the pyramidal cell
        self.Level1 = h.SectionList()
        self.Level1.append(self.comp[1])
        self.Level2 = h.SectionList()
        for loopCounter in range(2, 14):
            self.Level2.append(self.comp[loopCounter])
        self.Level3 = h.SectionList()
        for loopCounter in range(14, 26):
            self.Level3.append(self.comp[loopCounter])
        self.Level4 = h.SectionList()
        for loopCounter in range(26, 38):
            self.Level4.append(self.comp[loopCounter])
        self.Level5 = h.SectionList()
        self.Level5.append(self.comp[38])
        self.Level6 = h.SectionList()
        self.Level6.append(self.comp[39])
        self.Level7 = h.SectionList()
        self.Level7.append(self.comp[40])
        self.Level8 = h.SectionList()
        for loopCounter in range(41, 43):
            self.Level8.append(self.comp[loopCounter])
        self.Level9 = h.SectionList()
        for loopCounter in range(43, 45):
            self.Level9.append(self.comp[loopCounter])
        self.Level10 = h.SectionList()
        for loopCounter in range(45, 53):
            self.Level10.append(self.comp[loopCounter])
        self.Level11 = h.SectionList()
        for loopCounter in range(53, 61):
            self.Level11.append(self.comp[loopCounter])
        self.Level12 = h.SectionList()
        for loopCounter in range(61, 69):
            self.Level12.append(self.comp[loopCounter])

        self.Soma = h.SectionList()
        self.Soma.append(self.comp[1])

        self.Dendrites = h.SectionList()
        for loopCounter in range(2, 69):
            self.Dendrites.append(self.comp[loopCounter])

        self.Aux = h.SectionList()
        for loopCounter in range(4):
            self.Aux.append(self.aux10to13[loopCounter])
        self.Aux.append(self.aux38)
        for loopCounter in range(8):
            self.Aux.append(self.aux2to9[loopCounter])

        self.SomaDendrites = h.SectionList()
        for loopCounter in range(1, 69):
            self.SomaDendrites.append(self.comp[loopCounter])
        # for loopCounter in range(4):
        #     self.SomaDendrites.append(self.aux10to13[loopCounter])
        # self.SomaDendrites.append(self.aux38)
        # for loopCounter in range(8):
        #     self.SomaDendrites.append(self.aux2to9[loopCounter])

        self.Basal = h.SectionList()
        for loopCounter in range(2, 10):
            self.Basal.append(self.comp[loopCounter])
        for loopCounter in range(14, 22):
            self.Basal.append(self.comp[loopCounter])
        for loopCounter in range(26, 34):
            self.Basal.append(self.comp[loopCounter])

        self.Oblique = h.SectionList()
        for loopCounter in range(10, 14):
            self.Oblique.append(self.comp[loopCounter])
        for loopCounter in range(22, 26):
            self.Oblique.append(self.comp[loopCounter])
        for loopCounter in range(34, 38):
            self.Oblique.append(self.comp[loopCounter])

        self.Proximal = h.SectionList()
        for loopCounter in range(2, 14):
            self.Proximal.append(self.comp[loopCounter])
        self.Proximal.append(self.comp[39])

        self.Distal = h.SectionList()
        for loopCounter in range(45, 53):
            self.Distal.append(self.comp[loopCounter])
        for loopCounter in range(53, 61):
            self.Distal.append(self.comp[loopCounter])
        for loopCounter in range(61, 69):
            self.Distal.append(self.comp[loopCounter])
            
        # Defining 3D topology
        for section in self.comp[1].wholetree():
            h.pt3dclear(sec=section)
         
        for loopCounter in range(4):   
            h.pt3dadd(-134, -14, 0, 1, sec=self.aux10to13[loopCounter])
            h.pt3dadd(-104, -14, 0, 1, sec=self.aux10to13[loopCounter])
        h.pt3dadd(-149, -14, 0, 1, sec=self.aux38)
        h.pt3dadd(-134, -14, 0, 1, sec=self.aux38)
        for loopCounter in range(8):   
            h.pt3dadd(-149, -14, 0, 1, sec=self.aux2to9[loopCounter])
            h.pt3dadd(-134, -14, 0, 1, sec=self.aux2to9[loopCounter])
            
        h.pt3dadd(-149, -14, 0, 1, sec=self.comp[1])
        h.pt3dadd(-134, -14, 0, 1, sec=self.comp[1])
        h.pt3dadd(-134, -14, 0, 1, sec=self.comp[38])
        h.pt3dadd(-104, -14, 0, 1, sec=self.comp[38])
        h.pt3dadd(-104, -14, 0, 1, sec=self.comp[39])
        h.pt3dadd(-74, -14, 0, 1, sec=self.comp[39])
        h.pt3dadd(-74, -14, 0, 1, sec=self.comp[40])
        h.pt3dadd(-44, -14, 0, 1, sec=self.comp[40])

        h.pt3dadd(-44, -14, 0, 1, sec=self.comp[41])
        h.pt3dadd(-14, 30, 0, 1, sec=self.comp[41])
        h.pt3dadd(-14, 30, 0, 1, sec=self.comp[43])
        h.pt3dadd(0, 45, 0, 1, sec=self.comp[43])

        h.pt3dadd(0, 45, 0, 1, sec=self.comp[45])
        h.pt3dadd(45, 75, 0, 1, sec=self.comp[45])
        h.pt3dadd(0, 45, 0, 1, sec=self.comp[46])
        h.pt3dadd(45, 60, 0, 1, sec=self.comp[46])
        h.pt3dadd(0, 45, 0, 1, sec=self.comp[47])
        h.pt3dadd(45, 30, 0, 1, sec=self.comp[47])
        h.pt3dadd(0, 45, 0, 1, sec=self.comp[48])
        h.pt3dadd(45, 15, 0, 1, sec=self.comp[48])
        
        h.pt3dadd(45, 75, 0, 1, sec=self.comp[53])
        h.pt3dadd(75, 75, 0, 1, sec=self.comp[53])
        h.pt3dadd(45, 60, 0, 1, sec=self.comp[54])
        h.pt3dadd(75, 60, 0, 1, sec=self.comp[54])
        h.pt3dadd(45, 30, 0, 1, sec=self.comp[55])
        h.pt3dadd(75, 30, 0, 1, sec=self.comp[55])
        h.pt3dadd(45, 15, 0, 1, sec=self.comp[56])
        h.pt3dadd(75, 15, 0, 1, sec=self.comp[56])

        h.pt3dadd(75, 75, 0, 1, sec=self.comp[61])
        h.pt3dadd(90, 90, 0, 1, sec=self.comp[61])
        h.pt3dadd(75, 60, 0, 1, sec=self.comp[62])
        h.pt3dadd(90, 75, 0, 1, sec=self.comp[62])
        h.pt3dadd(75, 30, 0, 1, sec=self.comp[63])
        h.pt3dadd(90, 15, 0, 1, sec=self.comp[63])
        h.pt3dadd(75, 15, 0, 1, sec=self.comp[64])
        h.pt3dadd(90, 0, 0, 1, sec=self.comp[64])

        h.pt3dadd(-44, -14, 0, 1, sec=self.comp[42])
        h.pt3dadd(-14, -59, 0, 1, sec=self.comp[42])
        h.pt3dadd(-14, -59, 0, 1, sec=self.comp[44])
        h.pt3dadd(0, -74, 0, 1, sec=self.comp[44])

        h.pt3dadd(0, -74, 0, 1, sec=self.comp[49])
        h.pt3dadd(45, -44, 0, 1, sec=self.comp[49])
        h.pt3dadd(0, -74, 0, 1, sec=self.comp[50])
        h.pt3dadd(45, -59, 0, 1, sec=self.comp[50])
        h.pt3dadd(0, -74, 0, 1, sec=self.comp[51])
        h.pt3dadd(45, -89, 0, 1, sec=self.comp[51])
        h.pt3dadd(0, -74, 0, 1, sec=self.comp[52])
        h.pt3dadd(45, -104, 0, 1, sec=self.comp[52])

        h.pt3dadd(45, -44, 0, 1, sec=self.comp[57])
        h.pt3dadd(75, -44, 0, 1, sec=self.comp[57])
        h.pt3dadd(45, -59, 0, 1, sec=self.comp[58])
        h.pt3dadd(75, -59, 0, 1, sec=self.comp[58])
        h.pt3dadd(45, -89, 0, 1, sec=self.comp[59])
        h.pt3dadd(75, -89, 0, 1, sec=self.comp[59])
        h.pt3dadd(45, -104, 0, 1, sec=self.comp[60])
        h.pt3dadd(75, -104, 0, 1, sec=self.comp[60])

        h.pt3dadd(75, -44, 0, 1, sec=self.comp[65])
        h.pt3dadd(90, -29, 0, 1, sec=self.comp[65])
        h.pt3dadd(75, -59, 0, 1, sec=self.comp[66])
        h.pt3dadd(90, -44, 0, 1, sec=self.comp[66])
        h.pt3dadd(75, -89, 0, 1, sec=self.comp[67])
        h.pt3dadd(90, -104, 0, 1, sec=self.comp[67])
        h.pt3dadd(75, -104, 0, 1, sec=self.comp[68])
        h.pt3dadd(90, -119, 0, 1, sec=self.comp[68])

        h.pt3dadd(-104, -14, 0, 1, sec=self.comp[10])
        h.pt3dadd(-119, 0, 0, 1, sec=self.comp[10])
        h.pt3dadd(-119, 0, 0, 1, sec=self.comp[22])
        h.pt3dadd(-119, 30, 0, 1, sec=self.comp[22])
        h.pt3dadd(-119, 30, 0, 1, sec=self.comp[34])
        h.pt3dadd(-119, 60, 0, 1, sec=self.comp[34])

        h.pt3dadd(-104, -14, 0, 1, sec=self.comp[11])
        h.pt3dadd(-89, 0, 0, 1, sec=self.comp[11])
        h.pt3dadd(-89, 0, 0, 1, sec=self.comp[23])
        h.pt3dadd(-89, 30, 0, 1, sec=self.comp[23])
        h.pt3dadd(-89, 30, 0, 1, sec=self.comp[35])
        h.pt3dadd(-89, 60, 0, 1, sec=self.comp[35])

        h.pt3dadd(-104, -14, 0, 1, sec=self.comp[12])
        h.pt3dadd(-119, -29, 0, 1, sec=self.comp[12])
        h.pt3dadd(-119, -29, 0, 1, sec=self.comp[24])
        h.pt3dadd(-119, -59, 0, 1, sec=self.comp[24])
        h.pt3dadd(-119, -59, 0, 1, sec=self.comp[36])
        h.pt3dadd(-119, -89, 0, 1, sec=self.comp[36])

        h.pt3dadd(-104, -14, 0, 1, sec=self.comp[13])
        h.pt3dadd(-89, -29, 0, 1, sec=self.comp[13])
        h.pt3dadd(-89, -29, 0, 1, sec=self.comp[25])
        h.pt3dadd(-89, -59, 0, 1, sec=self.comp[25])
        h.pt3dadd(-89, -59, 0, 1, sec=self.comp[37])
        h.pt3dadd(-89, -89, 0, 1, sec=self.comp[37])

        h.pt3dadd(-149, -14, 0, 1, sec=self.comp[2])
        h.pt3dadd(-164, 30, 0, 1, sec=self.comp[2])
        h.pt3dadd(-164, 30, 0, 1, sec=self.comp[14])
        h.pt3dadd(-179, 45, 0, 1, sec=self.comp[14])
        h.pt3dadd(-179, 45, 0, 1, sec=self.comp[26])
        h.pt3dadd(-194, 60, 0, 1, sec=self.comp[26])

        h.pt3dadd(-149, -14, 0, 1, sec=self.comp[3])
        h.pt3dadd(-179, 30, 0, 1, sec=self.comp[3])
        h.pt3dadd(-179, 30, 0, 1, sec=self.comp[15])
        h.pt3dadd(-194, 45, 0, 1, sec=self.comp[15])
        h.pt3dadd(-194, 45, 0, 1, sec=self.comp[27])
        h.pt3dadd(-209, 60, 0, 1, sec=self.comp[27])

        h.pt3dadd(-149, -14, 0, 1, sec=self.comp[4])
        h.pt3dadd(-194, 15, 0, 1, sec=self.comp[4])
        h.pt3dadd(-194, 15, 0, 1, sec=self.comp[16])
        h.pt3dadd(-209, 30, 0, 1, sec=self.comp[16])
        h.pt3dadd(-209, 30, 0, 1, sec=self.comp[28])
        h.pt3dadd(-224, 45, 0, 1, sec=self.comp[28])

        h.pt3dadd(-149, -14, 0, 1, sec=self.comp[5])
        h.pt3dadd(-194, 0, 0, 1, sec=self.comp[5])
        h.pt3dadd(-194, 0, 0, 1, sec=self.comp[17])
        h.pt3dadd(-209, 15, 0, 1, sec=self.comp[17])
        h.pt3dadd(-209, 15, 0, 1, sec=self.comp[29])
        h.pt3dadd(-224, 30, 0, 1, sec=self.comp[29])

        h.pt3dadd(-149, -14, 0, 1, sec=self.comp[6])
        h.pt3dadd(-194, -29, 0, 1, sec=self.comp[6])
        h.pt3dadd(-194, -29, 0, 1, sec=self.comp[18])
        h.pt3dadd(-209, -44, 0, 1, sec=self.comp[18])
        h.pt3dadd(-209, -44, 0, 1, sec=self.comp[30])
        h.pt3dadd(-224, -59, 0, 1, sec=self.comp[30])

        h.pt3dadd(-149, -14, 0, 1, sec=self.comp[7])
        h.pt3dadd(-164, -59, 0, 1, sec=self.comp[7])
        h.pt3dadd(-164, -59, 0, 1, sec=self.comp[19])
        h.pt3dadd(-179, -74, 0, 1, sec=self.comp[19])
        h.pt3dadd(-179, -74, 0, 1, sec=self.comp[31])
        h.pt3dadd(-194, -89, 0, 1, sec=self.comp[31])

        h.pt3dadd(-149, -14, 0, 1, sec=self.comp[8])
        h.pt3dadd(-194, -44, 0, 1, sec=self.comp[8])
        h.pt3dadd(-194, -44, 0, 1, sec=self.comp[20])
        h.pt3dadd(-209, -59, 0, 1, sec=self.comp[20])
        h.pt3dadd(-209, -59, 0, 1, sec=self.comp[32])
        h.pt3dadd(-224, -74, 0, 1, sec=self.comp[32])

        h.pt3dadd(-149, -14, 0, 1, sec=self.comp[9])
        h.pt3dadd(-179, -59, 0, 1, sec=self.comp[9])
        h.pt3dadd(-179, -59, 0, 1, sec=self.comp[21])
        h.pt3dadd(-194, -74, 0, 1, sec=self.comp[21])
        h.pt3dadd(-194, -74, 0, 1, sec=self.comp[33])
        h.pt3dadd(-209, -89, 0, 1, sec=self.comp[33])