##
# File: AmberPTParserListener.py
# Date: 27-Jan-2022
#
# Updates:
# Generated from AmberPTParser.g4 by ANTLR 4.10.1
""" ParserLister class for AMBER PT files.
    @author: Masashi Yokochi
"""
import sys
import re
import collections

from antlr4 import ParseTreeListener
from rmsd.calculate_rmsd import NAMES_ELEMENT  # noqa: F401 pylint: disable=no-name-in-module, import-error, unused-import

try:
    from wwpdb.utils.align.alignlib import PairwiseAlign  # pylint: disable=no-name-in-module
    from wwpdb.utils.nmr.mr.AmberPTParser import AmberPTParser
    from wwpdb.utils.nmr.mr.ParserListenerUtil import (checkCoordinates,
                                                       translateToStdAtomName,
                                                       translateToStdResName,
                                                       REPRESENTATIVE_MODEL_ID)
    from wwpdb.utils.nmr.ChemCompUtil import ChemCompUtil
    from wwpdb.utils.nmr.BMRBChemShiftStat import BMRBChemShiftStat
    from wwpdb.utils.nmr.NEFTranslator.NEFTranslator import NEFTranslator
    from wwpdb.utils.nmr.AlignUtil import (monDict3,
                                           letterToDigit, indexToLetter,
                                           alignPolymerSequence,
                                           assignPolymerSequence,
                                           trimSequenceAlignment,
                                           retrieveAtomIdentFromMRMap)
except ImportError:
    from nmr.align.alignlib import PairwiseAlign  # pylint: disable=no-name-in-module
    from nmr.mr.AmberPTParser import AmberPTParser
    from nmr.mr.ParserListenerUtil import (checkCoordinates,
                                           translateToStdAtomName,
                                           translateToStdResName,
                                           REPRESENTATIVE_MODEL_ID)
    from nmr.ChemCompUtil import ChemCompUtil
    from nmr.BMRBChemShiftStat import BMRBChemShiftStat
    from nmr.NEFTranslator.NEFTranslator import NEFTranslator
    from nmr.AlignUtil import (monDict3,
                               letterToDigit, indexToLetter,
                               alignPolymerSequence,
                               assignPolymerSequence,
                               trimSequenceAlignment,
                               retrieveAtomIdentFromMRMap)


def chunk_string(string, length=4):
    """ Split a string into fixed length chunks.
    """
    return [string[i:i + length] for i in range(0, len(string), length)]


# This class defines a complete listener for a parse tree produced by AmberPTParser.
class AmberPTParserListener(ParseTreeListener):

    # atom name mapping of public MR file between the archive coordinates and submitted ones
    __mrAtomNameMapping = None

    # CCD accessing utility
    __ccU = None

    # BMRB chemical shift statistics
    __csStat = None

    # NEFTranslator
    __nefT = None

    # Pairwise align
    __pA = None

    # coordinates information generated by ParserListenerUtil.checkCoordinates()
    __polySeqModel = None
    __nonPolyModel = None

    __hasPolySeqModel = False

    # polymer sequence of AMBER parameter/topology file
    __polySeqPrmTop = None

    __seqAlign = None
    __chainAssign = None

    # version information
    __version = None
    __date = None
    __time = None

    # title
    __title = None

    # radius set
    __radiusSet = None

    # residue label
    __residueLabel = None

    # residue_pointer
    __residuePointer = None

    # atom_name
    __atomName = None

    # amber_atom_type
    __amberAtomType = None

    # AMBER atom number dictionary
    __atomNumberDict = None

    # Fortran format
    __a_format_pat = re.compile(r'\((\d+)[aA](\d+)\)$')
    __i_format_pat = re.compile(r'\((\d+)[iI](\d+)\)$')
    __i_format_pat = re.compile(r'\((\d+)[eE](\d+)\.?(\d+)?\)$')

    # __cur_column_len = None
    __cur_word_len = None

    warningMessage = ''

    def __init__(self, verbose=True, log=sys.stdout,
                 representativeModelId=REPRESENTATIVE_MODEL_ID,
                 mrAtomNameMapping=None,
                 cR=None, cC=None, ccU=None, csStat=None, nefT=None):

        self.__mrAtomNameMapping = None if mrAtomNameMapping is None or len(mrAtomNameMapping) == 0 else mrAtomNameMapping

        if cR is not None:
            ret = checkCoordinates(verbose, log, representativeModelId, cR, cC, testTag=False)
            self.__polySeqModel = ret['polymer_sequence']
            self.__nonPolyModel = ret['non_polymer']

        self.__hasPolySeqModel = self.__polySeqModel is not None and len(self.__polySeqModel) > 0

        # CCD accessing utility
        self.__ccU = ChemCompUtil(verbose, log) if ccU is None else ccU

        # BMRB chemical shift statistics
        self.__csStat = BMRBChemShiftStat(verbose, log, self.__ccU) if csStat is None else csStat

        # NEFTranslator
        self.__nefT = NEFTranslator(verbose, log, self.__ccU, self.__csStat) if nefT is None else nefT

        # Pairwise align
        if self.__hasPolySeqModel:
            self.__pA = PairwiseAlign()
            self.__pA.setVerbose(verbose)

        self.versionStatements = 0
        self.amberAtomTypeStatements = 0
        self.angleEquilValueStatements = 0
        self.angleForceConstantStatements = 0
        self.anglesIncHydrogenStatements = 0
        self.anglesWithoutHydrogenStatements = 0
        self.atomicNumberStatements = 0
        self.atomNameStatements = 0
        self.atomTypeIndexStatements = 0
        self.atomsPerMoleculeStatements = 0
        self.bondEquilValueStatements = 0
        self.bondForceConstantStatements = 0
        self.bondsIncHydrogenStatements = 0
        self.bondsWithoutHydrogenStatements = 0
        self.boxDimensionsStatements = 0
        self.capInfoStatements = 0
        self.capInfo2Statements = 0
        self.chargeStatements = 0
        self.dihedralForceConstantStatements = 0
        self.dihedralPeriodicityStatements = 0
        self.dihedralPhaseStatements = 0
        self.dihedralsIncHydrogenStatements = 0
        self.dihedralsWithoutHydrogenStatements = 0
        self.excludedAtomsListStatements = 0
        self.hbcutStatements = 0
        self.hbondAcoefStatements = 0
        self.hbondBcoefStatements = 0
        self.ipolStatements = 0
        self.irotatStatements = 0
        self.joinArrayStatements = 0
        self.lennardJonesAcoefStatements = 0
        self.lennardJonesBcoefStatements = 0
        self.massStatements = 0
        self.nonbondedParmIndexStatements = 0
        self.numberExcludedAtomsStatements = 0
        self.pointersStatements = 0
        self.polarizabilityStatements = 0
        self.radiiStatements = 0
        self.radiusSetStatements = 0
        self.residueLabelStatements = 0
        self.residuePointerStatements = 0
        self.sceeScaleFactorStatements = 0
        self.scnbScaleFactorStatements = 0
        self.screenStatements = 0
        self.soltyStatements = 0
        self.solventPointersStatements = 0
        self.titleStatements = 0
        self.treeChainClassificationStatements = 0

    # Enter a parse tree produced by AmberPTParser#amber_pt.
    def enterAmber_pt(self, ctx: AmberPTParser.Amber_ptContext):  # pylint: disable=unused-argument
        self.__atomNumberDict = {}
        self.__polySeqPrmTop = []

    # Exit a parse tree produced by AmberPTParser#amber_pt.
    def exitAmber_pt(self, ctx: AmberPTParser.Amber_ptContext):  # pylint: disable=unused-argument
        if not self.__hasPolySeqModel:
            if len(self.warningMessage) == 0:
                self.warningMessage = None
            else:
                self.warningMessage = self.warningMessage[0:-1]
                self.warningMessage = '\n'.join(set(self.warningMessage.split('\n')))
            return

        if self.__residueLabel is None or self.__residuePointer is None or self.__atomName is None or self.__amberAtomType is None:
            return

        if len(self.__residueLabel) == 0 or len(self.__residuePointer) == 0 or len(self.__atomName) == 0 or len(self.__amberAtomType) == 0:
            return

        residuePointer2 = [resPoint - 1 for resPoint in self.__residuePointer]
        del residuePointer2[0]
        residuePointer2.append(self.__residuePointer[-1] + 1000)

        chainIndex = letterToDigit(self.__polySeqModel[0]['chain_id']) - 1  # set tentative chain_id from label_asym_id, which will be assigned to coordinate auth_asym_id
        chainId = indexToLetter(chainIndex)

        terminus = [atomName.endswith('T') for atomName in self.__atomName]

        atomTotal = len(self.__atomName)
        if terminus[0]:
            terminus[0] = False
        for i in range(0, atomTotal - 1):
            j = i + 1
            if terminus[i] and terminus[j]:
                terminus[i] = False
        if terminus[-1]:
            terminus[-1] = False

        seqIdList = []
        compIdList = []

        NON_METAL_ELEMENTS = ('H', 'C', 'N', 'O', 'P', 'S')

        prevAtomName = ''
        prevSeqId = None
        offset = 0
        for atomNum, (atomName, atomType) in enumerate(zip(self.__atomName, self.__amberAtomType), start=1):
            _seqId = next(resNum for resNum, (atomNumBegin, atomNumEnd)
                          in enumerate(zip(self.__residuePointer, residuePointer2), start=1)
                          if atomNumBegin <= atomNum <= atomNumEnd)
            compId = self.__residueLabel[_seqId - 1]
            # the second condition indicates metal ions
            if terminus[atomNum - 2]\
               or (compId == atomName and compId.title() in NAMES_ELEMENT)\
               or (len(prevAtomName) > 0 and prevAtomName[0] not in NON_METAL_ELEMENTS and prevSeqId != _seqId):
                self.__polySeqPrmTop.append({'chain_id': chainId,
                                             'seq_id': seqIdList,
                                             'auth_comp_id': compIdList})
                seqIdList = []
                compIdList = []
                chainIndex += 1
                chainId = indexToLetter(chainIndex)
                offset = 1 - _seqId
            seqId = _seqId + offset
            if seqId not in seqIdList:
                seqIdList.append(seqId)
                compIdList.append(compId)
            if compId not in monDict3 and self.__mrAtomNameMapping is not None:
                _, compId, atomName = retrieveAtomIdentFromMRMap(self.__mrAtomNameMapping, seqId, compId, atomName)
            self.__atomNumberDict[atomNum] = {'chain_id': chainId,
                                              'seq_id': seqId,
                                              'auth_comp_id': compId,
                                              'auth_atom_id': atomName,
                                              'atom_type': atomType}
            prevAtomName = atomName
            prevSeqId = _seqId

        self.__polySeqPrmTop.append({'chain_id': chainId,
                                     'seq_id': seqIdList,
                                     'auth_comp_id': compIdList})

        for ps in self.__polySeqPrmTop:
            chainId = ps['chain_id']
            compIdList = []
            for seqId, authCompId in zip(ps['seq_id'], ps['auth_comp_id']):
                authAtomIds = [atomNum['auth_atom_id'] for atomNum in self.__atomNumberDict.values()
                               if atomNum['chain_id'] == chainId
                               and atomNum['seq_id'] == seqId
                               and atomNum['auth_atom_id'][0] != 'H']
                authCompId = translateToStdResName(authCompId)
                if self.__ccU.updateChemCompDict(authCompId):
                    chemCompAtomIds = [cca[self.__ccU.ccaAtomId] for cca in self.__ccU.lastAtomList]
                    valid = True
                    for _atomId in authAtomIds:
                        if _atomId not in chemCompAtomIds:
                            valid = False
                            break
                        if not valid:
                            break
                    if valid:
                        compIdList.append(authCompId)
                        for atomNum in self.__atomNumberDict.values():
                            if atomNum['chain_id'] == chainId and atomNum['seq_id'] == seqId:
                                atomNum['comp_id'] = authCompId
                                atomNum['atom_id'] = atomNum['auth_atom_id']
                                if 'atom_type' in atomNum:
                                    del atomNum['atom_type']
                    else:
                        compId = self.__csStat.getSimilarCompIdFromAtomIds([atomNum['auth_atom_id']
                                                                            for atomNum in self.__atomNumberDict.values()
                                                                            if atomNum['chain_id'] == chainId
                                                                            and atomNum['seq_id'] == seqId])
                        if compId is not None:
                            compIdList.append(compId + '?')  # decide when coordinate is available
                            chemCompAtomIds = None
                            if self.__ccU.updateChemCompDict(compId):
                                chemCompAtomIds = [cca[self.__ccU.ccaAtomId] for cca in self.__ccU.lastAtomList]
                            for atomNum in self.__atomNumberDict.values():
                                if atomNum['chain_id'] == chainId and atomNum['seq_id'] == seqId:
                                    atomNum['comp_id'] = compId
                                    if chemCompAtomIds is not None and atomNum['auth_atom_id'] in chemCompAtomIds:
                                        atomNum['atom_id'] = atomNum['auth_atom_id']
                                        if 'atom_type' in atomNum:
                                            del atomNum['atom_type']
                        else:
                            compIdList.append('.')
                            unknownAtomIds = [_atomId for _atomId in authAtomIds if _atomId not in chemCompAtomIds]
                            self.warningMessage += f"[Unknown atom name] "\
                                f"{unknownAtomIds} are unknown atom names for {authCompId} residue.\n"
                            compIdList.append(f"? {authCompId} {unknownAtomIds}")
                else:
                    compId = self.__csStat.getSimilarCompIdFromAtomIds([atomNum['auth_atom_id']
                                                                        for atomNum in self.__atomNumberDict.values()
                                                                        if atomNum['chain_id'] == chainId
                                                                        and atomNum['seq_id'] == seqId])
                    if compId is not None:
                        compIdList.append(compId + '?')  # decide when coordinate is available
                        chemCompAtomIds = None
                        if self.__ccU.updateChemCompDict(compId):
                            chemCompAtomIds = [cca[self.__ccU.ccaAtomId] for cca in self.__ccU.lastAtomList]
                        for atomNum in self.__atomNumberDict.values():
                            if atomNum['chain_id'] == chainId and atomNum['seq_id'] == seqId:
                                atomNum['comp_id'] = compId
                                if chemCompAtomIds is not None and atomNum['auth_atom_id'] in chemCompAtomIds:
                                    atomNum['atom_id'] = atomNum['auth_atom_id']
                                    if 'atom_type' in atomNum:
                                        del atomNum['atom_type']
                    else:
                        compIdList.append('.')
                        """ deferred to assignNonPolymer()
                        self.warningMessage += f"[Unknown residue name] "\
                            f"{authCompId!r} is unknown residue name.\n"
                        """

            ps['comp_id'] = compIdList

        for atomNum in self.__atomNumberDict.values():
            if 'comp_id' in atomNum and atomNum['comp_id'] != atomNum['auth_comp_id']\
               and 'atom_id' not in atomNum:
                if self.__ccU.updateChemCompDict(atomNum['comp_id']):
                    chemCompAtomIds = [cca[self.__ccU.ccaAtomId] for cca in self.__ccU.lastAtomList]

                    atomId = translateToStdAtomName(atomNum['auth_atom_id'], atomNum['comp_id'], chemCompAtomIds)

                    if atomId is not None and atomId in chemCompAtomIds:
                        atomNum['atom_id'] = atomId
                        if 'atom_type' in atomNum:
                            del atomNum['atom_type']
                    elif atomNum['comp_id'] != atomNum['auth_comp_id']:
                        authCompId = translateToStdResName(atomNum['auth_comp_id'])
                        if self.__ccU.updateChemCompDict(authCompId):
                            chemCompAtomIds = [cca[self.__ccU.ccaAtomId] for cca in self.__ccU.lastAtomList]

                            atomId = translateToStdAtomName(atomNum['auth_atom_id'], authCompId, chemCompAtomIds)

                            if atomId is not None and atomId in chemCompAtomIds:
                                atomNum['atom_id'] = atomId
                                if 'atom_type' in atomNum:
                                    del atomNum['atom_type']
            else:
                authCompId = translateToStdResName(atomNum['auth_comp_id'])
                if self.__ccU.updateChemCompDict(authCompId):
                    atomIds = self.__nefT.get_valid_star_atom_in_xplor(authCompId, atomNum['auth_atom_id'])[0]
                    if len(atomIds) == 1:
                        atomNum['atom_id'] = atomIds[0]
                        if 'atom_type' in atomNum:
                            del atomNum['atom_type']

        file_type = 'nm-aux-amb'

        self.__seqAlign, compIdMapping = alignPolymerSequence(self.__pA, self.__polySeqModel, self.__polySeqPrmTop)

        for cmap in compIdMapping:
            for atomNum in self.__atomNumberDict.values():
                if atomNum['chain_id'] == cmap['chain_id'] and atomNum['seq_id'] == cmap['seq_id']:
                    atomNum['comp_id'] = cmap['comp_id']
                    atomNum['auth_comp_id'] = cmap['auth_comp_id']
                    atomNum['atom_id'] = atomNum['auth_atom_id']
                    if 'atom_type' in atomNum:
                        del atomNum['atom_type']

        for atomNum in self.__atomNumberDict.values():
            if 'atom_id' not in atomNum:
                if 'comp_id' not in atomNum or atomNum['comp_id'] == atomNum['auth_comp_id']:
                    authCompId = translateToStdResName(atomNum['auth_comp_id'])
                    if self.__ccU.updateChemCompDict(authCompId):
                        self.warningMessage += f"[Unknown atom name] "\
                            f"{atomNum['auth_atom_id']!r} is not recognized as the atom name of {atomNum['auth_comp_id']!r} residue.\n"
                else:
                    authCompId = translateToStdResName(atomNum['auth_comp_id'])
                    if self.__ccU.updateChemCompDict(authCompId):
                        atomNum['atom_id'] = atomNum['auth_atom_id']
                        self.warningMessage += f"[Unknown atom name] "\
                            f"{atomNum['auth_atom_id']!r} is not recognized as the atom name of {atomNum['comp_id']!r} residue "\
                            f"(the original residue label is {atomNum['auth_comp_id']!r}).\n"

        self.__chainAssign, message = assignPolymerSequence(self.__pA, self.__ccU, file_type, self.__polySeqModel, self.__polySeqPrmTop, self.__seqAlign)

        if len(message) > 0:
            self.warningMessage += message

        if self.__chainAssign is not None:

            if len(self.__polySeqModel) == len(self.__polySeqPrmTop):

                chain_mapping = {}

                for ca in self.__chainAssign:
                    ref_chain_id = ca['ref_chain_id']
                    test_chain_id = ca['test_chain_id']

                    if ref_chain_id != test_chain_id:
                        chain_mapping[test_chain_id] = ref_chain_id

                if len(chain_mapping) == len(self.__polySeqModel):

                    for ps in self.__polySeqPrmTop:
                        if ps['chain_id'] in chain_mapping:
                            ps['chain_id'] = chain_mapping[ps['chain_id']]

                    for atomNum in self.__atomNumberDict.values():
                        if atomNum['chain_id'] in chain_mapping:
                            atomNum['chain_id'] = chain_mapping[atomNum['chain_id']]

                    self.__seqAlign, _ = alignPolymerSequence(self.__pA, self.__polySeqModel, self.__polySeqPrmTop)
                    self.__chainAssign, _ = assignPolymerSequence(self.__pA, self.__ccU, file_type, self.__polySeqModel, self.__polySeqPrmTop, self.__seqAlign)

            trimSequenceAlignment(self.__seqAlign, self.__chainAssign)

            if self.__nonPolyModel is not None:

                # metal ion
                if any(ps for ps in self.__polySeqPrmTop
                       if len(ps['seq_id']) == 1 and ps['comp_id'][0].title() in NAMES_ELEMENT):
                    self.assignMetalIon()

                # other non-polymer
                nonPolyIndices = [idx for idx, ps in enumerate(self.__polySeqPrmTop)
                                  if not any(ca for ca in self.__chainAssign
                                             if ca['test_chain_id'] == ps['chain_id'])
                                  and len(set(ps['comp_id'])) == 1 and ps['comp_id'][0] == '.']

                if len(nonPolyIndices) > 0:
                    self.assignNonPolymer(nonPolyIndices)

                    for idx in sorted(nonPolyIndices, reverse=True):
                        del self.__polySeqPrmTop[idx]

        if len(self.warningMessage) == 0:
            self.warningMessage = None
        else:
            self.warningMessage = self.warningMessage[0:-1]
            self.warningMessage = '\n'.join(set(self.warningMessage.split('\n')))

    def assignMetalIon(self):
        if self.__nonPolyModel is None:
            return

        metals = collections.Counter(s2['comp_id'][0] for s2 in self.__polySeqPrmTop
                                     if len(s2['seq_id']) == 1 and s2['comp_id'][0].title() in NAMES_ELEMENT).most_common()

        for metal in metals:
            compId = metal[0]

            atomNums = [atomNum for atomNum in self.__atomNumberDict.values()
                        if atomNum['auth_comp_id'] == compId and atomNum['auth_atom_id'] == compId]

            nonPolys = [nonPoly for nonPoly in self.__nonPolyModel
                        if nonPoly['comp_id'][0] == compId]

            for atomNum, nonPoly in zip(atomNums, nonPolys):
                atomNum['chain_id'] = nonPoly['auth_chain_id']
                atomNum['seq_id'] = nonPoly['auth_seq_id'][0]

    def assignNonPolymer(self, nonPolyIndices):
        if self.__nonPolyModel is None:
            return

        authCompIds = []

        for idx, ps in enumerate(self.__polySeqPrmTop):
            if idx not in nonPolyIndices:
                continue
            for authCompId, compId in zip(ps['auth_comp_id'], ps['comp_id']):
                if compId != '.':
                    continue
                authCompIds.append(authCompId)

        nonPolyCompIds = collections.Counter(authCompIds).most_common()

        compIds = []
        for nonPoly in self.__nonPolyModel:
            compId = nonPoly['comp_id'][0]
            if compId.title() in NAMES_ELEMENT:
                continue
            compIds.append(compId)

        refCompIds = collections.Counter(compIds).most_common()

        comp_id_mapping = {}

        for authCompId in nonPolyCompIds:
            refCompId = next((compId[0] for compId in refCompIds if compId[1] == authCompId[1] and compId[1] not in comp_id_mapping.values()), None)
            if refCompId is None:
                self.warningMessage += f"[Unknown residue name] "\
                    f"{authCompId[0]!r} is unknown residue name.\n"
                continue
            comp_id_mapping[authCompId[0]] = refCompId

        for authCompId, compId in comp_id_mapping.items():
            chemCompAtomIds = None
            if self.__ccU.updateChemCompDict(compId):
                chemCompAtomIds = [cca[self.__ccU.ccaAtomId] for cca in self.__ccU.lastAtomList]

            authSeqKeys = []

            for idx, ps in enumerate(self.__polySeqPrmTop):
                if idx not in nonPolyIndices:
                    continue
                _chainId = ps['chain_id']
                for _authCompId, _compId, _seqId in zip(ps['auth_comp_id'], ps['comp_id'], ps['seq_id']):
                    if _authCompId != authCompId or _compId != '.':
                        continue
                    authSeqKeys.append((_chainId, _seqId))

            nonPolys = [nonPoly for nonPoly in self.__nonPolyModel
                        if nonPoly['comp_id'][0] == compId]

            reported_auth_atom_id = []

            for authSeqKey, nonPoly in zip(authSeqKeys, nonPolys):
                atomNums = [atomNum for atomNum in self.__atomNumberDict.values()
                            if atomNum['chain_id'] == authSeqKey[0] and atomNum['seq_id'] == authSeqKey[1]]

                for atomNum in atomNums:
                    atomNum['chain_id'] = nonPoly['auth_chain_id']
                    atomNum['seq_id'] = nonPoly['auth_seq_id'][0]
                    atomNum['comp_id'] = compId
                    authAtomId = atomNum['auth_atom_id']
                    if chemCompAtomIds is not None and authAtomId in chemCompAtomIds:
                        atomNum['atom_id'] = authAtomId
                    else:
                        if authAtomId not in reported_auth_atom_id:
                            atomNum['atom_id'] = atomNum['auth_atom_id']
                            self.warningMessage += f"[Unknown atom name] "\
                                f"{authAtomId!r} is not recognized as the atom name of {compId!r} residue "\
                                f"(the original residue label is {authCompId!r}).\n"
                            reported_auth_atom_id.append(authAtomId)

    # Enter a parse tree produced by AmberPTParser#version_statement.
    def enterVersion_statement(self, ctx: AmberPTParser.Version_statementContext):  # pylint: disable=unused-argument
        self.versionStatements += 1

    # Exit a parse tree produced by AmberPTParser#version_statement.
    def exitVersion_statement(self, ctx: AmberPTParser.Version_statementContext):
        self.__version = str(ctx.Version())
        self.__date = str(ctx.Date_time(0))
        if ctx.Date_time(0):
            self.__time = str(ctx.Date_time(1))

    # Enter a parse tree produced by AmberPTParser#amber_atom_type_statement.
    def enterAmber_atom_type_statement(self, ctx: AmberPTParser.Amber_atom_type_statementContext):  # pylint: disable=unused-argument
        self.amberAtomTypeStatements += 1

    # Exit a parse tree produced by AmberPTParser#amber_atom_type_statement.
    def exitAmber_atom_type_statement(self, ctx: AmberPTParser.Amber_atom_type_statementContext):
        if ctx.Simple_name(0):
            atomTypeList = []
            i = 0
            while ctx.Simple_name(i):
                chunk = chunk_string(str(ctx.Simple_name(i)).upper(), self.__cur_word_len)
                atomTypeList.extend(chunk)
                i += 1
            self.__amberAtomType = atomTypeList
            return
        self.amberAtomTypeStatements -= 1

    # Enter a parse tree produced by AmberPTParser#angle_equil_value_statement.
    def enterAngle_equil_value_statement(self, ctx: AmberPTParser.Angle_equil_value_statementContext):  # pylint: disable=unused-argument
        self.angleEquilValueStatements += 1

    # Exit a parse tree produced by AmberPTParser#angle_equil_value_statement.
    def exitAngle_equil_value_statement(self, ctx: AmberPTParser.Angle_equil_value_statementContext):
        if ctx.Real(0):
            return
        self.angleEquilValueStatements -= 1

    # Enter a parse tree produced by AmberPTParser#angle_force_constant_statement.
    def enterAngle_force_constant_statement(self, ctx: AmberPTParser.Angle_force_constant_statementContext):  # pylint: disable=unused-argument
        self.angleForceConstantStatements += 1

    # Exit a parse tree produced by AmberPTParser#angle_force_constant_statement.
    def exitAngle_force_constant_statement(self, ctx: AmberPTParser.Angle_force_constant_statementContext):
        if ctx.Real(0):
            return
        self.angleForceConstantStatements -= 1

    # Enter a parse tree produced by AmberPTParser#angles_inc_hydrogen_statement.
    def enterAngles_inc_hydrogen_statement(self, ctx: AmberPTParser.Angles_inc_hydrogen_statementContext):  # pylint: disable=unused-argument
        self.anglesIncHydrogenStatements += 1

    # Exit a parse tree produced by AmberPTParser#angles_inc_hydrogen_statement.
    def exitAngles_inc_hydrogen_statement(self, ctx: AmberPTParser.Angles_inc_hydrogen_statementContext):
        if ctx.Integer(0):
            return
        self.anglesIncHydrogenStatements -= 1

    # Enter a parse tree produced by AmberPTParser#angles_without_hydrogen_statement.
    def enterAngles_without_hydrogen_statement(self, ctx: AmberPTParser.Angles_without_hydrogen_statementContext):  # pylint: disable=unused-argument
        self.anglesWithoutHydrogenStatements += 1

    # Exit a parse tree produced by AmberPTParser#angles_without_hydrogen_statement.
    def exitAngles_without_hydrogen_statement(self, ctx: AmberPTParser.Angles_without_hydrogen_statementContext):
        if ctx.Integer(0):
            return
        self.anglesWithoutHydrogenStatements -= 1

    # Enter a parse tree produced by AmberPTParser#atomic_number_statement.
    def enterAtomic_number_statement(self, ctx: AmberPTParser.Atomic_number_statementContext):  # pylint: disable=unused-argument
        self.atomicNumberStatements += 1

    # Exit a parse tree produced by AmberPTParser#atomic_number_statement.
    def exitAtomic_number_statement(self, ctx: AmberPTParser.Atomic_number_statementContext):
        if ctx.Integer(0):
            return
        self.atomicNumberStatements -= 1

    # Enter a parse tree produced by AmberPTParser#atom_name_statement.
    def enterAtom_name_statement(self, ctx: AmberPTParser.Atom_name_statementContext):  # pylint: disable=unused-argument
        self.atomNameStatements += 1

    # Exit a parse tree produced by AmberPTParser#atom_name_statement.
    def exitAtom_name_statement(self, ctx: AmberPTParser.Atom_name_statementContext):
        if ctx.Simple_name(0):
            atomIdList = []
            i = 0
            while ctx.Simple_name(i):
                chunk = chunk_string(str(ctx.Simple_name(i)).upper(), self.__cur_word_len)
                atomIdList.extend(chunk)
                i += 1
            self.__atomName = atomIdList
            return
        self.atomNameStatements -= 1

    # Enter a parse tree produced by AmberPTParser#atom_type_index_statement.
    def enterAtom_type_index_statement(self, ctx: AmberPTParser.Atom_type_index_statementContext):  # pylint: disable=unused-argument
        self.atomTypeIndexStatements += 1

    # Exit a parse tree produced by AmberPTParser#atom_type_index_statement.
    def exitAtom_type_index_statement(self, ctx: AmberPTParser.Atom_type_index_statementContext):
        if ctx.Integer(0):
            return
        self.atomTypeIndexStatements += 1

    # Enter a parse tree produced by AmberPTParser#atoms_per_molecule_statement.
    def enterAtoms_per_molecule_statement(self, ctx: AmberPTParser.Atoms_per_molecule_statementContext):  # pylint: disable=unused-argument
        self.atomsPerMoleculeStatements += 1

    # Exit a parse tree produced by AmberPTParser#atoms_per_molecule_statement.
    def exitAtoms_per_molecule_statement(self, ctx: AmberPTParser.Atoms_per_molecule_statementContext):
        if ctx.Integer(0):
            return
        self.atomsPerMoleculeStatements -= 1

    # Enter a parse tree produced by AmberPTParser#bond_equil_value_statement.
    def enterBond_equil_value_statement(self, ctx: AmberPTParser.Bond_equil_value_statementContext):  # pylint: disable=unused-argument
        self.bondEquilValueStatements += 1

    # Exit a parse tree produced by AmberPTParser#bond_equil_value_statement.
    def exitBond_equil_value_statement(self, ctx: AmberPTParser.Bond_equil_value_statementContext):
        if ctx.Real(0):
            return
        self.bondEquilValueStatements -= 1

    # Enter a parse tree produced by AmberPTParser#bond_force_constant_statement.
    def enterBond_force_constant_statement(self, ctx: AmberPTParser.Bond_force_constant_statementContext):  # pylint: disable=unused-argument
        self.bondForceConstantStatements += 1

    # Exit a parse tree produced by AmberPTParser#bond_force_constant_statement.
    def exitBond_force_constant_statement(self, ctx: AmberPTParser.Bond_force_constant_statementContext):
        if ctx.Real(0):
            return
        self.bondForceConstantStatements -= 1

    # Enter a parse tree produced by AmberPTParser#bonds_inc_hydrogen_statement.
    def enterBonds_inc_hydrogen_statement(self, ctx: AmberPTParser.Bonds_inc_hydrogen_statementContext):  # pylint: disable=unused-argument
        self.bondsIncHydrogenStatements += 1

    # Exit a parse tree produced by AmberPTParser#bonds_inc_hydrogen_statement.
    def exitBonds_inc_hydrogen_statement(self, ctx: AmberPTParser.Bonds_inc_hydrogen_statementContext):
        if ctx.Integer(0):
            return
        self.bondsIncHydrogenStatements -= 1

    # Enter a parse tree produced by AmberPTParser#bonds_without_hydrogen_statement.
    def enterBonds_without_hydrogen_statement(self, ctx: AmberPTParser.Bonds_without_hydrogen_statementContext):  # pylint: disable=unused-argument
        self.bondsWithoutHydrogenStatements += 1

    # Exit a parse tree produced by AmberPTParser#bonds_without_hydrogen_statement.
    def exitBonds_without_hydrogen_statement(self, ctx: AmberPTParser.Bonds_without_hydrogen_statementContext):
        if ctx.Integer(0):
            return
        self.bondsWithoutHydrogenStatements -= 1

    # Enter a parse tree produced by AmberPTParser#box_dimensions_statement.
    def enterBox_dimensions_statement(self, ctx: AmberPTParser.Box_dimensions_statementContext):  # pylint: disable=unused-argument
        self.boxDimensionsStatements += 1

    # Exit a parse tree produced by AmberPTParser#box_dimensions_statement.
    def exitBox_dimensions_statement(self, ctx: AmberPTParser.Box_dimensions_statementContext):
        if ctx.Real(0):
            return
        self.boxDimensionsStatements -= 1

    # Enter a parse tree produced by AmberPTParser#cap_info_statement.
    def enterCap_info_statement(self, ctx: AmberPTParser.Cap_info_statementContext):  # pylint: disable=unused-argument
        self.capInfoStatements += 1

    # Exit a parse tree produced by AmberPTParser#cap_info_statement.
    def exitCap_info_statement(self, ctx: AmberPTParser.Cap_info_statementContext):
        if ctx.Integer(0):
            return
        self.capInfoStatements -= 1

    # Enter a parse tree produced by AmberPTParser#cap_info2_statement.
    def enterCap_info2_statement(self, ctx: AmberPTParser.Cap_info2_statementContext):  # pylint: disable=unused-argument
        self.capInfo2Statements += 1

    # Exit a parse tree produced by AmberPTParser#cap_info2_statement.
    def exitCap_info2_statement(self, ctx: AmberPTParser.Cap_info2_statementContext):
        if ctx.Real(0):
            return
        self.capInfo2Statements -= 1

    # Enter a parse tree produced by AmberPTParser#charge_statement.
    def enterCharge_statement(self, ctx: AmberPTParser.Charge_statementContext):  # pylint: disable=unused-argument
        self.chargeStatements += 1

    # Exit a parse tree produced by AmberPTParser#charge_statement.
    def exitCharge_statement(self, ctx: AmberPTParser.Charge_statementContext):
        if ctx.Real(0):
            return
        self.chargeStatements -= 1

    # Enter a parse tree produced by AmberPTParser#dihedral_force_constant_statement.
    def enterDihedral_force_constant_statement(self, ctx: AmberPTParser.Dihedral_force_constant_statementContext):  # pylint: disable=unused-argument
        self.dihedralForceConstantStatements += 1

    # Exit a parse tree produced by AmberPTParser#dihedral_force_constant_statement.
    def exitDihedral_force_constant_statement(self, ctx: AmberPTParser.Dihedral_force_constant_statementContext):
        if ctx.Real(0):
            return
        self.dihedralForceConstantStatements -= 1

    # Enter a parse tree produced by AmberPTParser#dihedral_periodicity_statement.
    def enterDihedral_periodicity_statement(self, ctx: AmberPTParser.Dihedral_periodicity_statementContext):  # pylint: disable=unused-argument
        self.dihedralPeriodicityStatements += 1

    # Exit a parse tree produced by AmberPTParser#dihedral_periodicity_statement.
    def exitDihedral_periodicity_statement(self, ctx: AmberPTParser.Dihedral_periodicity_statementContext):
        if ctx.Real(0):
            return
        self.dihedralPeriodicityStatements -= 1

    # Enter a parse tree produced by AmberPTParser#dihedral_phase_statement.
    def enterDihedral_phase_statement(self, ctx: AmberPTParser.Dihedral_phase_statementContext):  # pylint: disable=unused-argument
        self.dihedralPhaseStatements += 1

    # Exit a parse tree produced by AmberPTParser#dihedral_phase_statement.
    def exitDihedral_phase_statement(self, ctx: AmberPTParser.Dihedral_phase_statementContext):
        if ctx.Real(0):
            return
        self.dihedralPhaseStatements -= 1

    # Enter a parse tree produced by AmberPTParser#dihedrals_inc_hydrogen_statement.
    def enterDihedrals_inc_hydrogen_statement(self, ctx: AmberPTParser.Dihedrals_inc_hydrogen_statementContext):  # pylint: disable=unused-argument
        self.dihedralsIncHydrogenStatements += 1

    # Exit a parse tree produced by AmberPTParser#dihedrals_inc_hydrogen_statement.
    def exitDihedrals_inc_hydrogen_statement(self, ctx: AmberPTParser.Dihedrals_inc_hydrogen_statementContext):
        if ctx.Integer(0):
            return
        self.dihedralsIncHydrogenStatements -= 1

    # Enter a parse tree produced by AmberPTParser#dihedrals_without_hydrogen_statement.
    def enterDihedrals_without_hydrogen_statement(self, ctx: AmberPTParser.Dihedrals_without_hydrogen_statementContext):  # pylint: disable=unused-argument
        self.dihedralsWithoutHydrogenStatements += 1

    # Exit a parse tree produced by AmberPTParser#dihedrals_without_hydrogen_statement.
    def exitDihedrals_without_hydrogen_statement(self, ctx: AmberPTParser.Dihedrals_without_hydrogen_statementContext):
        if ctx.Integer(0):
            return
        self.dihedralsWithoutHydrogenStatements -= 1

    # Enter a parse tree produced by AmberPTParser#excluded_atoms_list_statement.
    def enterExcluded_atoms_list_statement(self, ctx: AmberPTParser.Excluded_atoms_list_statementContext):  # pylint: disable=unused-argument
        self.excludedAtomsListStatements += 1

    # Exit a parse tree produced by AmberPTParser#excluded_atoms_list_statement.
    def exitExcluded_atoms_list_statement(self, ctx: AmberPTParser.Excluded_atoms_list_statementContext):
        if ctx.Integer(0):
            return
        self.excludedAtomsListStatements -= 1

    # Enter a parse tree produced by AmberPTParser#hbcut_statement.
    def enterHbcut_statement(self, ctx: AmberPTParser.Hbcut_statementContext):  # pylint: disable=unused-argument
        self.hbcutStatements += 1

    # Exit a parse tree produced by AmberPTParser#hbcut_statement.
    def exitHbcut_statement(self, ctx: AmberPTParser.Hbcut_statementContext):
        if ctx.Real(0):
            return
        self.hbcutStatements -= 1

    # Enter a parse tree produced by AmberPTParser#hbond_acoef_statement.
    def enterHbond_acoef_statement(self, ctx: AmberPTParser.Hbond_acoef_statementContext):  # pylint: disable=unused-argument
        self.hbondAcoefStatements += 1

    # Exit a parse tree produced by AmberPTParser#hbond_acoef_statement.
    def exitHbond_acoef_statement(self, ctx: AmberPTParser.Hbond_acoef_statementContext):
        if ctx.Real(0):
            return
        self.hbondAcoefStatements -= 1

    # Enter a parse tree produced by AmberPTParser#hbond_bcoef_statement.
    def enterHbond_bcoef_statement(self, ctx: AmberPTParser.Hbond_bcoef_statementContext):  # pylint: disable=unused-argument
        self.hbondBcoefStatements += 1

    # Exit a parse tree produced by AmberPTParser#hbond_bcoef_statement.
    def exitHbond_bcoef_statement(self, ctx: AmberPTParser.Hbond_bcoef_statementContext):
        if ctx.Real(0):
            return
        self.hbondBcoefStatements -= 1

    # Enter a parse tree produced by AmberPTParser#ipol_statement.
    def enterIpol_statement(self, ctx: AmberPTParser.Ipol_statementContext):  # pylint: disable=unused-argument
        self.ipolStatements += 1

    # Exit a parse tree produced by AmberPTParser#ipol_statement.
    def exitIpol_statement(self, ctx: AmberPTParser.Ipol_statementContext):
        if ctx.Integer(0):
            return
        self.ipolStatements -= 1

    # Enter a parse tree produced by AmberPTParser#irotat_statement.
    def enterIrotat_statement(self, ctx: AmberPTParser.Irotat_statementContext):  # pylint: disable=unused-argument
        self.irotatStatements += 1

    # Exit a parse tree produced by AmberPTParser#irotat_statement.
    def exitIrotat_statement(self, ctx: AmberPTParser.Irotat_statementContext):
        if ctx.Integer(0):
            return
        self.irotatStatements -= 1

    # Enter a parse tree produced by AmberPTParser#join_array_statement.
    def enterJoin_array_statement(self, ctx: AmberPTParser.Join_array_statementContext):  # pylint: disable=unused-argument
        self.joinArrayStatements += 1

    # Exit a parse tree produced by AmberPTParser#join_array_statement.
    def exitJoin_array_statement(self, ctx: AmberPTParser.Join_array_statementContext):
        if ctx.Integer(0):
            return
        self.joinArrayStatements -= 1

    # Enter a parse tree produced by AmberPTParser#lennard_jones_acoef_statement.
    def enterLennard_jones_acoef_statement(self, ctx: AmberPTParser.Lennard_jones_acoef_statementContext):  # pylint: disable=unused-argument
        self.lennardJonesAcoefStatements += 1

    # Exit a parse tree produced by AmberPTParser#lennard_jones_acoef_statement.
    def exitLennard_jones_acoef_statement(self, ctx: AmberPTParser.Lennard_jones_acoef_statementContext):
        if ctx.Real(0):
            return
        self.lennardJonesAcoefStatements -= 1

    # Enter a parse tree produced by AmberPTParser#lennard_jones_bcoef_statement.
    def enterLennard_jones_bcoef_statement(self, ctx: AmberPTParser.Lennard_jones_bcoef_statementContext):  # pylint: disable=unused-argument
        self.lennardJonesBcoefStatements += 1

    # Exit a parse tree produced by AmberPTParser#lennard_jones_bcoef_statement.
    def exitLennard_jones_bcoef_statement(self, ctx: AmberPTParser.Lennard_jones_bcoef_statementContext):
        if ctx.Real(0):
            return
        self.lennardJonesBcoefStatements -= 1

    # Enter a parse tree produced by AmberPTParser#mass_statement.
    def enterMass_statement(self, ctx: AmberPTParser.Mass_statementContext):  # pylint: disable=unused-argument
        self.massStatements += 1

    # Exit a parse tree produced by AmberPTParser#mass_statement.
    def exitMass_statement(self, ctx: AmberPTParser.Mass_statementContext):
        if ctx.Real(0):
            return
        self.massStatements -= 1

    # Enter a parse tree produced by AmberPTParser#nonbonded_parm_index_statement.
    def enterNonbonded_parm_index_statement(self, ctx: AmberPTParser.Nonbonded_parm_index_statementContext):  # pylint: disable=unused-argument
        self.nonbondedParmIndexStatements += 1

    # Exit a parse tree produced by AmberPTParser#nonbonded_parm_index_statement.
    def exitNonbonded_parm_index_statement(self, ctx: AmberPTParser.Nonbonded_parm_index_statementContext):
        if ctx.Integer(0):
            return
        self.nonbondedParmIndexStatements -= 1

    # Enter a parse tree produced by AmberPTParser#number_excluded_atoms_statement.
    def enterNumber_excluded_atoms_statement(self, ctx: AmberPTParser.Number_excluded_atoms_statementContext):  # pylint: disable=unused-argument
        self.numberExcludedAtomsStatements += 1

    # Exit a parse tree produced by AmberPTParser#number_excluded_atoms_statement.
    def exitNumber_excluded_atoms_statement(self, ctx: AmberPTParser.Number_excluded_atoms_statementContext):
        if ctx.Integer(0):
            return
        self.numberExcludedAtomsStatements -= 1

    # Enter a parse tree produced by AmberPTParser#pointers_statement.
    def enterPointers_statement(self, ctx: AmberPTParser.Pointers_statementContext):  # pylint: disable=unused-argument
        self.pointersStatements += 1

    # Exit a parse tree produced by AmberPTParser#pointers_statement.
    def exitPointers_statement(self, ctx: AmberPTParser.Pointers_statementContext):
        if ctx.Integer(0):
            return
        self.pointersStatements -= 1

    # Enter a parse tree produced by AmberPTParser#polarizability_statement.
    def enterPolarizability_statement(self, ctx: AmberPTParser.Polarizability_statementContext):  # pylint: disable=unused-argument
        self.polarizabilityStatements += 1

    # Exit a parse tree produced by AmberPTParser#polarizability_statement.
    def exitPolarizability_statement(self, ctx: AmberPTParser.Polarizability_statementContext):
        if ctx.Real(0):
            return
        self.polarizabilityStatements -= 1

    # Enter a parse tree produced by AmberPTParser#radii_statement.
    def enterRadii_statement(self, ctx: AmberPTParser.Radii_statementContext):  # pylint: disable=unused-argument
        self.radiiStatements += 1

    # Exit a parse tree produced by AmberPTParser#radii_statement.
    def exitRadii_statement(self, ctx: AmberPTParser.Radii_statementContext):
        if ctx.Real(0):
            return
        self.radiiStatements -= 1

    # Enter a parse tree produced by AmberPTParser#radius_set_statement.
    def enterRadius_set_statement(self, ctx: AmberPTParser.Radius_set_statementContext):  # pylint: disable=unused-argument
        self.radiusSetStatements += 1

    # Exit a parse tree produced by AmberPTParser#radius_set_statement.
    def exitRadius_set_statement(self, ctx: AmberPTParser.Radius_set_statementContext):
        if ctx.Simple_name(0):
            radiusSet = []
            i = 0
            while ctx.Simple_name(i):
                radiusSet.append(str(ctx.Simple_name(i)))
                i += 1

            self.__radiusSet = ' '.join(radiusSet)
            return
        self.radiusSetStatements -= 1

    # Enter a parse tree produced by AmberPTParser#residue_label_statement.
    def enterResidue_label_statement(self, ctx: AmberPTParser.Residue_label_statementContext):  # pylint: disable=unused-argument
        self.residueLabelStatements += 1

        self.__residueLabel = []

    # Exit a parse tree produced by AmberPTParser#residue_label_statement.
    def exitResidue_label_statement(self, ctx: AmberPTParser.Residue_label_statementContext):
        if ctx.Simple_name(0):
            i = 0
            while ctx.Simple_name(i):
                chunk = chunk_string(str(ctx.Simple_name(i)).upper(), self.__cur_word_len)
                self.__residueLabel.extend(chunk)
                i += 1
            return
        self.residueLabelStatements -= 1

    # Enter a parse tree produced by AmberPTParser#residue_pointer_statement.
    def enterResidue_pointer_statement(self, ctx: AmberPTParser.Residue_pointer_statementContext):  # pylint: disable=unused-argument
        self.residuePointerStatements += 1

        self.__residuePointer = []

    # Exit a parse tree produced by AmberPTParser#residue_pointer_statement.
    def exitResidue_pointer_statement(self, ctx: AmberPTParser.Residue_pointer_statementContext):
        if ctx.Integer(0):
            i = 0
            while ctx.Integer(i):
                self.__residuePointer.append(int(str(ctx.Integer(i))))
                i += 1
            return
        self.residueLabelStatements -= 1

    # Enter a parse tree produced by AmberPTParser#scee_scale_factor_statement.
    def enterScee_scale_factor_statement(self, ctx: AmberPTParser.Scee_scale_factor_statementContext):  # pylint: disable=unused-argument
        self.sceeScaleFactorStatements += 1

    # Exit a parse tree produced by AmberPTParser#scee_scale_factor_statement.
    def exitScee_scale_factor_statement(self, ctx: AmberPTParser.Scee_scale_factor_statementContext):
        if ctx.Real(0):
            return
        self.sceeScaleFactorStatements -= 1

    # Enter a parse tree produced by AmberPTParser#scnb_scale_factor_statement.
    def enterScnb_scale_factor_statement(self, ctx: AmberPTParser.Scnb_scale_factor_statementContext):  # pylint: disable=unused-argument
        self.scnbScaleFactorStatements += 1

    # Exit a parse tree produced by AmberPTParser#scnb_scale_factor_statement.
    def exitScnb_scale_factor_statement(self, ctx: AmberPTParser.Scnb_scale_factor_statementContext):
        if ctx.Real(0):
            return
        self.scnbScaleFactorStatements -= 1

    # Enter a parse tree produced by AmberPTParser#screen_statement.
    def enterScreen_statement(self, ctx: AmberPTParser.Screen_statementContext):  # pylint: disable=unused-argument
        self.screenStatements += 1

    # Exit a parse tree produced by AmberPTParser#screen_statement.
    def exitScreen_statement(self, ctx: AmberPTParser.Screen_statementContext):
        if ctx.Real(0):
            return
        self.screenStatements -= 1

    # Enter a parse tree produced by AmberPTParser#solty_statement.
    def enterSolty_statement(self, ctx: AmberPTParser.Solty_statementContext):  # pylint: disable=unused-argument
        self.soltyStatements += 1

    # Exit a parse tree produced by AmberPTParser#solty_statement.
    def exitSolty_statement(self, ctx: AmberPTParser.Solty_statementContext):
        if ctx.Real(0):
            return
        self.soltyStatements -= 1

    # Enter a parse tree produced by AmberPTParser#solvent_pointers_statement.
    def enterSolvent_pointers_statement(self, ctx: AmberPTParser.Solvent_pointers_statementContext):  # pylint: disable=unused-argument
        self.solventPointersStatements += 1

    # Exit a parse tree produced by AmberPTParser#solvent_pointers_statement.
    def exitSolvent_pointers_statement(self, ctx: AmberPTParser.Solvent_pointers_statementContext):
        if ctx.Integer(0):
            return
        self.solventPointersStatements -= 1

    # Enter a parse tree produced by AmberPTParser#title_statement.
    def enterTitle_statement(self, ctx: AmberPTParser.Title_statementContext):  # pylint: disable=unused-argument
        self.titleStatements += 1

    # Exit a parse tree produced by AmberPTParser#title_statement.
    def exitTitle_statement(self, ctx: AmberPTParser.Title_statementContext):
        if ctx.Simple_name(0):
            title = []
            i = 0
            while ctx.Simple_name(i):
                title.append(str(ctx.Simple_name(i)))
                i += 1

            self.__title = ' '.join(title)
            return
        self.titleStatements -= 1

    # Enter a parse tree produced by AmberPTParser#tree_chain_classification_statement.
    def enterTree_chain_classification_statement(self, ctx: AmberPTParser.Tree_chain_classification_statementContext):  # pylint: disable=unused-argument
        self.treeChainClassificationStatements += 1

    # Exit a parse tree produced by AmberPTParser#tree_chain_classification_statement.
    def exitTree_chain_classification_statement(self, ctx: AmberPTParser.Tree_chain_classification_statementContext):
        if ctx.Simple_name(0):
            return
        self.treeChainClassificationStatements -= 1

    # Enter a parse tree produced by AmberPTParser#format_function.
    def enterFormat_function(self, ctx: AmberPTParser.Format_functionContext):
        try:
            if ctx.Fortran_format_A():
                g = self.__a_format_pat.search(str(ctx.Fortran_format_A())).groups()
                # self.__cur_column_len = int(g[0])
                self.__cur_word_len = int(g[1])
            elif ctx.Fortran_format_I():
                g = self.__i_format_pat.search(str(ctx.Fortran_format_I())).groups()
                # self.__cur_column_len = int(g[0])
                self.__cur_word_len = int(g[1])
            else:
                g = self.__e_format_pat.search(str(ctx.Fortran_format_E())).groups()
                # self.__cur_column_len = int(g[0])
                self.__cur_word_len = int(g[1])
        except AttributeError:
            # self.__cur_column_len = None
            self.__cur_word_len = None

    # Exit a parse tree produced by AmberPTParser#format_function.
    def exitFormat_function(self, ctx: AmberPTParser.Format_functionContext):  # pylint: disable=unused-argument
        pass

    def getContentSubtype(self):
        """ Return content subtype of AMBER parameter/topology file.
        """

        contentSubtype = {'version': self.versionStatements,
                          'amber_atom_type': self.amberAtomTypeStatements,
                          'angle_equil_value': self.angleEquilValueStatements,
                          'angle_force_constant': self.angleForceConstantStatements,
                          'angles_inc_hydrogen': self.anglesIncHydrogenStatements,
                          'angles_without_hydrogen': self.anglesWithoutHydrogenStatements,
                          'atomic_number': self.atomicNumberStatements,
                          'atom_name': self.atomNameStatements,
                          'atom_type_index': self.atomTypeIndexStatements,
                          'atoms_per_molecule': self.atomsPerMoleculeStatements,
                          'bond_equil_value': self.bondEquilValueStatements,
                          'bond_force_constant': self.bondForceConstantStatements,
                          'bonds_inc_hydrogen': self.bondsIncHydrogenStatements,
                          'bonds_without_hydrogen': self.bondsWithoutHydrogenStatements,
                          'box_dimensions': self.boxDimensionsStatements,
                          'cap_info': self.capInfoStatements,
                          'cap_info2': self.capInfo2Statements,
                          'charge': self.chargeStatements,
                          'dihedral_force_constant': self.dihedralForceConstantStatements,
                          'dihedral_periodicity': self.dihedralPeriodicityStatements,
                          'dihedral_phase': self.dihedralPhaseStatements,
                          'dihedrals_inc_hydrogen': self.dihedralsIncHydrogenStatements,
                          'dihedrals_without_hydrogen': self.dihedralsWithoutHydrogenStatements,
                          'excluded_atoms_list': self.excludedAtomsListStatements,
                          'hbcut': self.hbcutStatements,
                          'hbond_acoef': self.hbondAcoefStatements,
                          'hbond_bcoef': self.hbondBcoefStatements,
                          'ipol': self.ipolStatements,
                          'irotat': self.irotatStatements,
                          'join_array': self.joinArrayStatements,
                          'lennard_jones_acoef': self.lennardJonesAcoefStatements,
                          'lennard_jones_bcoef': self.lennardJonesBcoefStatements,
                          'mass': self.massStatements,
                          'nonbonded_parm_index': self.nonbondedParmIndexStatements,
                          'number_excluded_atoms': self.numberExcludedAtomsStatements,
                          'pointers': self.pointersStatements,
                          'polarizability': self.polarizabilityStatements,
                          'radii': self.radiiStatements,
                          'radius_set': self.radiusSetStatements,
                          'residue_label': self.residueLabelStatements,
                          'residue_pointer': self.residuePointerStatements,
                          'scee_scale_factor': self.sceeScaleFactorStatements,
                          'scnb_scale_factor': self.scnbScaleFactorStatements,
                          'screen': self.screenStatements,
                          'solty': self.soltyStatements,
                          'solvent_pointers': self.solventPointersStatements,
                          'title': self.titleStatements,
                          'tree_chain_classification': self.treeChainClassificationStatements
                          }

        return {k: 1 for k, v in contentSubtype.items() if v > 0}

    def getVersionInfo(self):
        """ Return version information of AMBER parameter/topology file.
            @return: version, date, time
        """
        return self.__version, self.__date, self.__time

    def getTitle(self):
        """ Return title of AMBER parameter/topology file.
        """
        return self.__title

    def getRadiusSet(self):
        """ Return radius set of AMBER parameter/topology file.
        """
        return self.__radiusSet

    def getAtomNumberDict(self):
        """ Return AMBER atomic number dictionary.
        """
        return self.__atomNumberDict

    def getPolymerSequence(self):
        """ Return polymer sequence of AMBER parameter/topology file.
        """
        return None if self.__polySeqPrmTop is None or len(self.__polySeqPrmTop) == 0 else self.__polySeqPrmTop

    def getSequenceAlignment(self):
        """ Return sequence alignment between coordinates and AMBER parameter/topology.
        """
        return None if self.__seqAlign is None or len(self.__seqAlign) == 0 else self.__seqAlign

    def getChainAssignment(self):
        """ Return chain assignment between coordinates and AMBER parameter/topology.
        """
        return None if self.__chainAssign is None or len(self.__chainAssign) == 0 else self.__chainAssign

# del AmberPTParser
