class TurbomoleEnum:

    # general
    # ---------
    COORD = "coord"  # hard-coded file name of input coordinates
    CONTROL = "control"  # hard-coded file name for control script
    TM_CONFIG_DIR = "tm_config_dir"  # directory path where the *.tm configurations lie
    # the basename of the parameter set chosen, e.g. "b97-3c-ri-d3-def2-mtzvp-int-nosym-charge"
    TM_CONFIG_BASENAME = "tm_config_basename"
    # this does not contain the charge or the ending
    # full name e.g. "b97-3c-ri-d3-def2-mtzvp-int-nosym-charge-1.tm"
    TM_CONFIG_ENDING = ".tm"  # ending of turbomole configuration files
    TM_CONFIG_COSMO = "tm_config_cosmo"  # path to the COSMO configuration file
    # this needs to be set for each turbomole calculation (current folder)
    TM_TURBOTMPDIR = "TURBOTMPDIR"
    TM_OUTPUT_COSMOFILE = "mol.cosmo"  # hard-coded file name of the turbomole output
    TM_OUTPUT_COORDFILE = "coord"
    TM_OUTPUT_FINAL_XYZ = "final.xyz"  # hard-coded file name for turbomole output
    TM_OUTPUT_FINAL_SDF = "final.sdf"

    # the "ridft" binary (+ configuration elements)
    # ---------
    TM_RIDFT = "ridft"  # do DFT calculation with RI-J approximation for
    # inter-electronic Coulomb term
    TM_RIDFT_FAIL_IDENTIFICATION_STRING = "ridft ended abnormally"
    TM_RIDFT_SUCCESS_STRING = (
        "ridft ended normally"  # if this string is in stderr, execution was successful
    )

    # the "jobex" binary (+ configuration elements)
    # ---------
    TM_JOBEX = "jobex"  # used for DFT optimization
    TM_JOBEX_C = "-c"
    TM_JOBEX_GCART = "-gcart"
    TM_JOBEX_FAIL_IDENTIFICATION_STRING = "jobex ended abnormally"
    TM_JOBEX_SUCCESS_STRING = "jobex ended normally"

    # the "cosmoprep" binary (+ configuration elements)
    # ---------
    TM_COSMOPREP = "cosmoprep"
    TM_COSMOPREP_SUCCESS_STRING = "cosmoprep ended normally"

    # the "define" binary (+ configuration elements)
    # ---------
    TM_DEFINE = "define"
    TM_DEFINE_SUCCESS_STRING = (
        "define ended normally"  # if this string is in stderr, execution was successful
    )

    # the "x2t" binary (+ configuration elements)
    # ---------
    TM_X2T = (
        "x2t"  # program to translate an XYZ file to TM input: x2t input.xyz > coord
    )
    TM_X2T_SUCCESS_STRING = (
        "$coord"  # if this string is in stdout, execution was successful
    )

    # the "t2x" binary (+ configuration elements)
    # ---------
    # program to translate an TM input to an XYZ file (last snapshot): t2x -c > final.xyz
    TM_T2X = "t2x"
    TM_T2X_C = "-c"

    # the "cosmotherm" binary (+ configuration elements)
    # ---------
    CT_COSMOTHERM = "cosmotherm"  # the cosmotherm binary
    CT_COSMOTHERM_FAIL_STRING = (
        "COSMOtherm ERROR Termination"  # if this string is in stderr, the job failed
    )
    CT_COSMOTHERM_CONFIG_FILE = (
        "cosmotherm.inp"  # hard-coded name of the input file generated before execution
    )
    CT_COSMOTHERM_OUTPUT_FILE = (
        "cosmotherm.out"  # hard-coded name of the output file generated by cosmotherm
    )
    CT_COSMOTHERM_TAB_ENDING = "cosmother.tab"

    CT_CONFIG = "cosmotherm_config"
    CT_CONFIG_DEFAULTPATH = "src/icolos/config/cosmo/default_cosmo.config"

    # control script fields
    # ---------
    CONTROL_COSMO_OUT = (
        # line, after which insertion is to be put (separate line)
        "$cosmo_out file=n"
    )
    CONTROL_COSMO_INSERTION = "$cosmo_isorad"
    CONTROL_COSMO_REPLACE = (
        "$cosmo_out file=mol.cosmo"  # after insertion, replace "$cosmo_out" with this
    )

    # try to find the internal value and return
    def __getattr__(self, name):
        if name in self:
            return name
        raise AttributeError

    # prohibit any attempt to set any values
    def __setattr__(self, key, value):
        raise ValueError("No changes allowed.")


class CosmoOutputEnum:

    PATTERN = "pattern"
    ELEMENT = "element"

    # general block
    # ---------
    # this works, because we always set the name of the compound to "mol"
    GENERAL_BLOCK_PATTERN_STRING = "--- Compound 1 (mol) ---"
    # alternatively, one could search for "Compound 1 ", as we also feed it in in order

    # the key (e.g. "E_cosmo") is the tag name for the SDF write-out, "pattern" identifies the line and
    # "element" is the number (on the right side, after the ':') of the element (starting with 0) from a
    # split using ' ', respectively
    GENERAL_BLOCK_ANNOTATIONS = {
        "E_cosmo": {"pattern": "E_COSMO+dE", "element": 0},
        "volume": {"pattern": "Volume", "element": 0},
        "area": {"pattern": "Area", "element": 0},
        "dipole": {"pattern": "Dipole moment", "element": 0},
        "HB_acc": {"pattern": "H-bond moment (accept)", "element": 0},
        "HB_don": {"pattern": "H-bond moment (donor)", "element": 0},
        "sigma1": {"pattern": "Sigma moments", "element": 0},
        "sigma2": {"pattern": "Sigma moments", "element": 1},
        "sigma3": {"pattern": "Sigma moments", "element": 2},
        "sigma4": {"pattern": "Sigma moments", "element": 3},
        "sigma5": {"pattern": "Sigma moments", "element": 4},
        "sigma6": {"pattern": "Sigma moments", "element": 5},
    }

    # solvent blocks
    # ---------
    SOLVENT_BLOCK_PATTERN_STRING = "Gibbs Free Energy of Solvation"
    SOLVENT_BLOCK_START_PATTERN = "----------------------"
    SOLVENT_BLOCK_BODY_START_PATTERN = "Compound: 1  (mol)"
    SOLVENT_TRANSLATE_SOLVENT = {
        "h2o": "h2o",
        "methanol": "meoh",
        "1-octanol": "octanol",
        "dimethyls": "dmso",
        "cyclohexa": "cychex",
        "chcl3": "chcl3",
        "acetonitr": "acn",
        "thf": "thf",
    }
    SOLVENT_REPLACEHOLDER = "{solvent}"
    SOLVENT_BLOCK_BODY_ANNOTATIONS = {
        "Gsolv_{solvent}": {"pattern": "Gibbs Free Energy of Solvation", "element": 0},
        "G_{solvent}": {"pattern": "Free energy of molecule in mix", "element": 0},
    }
    SOLVENT_BLOCK_HEADER_COMPOUNDS_PATTERN = "Compound   "
    SOLVENT_BLOCK_HEADER_MOLFRACTION_PATTERN = "Mole Fraction"
    SOLVENT_BLOCK_CURRENT_FRACTION_VALUE = "1.0000"

    # try to find the internal value and return
    def __getattr__(self, name):
        if name in self:
            return name
        raise AttributeError

    # prohibit any attempt to set any values
    def __setattr__(self, key, value):
        raise ValueError("No changes allowed.")


class FeatureCounterEnum:

    PROPERTY_NUM_RINGS = "num_rings"
    PROPERTY_NUM_AROMATIC_RINGS = "num_aromatic_rings"

    # try to find the internal value and return
    def __getattr__(self, name):
        if name in self:
            return name
        raise AttributeError

    # prohibit any attempt to set any values
    def __setattr__(self, key, value):
        raise ValueError("No changes allowed.")


class CrestEnum:

    # Note: The first argument is usually a coordinate file in TM (coord, Bohr),
    #       Xmol (*.xyz, Ang.) or SDF format.
    # Call: "crest <input_file> <parameters>"
    # This collection is based on Version 2.10.2, compatible with XTB version 6.1 and later

    # General options (all)
    # ---------
    CREST = "crest"  # binary name
    CREST_H = "-h"  # print the help message
    CREST_HELP_IDENTIFICATION_STRING = "Conformer-Rotamer Ensemble Sampling Tool"
    CREST_V3 = "-v3"  # version 3 (the default iMTD-GC workflows)
    CREST_G = "-g"  # 1 string parameter; use GBSA implicit solvent for solvent <string>
    CREST_CHRG = "-chrg"  # 1 int parameter; the molecule's charge
    CREST_UHF = "-uhf"  # 1 int parameter; set <int>=Nα-Nβ electrons
    CREST_NOZS = (
        "-nozs"  # do not perform z-mat sorting, default: z-matrix will be sorted
    )
    CREST_ZS = "-zs"  # perform z-matrix sorting [default]
    # 1 level parameter (vloose, loose, normal, tight, vtight); default: vtight
    CREST_OPT = "-opt"
    CREST_GFN1 = "-gfn1"  # use GFN1-xTB
    CREST_GFN2 = "-gfn2"  # use GFN2-xTB [default]
    CREST_GFF = "-gff"  # use GFN-FF (requires xtb 6.3 or newer)
    # 1 string parameter; specify name of the xtb binary that should be used
    CREST_XNAM = "-xnam"
    # 1 float parameter; set energy window in kcl/mol, default: 6.0 kcal/mol
    CREST_EWIN = "-ewin"
    CREST_RTHR = (
        "-rthr"  # 1 float parameter; set RMSD threshold in Ang, default: 0.125 Ang
    )
    CREST_ETHR = (
        "-ethr"  # 1 float parameter; set E threshold in kcal/mol, default: 0.1 kcal/mol
    )
    CREST_BTHR = (
        "-bthr"  # 1 float parameter; set Rot. const. threshold, default: 15.0 MHz
    )
    # 1 float parameter; Boltzmann population threshold, default: 0.05 (= 5%)
    CREST_PTHR = "-pthr"
    CREST_EQV = "-eqv"  # activate NMR-equivalence printout
    CERST_NMR = "-nmr"  # activate NMR-mode (= [-eqv] + opt. level: vtight)
    CREST_PRSC = "-prsc"  # create a scoord.* file for each conformer
    CREST_NICEPRINT = "-niceprint"  # progress bar printout for optimizations
    CREST_DRY = "-dry"  # performs a "dry run"; only prints the settings

    # iMTD-GC workflows (selected)
    # ---------
    CREST_CROSS = "-cross"  # do the GC part [default]
    CREST_NOCROSS = "-nocross"  # don't do the GC part
    # 1 int parameter; set SHAKE mode for MD (0=off, 1=H-only, 2=all bonds), default: 2
    CREST_SHAKE = "-shake"
    CREST_TSTEP = "-tstep"  # 1 int parameter; set MD time step in fs, default: 5

    # other (selected)
    # ---------
    CREST_T = (
        # 1 int parameter; set total compound_number of CPUs (threads) to be used
        "-T"
    )

    # try to find the internal value and return
    def __getattr__(self, name):
        if name in self:
            return name
        raise AttributeError

    # prohibit any attempt to set any values
    def __setattr__(self, key, value):
        raise ValueError("No changes allowed.")


class AutoDockVinaEnum:
    # executable "vina" + parameters
    # ---------
    VINA = "vina"
    VINA_CALL = "vina"  # the binary call
    VINA_HELP = "--help"  # display usage summary
    VINA_HELP_ADVANCED = "--help_advanced"  # display usage summary (with all options)
    VINA_VERSION = "--version"  # diplay program version
    VINA_VERSION_IDENTIFICATION_STRING = (
        "AutoDock Vina 1.1.2"  # string, which needs to be present in help output in
    )
    # order to assume "AutoDock Vina" can be properly used
    VINA_CONFIGURATION = (
        "--config"  # path to configuration file, where options below can be put
    )

    # input
    VINA_RECEPTOR = "--receptor"  # rigid part of the receptor (PDBQT)
    VINA_LIGAND = "--ligand"  # ligand (PDBQT); only one at a time
    VINA_FLEX = "--flex"  # flexible side chains, if any (PDBQT)

    # search space
    VINA_CENTER_X = "--center_x"  # X coordinate of the center
    VINA_CENTER_Y = "--center_y"  # Y coordinate of the center
    VINA_CENTER_Z = "--center_z"  # Z coordinate of the center
    VINA_SIZE_X = "--size_x"  # size in the X dimension (Angstroms)
    VINA_SIZE_Y = "--size_y"  # size in the X dimension (Angstroms)
    VINA_SIZE_Z = "--size_z"  # size in the X dimension (Angstroms)

    # output
    VINA_OUT = "--out"  # output models (PDBQT), the default is chosen based on the
    # ligand file name

    # advanced options
    VINA_SCORE_ONLY = "--score_only"  # score only - search space can be omitted
    VINA_LOCAL_ONLY = "--local_only"  # do local search only
    VINA_RANDOMIZE_ONLY = (
        "--randomize_only"  # randomize input, attempting to avoid clashes
    )
    VINA_WEIGHT_GAUSS1 = "--weight_gauss1"  # gauss_1 weight (default: -0.035579)
    VINA_WEIGHT_GAUSS2 = "--weight_gauss2"  # gauss_2 weight (default: -0.005156)
    VINA_WEIGHT_REPULSION = (
        "--weight_repulsion"  # repulsion weight (default: 0.84024500000000002)
    )
    VINA_WEIGHT_HYDROPHOBIC = (
        "--weight_hydrophobic"  # hydrophobic weight (-0.035069000000000003)
    )
    VINA_WEIGHT_HYDROGEN = (
        "--weight_hydrogen"  # hydrogen bond weight (-0.58743900000000004)
    )
    VINA_WEIGHT_ROT = "--weight_rot"  # N_rot weight (default: 0.058459999999999998)

    # miscellaneous (optional)
    VINA_CPU = "--cpu"  # the number of CPUs to use (the default is to try to detect
    # the number of CPUs or, failing that, use 1)
    VINA_SEED = "--seed"  # explicit random seed
    VINA_EXHAUSTIVENESS = (
        "--exhaustiveness"  # exhaustiveness of the global search (roughly proportional
    )
    # to time): 1+ (default: 8)
    VINA_NUM_MODES = (
        "--num_modes"  # maximum number of binding modes to generate (default: 9)
    )
    VINA_ENERGY_RANGE = "--energy_range"  # maximum energy difference between the best binding mode and the
    # worst one displayed [kcal/mol] (default: 3)

    # ---------
    # Vina output specifications
    # ---------
    ADV_PDBQT = ".pdbqt"

    # the score is part of a tag in the PDBQT -> SDF translated output (tag "REMARK"), which looks like that:
    # < REMARK >
    # VINA RESULT: -9.1 0.000 0.000
    # Name = /tmp/tmpjssiy8z4.pdb
    # ...

    # Note, that the three values are: affinity [kcal/mol] | dist from best mode (rmsd l.b.) | rmsd (u. b.)
    REMARK_TAG = "REMARK"
    RESULT_LINE_IDENTIFIER = "VINA RESULT"
    RESULT_LINE_POS_SCORE = 2
    RESULT_LINE_POS_RMSDTOBEST_LB = 3
    RESULT_LINE_POS_RMSDTOBEST_UB = 4

    # try to find the internal value and return
    def __getattr__(self, name):
        if name in self:
            return name
        raise AttributeError

    # prohibit any attempt to set any values
    def __setattr__(self, key, value):
        raise ValueError("No changes allowed.")


class GoldEnum:

    GOLD_CALL = "gold_auto"
    GOLD_HELP = "-h"
    GOLD_HELP_IDENTIFICATION_STRING = "Usage: gold_auto"
    GOLD_QUIET = "-q"

    REMARK_TAG = "REMARK"

    # try to find the internal value and return
    def __getattr__(self, name):
        if name in self:
            return name
        raise AttributeError

    # prohibit any attempt to set any values
    def __setattr__(self, key, value):
        raise ValueError("No changes allowed.")


class GoldOutputEnum:

    # try to find the internal value and return
    def __getattr__(self, name):
        if name in self:
            return name
        raise AttributeError

    # prohibit any attempt to set any values
    def __setattr__(self, key, value):
        raise ValueError("No changes allowed.")


class CrestOutputEnum:

    COORD = "coord"
    COORD_ORIGINAL = "coord.original"
    CRE_MEMBERS = "cre_members"
    CREST_ENERGIES = "crest.energies"
    CREST_BEST_XYZ = "crest_best.xyz"
    CREST_CONFORMERS_SDF = "crest_conformers.sdf"
    CREST_CONFORMERS_XYZ = "crest_conformers.xyz"
    CREST_ROTAMERS_XYZ = "crest_rotamers.xyz"
    XTBTOPO_MOL2 = "xtbtopo.mol"

    # format properties
    PREFIX_ENERGIES_XYZ = "        "

    # try to find the internal value and return
    def __getattr__(self, name):
        if name in self:
            return name
        raise AttributeError

    # prohibit any attempt to set any values
    def __setattr__(self, key, value):
        raise ValueError("No changes allowed.")


class OpenBabelEnum:

    # executable "obabel" + parameters
    # ---------
    OBABEL = "obabel"
    OBABEL_IDENTIFICATION_STRING = "-O<outfilename>"
    OBABEL_INPUTFORMAT_PDBQT = (
        # sets the input format to "PDBQT" (output of "AutoDock Vina")
        "-ipdbqt"
    )
    OBABEL_INPUTFORMAT_XYZ = (
        "-ixyz"  # sets the input format to "XYZ" (format in XTB/TM)
    )
    OBABEL_INPUTFORMAT_PDB = "-ipdb"
    OBABEL_INPUTFORMAT_SDF = "-isdf"  # sets the input format to "SDF"
    OBABEL_P = "-p"  # sets the <pH> value (e.g. "-p 7.4") for protonation
    # note, that this overwrites "--addpolarh", which is thus not used
    # specifies the output path (directly pasted afterwards, e.g. "-Omypath.pdb")
    OBABEL_O = "-O"
    # sets the output format to "PDBQT" (input for "AutoDock Vina")
    OBABEL_OUTPUT_FORMAT_PDBQT = "-opdbqt"
    OBABEL_OUTPUT_FORMAT_SDF = "-osdf"  # sets the output format to "SDF"
    # sets the output format to "XYZ" (format in XTB/TM)
    OBABEL_OUTPUT_FORMAT_XYZ = "-oxyz"
    OBABEL_OUTPUT_FORMAT_PDB = "-opdb"
    OBABEL_X = "-x"  # specifies generation options
    OBABEL_M = "-m"  # produce multiple output files
    # one of the 'X' options ("-x"), which disables the tree construction of the receptor
    # (makes it static), directly pasted together: e.g. "-xr"
    OBABEL_X_R = "r"
    # sets the partial charge generation method (execute "obabel -L charges" to see list of available methods)
    OBABEL_PARTIALCHARGE = "--partialcharge"
    # one method to compute the partial charges, used as: "--partialcharge gasteiger"
    OBABEL_PARTIALCHARGE_GASTEIGER = "gasteiger"

    # try to find the internal value and return
    def __getattr__(self, name):
        if name in self:
            return name
        raise AttributeError

    # prohibit any attempt to set any values
    def __setattr__(self, key, value):
        raise ValueError("No changes allowed.")


class OpenBabelOutputEnum:

    # try to find the internal value and return
    def __getattr__(self, name):
        if name in self:
            return name
        raise AttributeError

    # prohibit any attempt to set any values
    def __setattr__(self, key, value):
        raise ValueError("No changes allowed.")


class OMEGAEnum:

    # executable "oeomega" + parameters; the first parameter is a string indicating the mode
    # ---------
    OMEGA = "oeomega"
    OMEGA_HELP = "--help"  # print the help message
    OMEGA_HELP_IDENTIFICATION_STRING = "To cite OMEGA please"
    OMEGA_MODE_CLASSIC = "classic"  # The original customizable omega2 interface
    OMEGA_MODE_MACROCYCLE = "macrocycle"  # Conformer generation for macrocycles
    OMEGA_MODE_ROCS = "rocs"  # Optimal conformer generation for ROCS
    OMEGA_MODE_POSE = "pose"  # Optimal conformer generation for molecular
    # alignment and pose prediction by docking
    OMEGA_MODE_DENSE = "dense"  # Optimal conformer generation for FREEDOM

    # mode "classic" parameters
    # ---------
    CLASSIC_INPUT = "-in"  # Input filename (required, if "-param" not set)
    CLASSIC_OUTPUT = "-out"  # Output filename (required, if "-param" not set)
    CLASSIC_PARAM = "-param"  # A parameter file
    CLASSIC_PREFIX = "-prefix"  # Prefix to use to name output files
    CLASSIC_PROGRESS = "-progress"  # Method of showing job progress. Either "none",
    # "dots", "log" or "percent".
    CLASSIC_SDENERGY = "-sdEnergy"  # Writes conformer energies to the SD tag field
    CLASSIC_VERBOSE = "-verbose"  # Triggers copious logging output
    # Generate structures from connection-table only.
    CLASSIC_FROMCT = "-fromCT"
    CLASSIC_EWINDOW = "-ewindow"  # Energy window used for conformer selection.
    CLASSIC_MAXCONFS = (
        "-maxconfs"  # Maximum compound_number of conformations to be saved
    )
    CLASSIC_RMS = "-rms"  # RMS threshold used to determine duplicate
    # conformations
    # if set to false ("-canonOrder false"), OMEGA will not update the atom orders
    CLASSIC_CANON_ORDER = "-canonOrder"
    CLASSIC_STRICTSTEREO = (
        "-strictstereo"  # Requires that all chiral atoms and bonds have
    )
    # specified stereo
    CLASSIC_STRICT = "-strict"  # A convenience flag to set "-strictstereo",
    # "-strictatomtyping" and "-strictfrags" to true
    # or false and override [sic] previous settings.

    # mode "rocs" parameters
    # ---------
    ROCS_INPUT = "-in"  # Input filename (required, if "-param" not set)
    ROCS_OUTPUT = "-out"  # Output filename (required, if "-param" not set)
    ROCS_PARAM = "-param"  # A parameter file
    ROCS_PREFIX = "-prefix"  # Prefix to use to name output files
    ROCS_PROGRESS = "-progress"  # Method of showing job progress. Either "none",
    # "dots", "log" or "percent".
    ROCS_VERBOSE = "-verbose"  # Triggers copious logging output

    # mode "dense" parameters
    # ---------
    DENSE_INPUT = "-in"  # Input filename (required, if "-param" not set)
    DENSE_OUTPUT = "-out"  # Output filename (required, if "-param" not set)
    DENSE_PARAM = "-param"  # A parameter file
    DENSE_PREFIX = "-prefix"  # Prefix to use to name output files
    DENSE_PROGRESS = "-progress"  # Method of showing job progress. Either "none",
    # "dots", "log" or "percent".
    DENSE_VERBOSE = "-verbose"  # Triggers copious logging output

    # mode "macrocycle" parameters
    # ---------
    MACROCYCLE_INPUT = "-in"  # Input filename (required, if "-param" not set)
    # Output filename (required, if "-param" not set)
    MACROCYCLE_OUTPUT = "-out"
    MACROCYCLE_PARAM = "-param"  # A parameter file
    MACROCYCLE_PREFIX = "-prefix"  # Prefix to use to name output files
    MACROCYCLE_EWINDOW = "-ewindow"  # Energy window for the output conformers
    MACROCYCLE_ITERATION_CYCLE_SIZE = (
        "-iteration_cycle_size"  # Number of iterations to run before checking if a
    )
    # new minimum was found (run will finish if no new
    # minimum is found).
    MACROCYCLE_MAXCONFS = (
        "-maxconfs"  # Maximum compound_number of conformations to be saved
    )
    MACROCYCLE_MAX_ITERATIONS = (
        # Maximum compound_number of iterations (calculation may
        "-max_iterations"
    )
    # converge before reaching this compound_number).
    MACROCYCLE_REF_TOLERANCE = (
        "-ref_tolerance"  # RMS gradient tolerance for force field refinement
    )
    MACROCYCLE_RMS = "-rms"  # RMS clustering threshold (if 0.0 clustering is
    # skipped).
    MACROCYCLE_RMSD_DEDUPLICATE = (
        "-rmsd_deduplicate"  # Deduplicate using a RMSD calculation (slow)
    )
    # rather than energy and torsion comparison

    # mode "pose" parameters
    # ---------
    POSE_INPUT = "-in"  # Input filename (required, if "-param" not set)
    POSE_OUTPUT = "-out"  # Output filename (required, if "-param" not set)
    POSE_PREFIX = "-prefix"  # Prefix to use to name output files
    POSE_PROGRESS = "-progress"  # Method of showing job progress. Either "none",
    # "dots", "log" or "percent".
    POSE_VERBOSE = "-verbose"  # Triggers copious logging output

    # try to find the internal value and return
    def __getattr__(self, name):
        if name in self:
            return name
        raise AttributeError

    # prohibit any attempt to set any values
    def __setattr__(self, key, value):
        raise ValueError("No changes allowed.")


class OMEGAOutputEnum:

    # tags
    CLASSIC_ENERGY_OUTPUT_TAG = "mmff94smod_NoEstat"

    # other
    # This hard-coded output name will be parsed.
    OUTPUT_SDF_NAME = "omega_out.sdf"

    # try to find the internal value and return
    def __getattr__(self, name):
        if name in self:
            return name
        raise AttributeError

    # prohibit any attempt to set any values
    def __setattr__(self, key, value):
        raise ValueError("No changes allowed.")


class XTBEnum:

    # Usage: xtb[options] <geometry> [options]
    # < geometry > may be provided as valid TM coordinate file(*coord in Bohr) or in xmol format(*xyz in Ångström).
    # Output Conventions: total energies are given in atomic units (Eh), gaps/HL energies are given in eV
    XTB = "xtb"
    XTB_HELP = "--help"
    XTB_HELP_IDENTIFICATION_STRING = "normal termination of xtb"  # written to stderr
    XTB_CHRG = "--chrg"  # 1 int parameter; specify molecular charge
    XTB_UHF = "--uhf"  # 1 int parameter; specify Nalph-Nbeta
    # 1 float parameter; accuracy for SCC calculation, lower is better (default = 1.0)
    XTB_ACC = "--acc"
    # 1 int parameter; compound_number of iterations in SCC (default = 250)
    XTB_ITERATION = "--iteration"
    # 1 level parameter; compound_number of cycles in ANCopt (default = automatic)
    XTB_CYCLES = "--cycles"
    XTB_GFN = (
        # 1 int parameter; specify parametrisation of GFN-xTB (default = 2)
        "--gfn"
    )
    XTB_QMDFF = "--qmdff"  # use QMDFF for single point (needs solvent-file)
    XTB_TM = "--tm"  # use TURBOMOLE for single point (needs control-file)
    XTB_ORCA = "--orca"  # use ORCA for single point (writes ORCA input)
    XTB_MOPAC = "--mopac"  # use MOPAC for single point (writes MOPAC input)
    # uses periodic boundary conditions (in developement)
    XTB_PERIODIC = "--periodic"
    # 1 float parameter; electronic temperature (default = 300K)
    XTB_ETEMP = "--etemp"
    # 1 level parameter; generalized born (GB) model with solvent accessable surface area (SASA) model
    XTB_GBSA = "--gbsa"
    XTB_OPT = "--opt"  # 1 level parameter; either "crude", "sloppy",
    # "loose", "normal" (default), "tight", "verytight"
    XTB_P = "-P"  # 1 int parameter; compound_number of cores

    # --vparam FILE  Parameter file for vTB calculation
    # --xparam FILE  Parameter file for xTB calculation (not used)
    # --pop          requests printout of Mulliken population analysis
    # --molden       requests printout of molden file
    # --dipole      requests dipole printout
    # --wbo          requests Wiberg bond order printout
    # --lmo          requests localization of orbitals
    # --fod          requests FOD calculation, adjusts electronic temperature to 12500 K if possible
    # --scc, --sp    performs a single point calculation
    # --vip          performs calculation of ionisation potential
    # --vea          performs calculation of electron affinity
    # --vipea        performs calculation of IP and EA
    # --vomega       performs calculation of electrophilicity index
    # --vfukui       calculate Fukui indicies using GFN-xTB
    # --esp          calculate electrostatic potential on VdW-grid
    # --stm          calculate STM image
    # --grad         performs a gradient calculation
    # --optts [LEVEL] [ROOT] call ancopt(3) to perform a transition state optimization, may
    # need to perform a hessian calculation first
    # --hess         perform a numerical hessian calculation on input geometry
    # --ohess [LEVEL] perform a numerical hessian calculation on an ancopt(3) optimized geometry
    # --md           molecular dynamics simulation on start geometry
    # --omd          molecular dynamics simulation on ancopt(3) optimized geometry, a loose
    # optimization level will be chosen.
    # --metadyn [INT] meta dynamics simulation on start geometry saving INT snapshots to bias the simulation
    # --siman        conformational search by simulated annealing based on molecular dynamics.
    # Conformers are optimized with ancopt.
    # --modef INT    modefollowing algorithm.  INT specifies the mode that should be used for the modefollowing.
    # -I,--input FILE   use FILE as input source for xcontrol(7) instructions
    # --namespace STRING give this xtb(1) run a namespace. All files, even temporary ones, will
    # be named accordingly (might not work everywhere).
    # --[no]copy     copies the xcontrol file at startup (default = true)
    # --[no]restart  restarts calculation from xtbrestart (default = true)
    # -P,--parallel INT compound_number of parallel processes
    # --define       performs automatic check of input and terminate
    # --version      print version and terminate
    # --citation     print citation and terminate
    # --license      print license and terminate
    # -v,--verbose      be more verbose (not supported in every unit)

    # try to find the internal value and return
    def __getattr__(self, name):
        if name in self:
            return name
        raise AttributeError

    # prohibit any attempt to set any values
    def __setattr__(self, key, value):
        raise ValueError("No changes allowed.")


class XTBOutputEnum:

    XTBOPT_SDF = "xtbopt.sdf"
    XTBTOPO_SDF = "xtbtopo.sdf"
    XTBOPT_LOG = "xtbopt.log"
    XTBRESTART = "xtbrestart"
    WBO = "wbo"
    CHARGES = "charges"
    SUCCESS = "success"
    FAILURE = "failure"

    # tags
    TOTAL_ENERGY_TAG = "total energy / Eh"
    GRADIENT_TAG = "gradient norm / Eh/a0"

    # try to find the internal value and return
    def __getattr__(self, name):
        if name in self:
            return name
        raise AttributeError

    # prohibit any attempt to set any values
    def __setattr__(self, key, value):
        raise ValueError("No changes allowed.")


class MacromodelEnum:

    MACROMODEL = "macromodel"
    MACROMODEL_HELP = "-h"
    MACROMODEL_HELP_IDENTIFICATION_STRING = "MacroModel Startup Script"
    MACROMODEL_NJOBS = "-NJOBS"
    MACROMODEL_WAIT = "-WAIT"

    # try to find the internal value and return
    def __getattr__(self, name):
        if name in self:
            return name
        raise AttributeError

    # prohibit any attempt to set any values
    def __setattr__(self, key, value):
        raise ValueError("No changes allowed.")


class ModelBuilderEnum:

    # OPTBUILD parameters
    OPTBUILD_ENTRY_POINT = "optbuild.py"
    CONFIG = "--config"
    BEST_BUILDCONFIG_OUTPATH = (
        "--best-buildconfig-outpath"  # path to the output JSON for the best trial
    )
    BEST_MODEL_OUTPATH = (
        # path to the output model (PKL) for the best trial
        "--best-model-outpath"
    )
    MERGED_MODEL_OUTPATH = (
        "--merged-model-outpath"  # path to the production output model (PKL)
    )
    PERSISTENCE_MODE = "--model-persistence-mode"
    PERSISTENCE_MODE_PLAINSKLEARN = "plain_sklearn"
    PERSISTENCE_MODE_SKLEARNWITHOPTUNAAZ = "sklearn_with_optunaz"

    # try to find the internal value and return
    def __getattr__(self, name):
        if name in self:
            return name
        raise AttributeError

    # prohibit any attempt to set any values
    def __setattr__(self, key, value):
        raise ValueError("No changes allowed.")


class LigprepEnum:

    LIGPREP = "ligprep"
    LIGPREP_HELP = "-h"
    LIGPREP_HELP_IDENTIFICATION_STRING = "usage: ligprep [options]"
    # SMI input followed by <path> (alternatives: "-icsv", "-imae" and "-isd")
    LIGPREP_INPUT_ISMI = "-ismi"
    LIGPREP_OUTPUT_OSD = (
        "-osd"  # SD(F) output followed by <path> (alternative: "-omae")
    )
    # not used in AZdock, but would be an option to feed parameters from configuration file
    LIGPREP_INP_CONFIG = "-inp"
    LIGPREP_EPIK = (
        "-epik"  # Use "Epik" for ionization and tautomerization (Recommended)
    )
    LIGPREP_PH = (
        # Effective / target pH; followed by <number> (use 7.0 as default)
        "-ph"
    )
    # pH tolerance for generated structures; followed by <number> (use 2.0 as default)
    LIGPREP_PHT = "-pht"
    LIGPREP_AC = (
        "-ac"  # Do not respect existing chirality properties and do not respect
    )
    # chiralities from the input geometry. Generate stereoisomers for all chiral centers up to
    # the number permitted (specified using the -s option). This is equivalent to "Generate
    # all combinations" in the Ligand Preparation user interface. Default
    # behavior is to respect only explicitly indicated chiralities.
    # Filter structures via LigFilter using specifications from the file provided. Default: do not filter.
    LIGPREP_F = "-f"
    LIGPREP_G = (
        # Respect chiralities from input geometry when generating stereoisomers.
        "-g"
    )
    # Generate up to this <number> stereoisomers per input structure. (Default: 32).
    LIGPREP_S = "-s"
    # Force-field to be used for the final geometry optimization (either 14 or 16, which refers to OPLS_2005 and
    LIGPREP_BFF = "-bff"
    # OPLS3e respectively. Default: 14
    LIGPREP_FF_OPLS_2005 = "14"  # Default force-field
    LIGPREP_FF_OPLS3e = "16"  # Alternative force-field
    LIGPREP_NJOBS = (
        # Divide the overall job into NJOBS subjobs. Set to 1 by default.
        "-NJOBS"
    )
    # Divide the overall job into subjobs with no more than NSTRUCTS structures. Set to 1 by default.
    LIGPREP_NSTRUCTS = "-NSTRUCTS"
    LIGPREP_HOST = (
        # Run the job on <hostname> remotely on the indicated host entry.
        "-HOST"
    )
    LIGPREP_HOST_LOCALHOST = "localhost"  # Default value for the run.
    LIGPREP_WAIT = "-WAIT"  # Do not return a prompt until the job completes.

    LIGPREP_LOG_ENDING = ".log"

    # try to find the internal value and return
    def __getattr__(self, name):
        if name in self:
            return name
        raise AttributeError

    # prohibit any attempt to set any values
    def __setattr__(self, key, value):
        raise ValueError("No changes allowed.")


class GlideEnum:
    # executable "glide" + parameters
    # note, that you can get the full list of parameters with "$SCHRODINGER/glide -k"
    # ---------
    GLIDE = "glide"
    GLIDE_CALL = "$SCHRODINGER/glide"
    GLIDE_HELP = "-h"
    GLIDE_HELP_IDENTIFICATION_STRING = "positional arguments:"
    GLIDE_WAIT = "-WAIT"
    GLIDE_OVERWRITE = "-OVERWRITE"  # Remove previous job files before running.
    GLIDE_NJOBS = "-NJOBS"  # Divide the overall job into NJOBS subjobs.
    GLIDE_HOST = "-HOST"  # Run job remotely on the indicated host entry.
    # WARNING: does not seem to be supported (any longer?) - probably "-NOLOCAL" now?
    GLIDE_TMPLAUNCHDIR = "-TMPLAUNCHDIR"
    # WARNING: does not seem to be supported (any longer?)
    GLIDE_ATTACHED = "-ATTACHED"
    # amide bond rotation behavior: "fixed", "free", "penal", "trans", "gen[eralized]"
    GLIDE_AMIDE_MODE = "AMIDE_MODE"
    # bypass elimination of poses in rough scoring stage (useful for fragment docking)
    GLIDE_EXPANDED_SAMPLING = "EXPANDED_SAMPLING"
    GLIDE_GRIDFILE = "GRIDFILE"  # path to grid (.grd or .zip) file
    GLIDE_LIGANDFILE = "LIGANDFILE"  # Glide docking ligands file name
    # expand size of the Glide funnel by N times to process poses from N confgen runs with minor
    # perturbations to the input ligand coordinates
    GLIDE_NENHANCED_SAMPLING = "NENHANCED_SAMPLING"
    # format for file containing docked poses: "poseviewer" for _pv.mae output; "ligandlib" for
    # _lib.mae; similarly "poseviewer_sd" and "ligandlib_sd" for sdf output; "phase_subset" for bypassing
    # _lib or _pv in favor of a Phase subset file.
    GLIDE_POSE_OUTTYPE = "POSE_OUTTYPE"
    GLIDE_POSE_OUTTYPE_LIGANDLIB = (
        "ligandlib_sd"  # sets the write-out to SDF (easily parsed)
    )
    # uses the poseviewer (MAE format) write-out; contains the receptor
    GLIDE_POSE_OUTTYPE_POSEVIEWER = "poseviewer"
    GLIDE_POSES_PER_LIG = (
        "POSES_PER_LIG"  # maximum number of poses to report per each input ligand
    )
    # maximum number of best-by-Emodel poses to submit to post-docking minimization
    GLIDE_POSTDOCK_NPOSE = "POSTDOCK_NPOSE"
    GLIDE_POSTDOCKSTRAIN = (
        "POSTDOCKSTRAIN"  # include strain correction in post-docking score
    )
    # glide docking precision ("SP", "XP" or "HTVS")
    GLIDE_PRECISION = "PRECISION"
    # reward formation of intramolecular hydrogen bonds in the ligand
    GLIDE_REWARD_INTRA_HBONDS = "REWARD_INTRA_HBONDS"
    GLIDE_USE_CONS = "USE_CONS"
    GLIDE_NREQUIRED_CONS = "NREQUIRED_CONS"
    # if any of these string is present in the logfile associated with a subjob, all went well
    GLIDE_LOG_SUCCESS_STRING = "glide_sort command succeeded"
    GLIDE_LOG_FINISHED_STRINGS = {"Exiting Glide"}
    GLIDE_LOG_FAIL_STRINGS = {
        "*** Error in",
        # if any of these strings is present in the logfile associated with a subjob, there was an
        # issue resulting in the complete failure of the execution
        "Glide cannot recover from this signal and will now abort.",
        "======= Backtrace: =========",
    }

    # "Glide: FATAL mmlewis error"}

    # try to find the internal value and return
    def __getattr__(self, name):
        if name in self:
            return name
        raise AttributeError

    # prohibit any attempt to set any values
    def __setattr__(self, key, value):
        raise ValueError("No changes allowed.")


class SchrodingerExecutablesEnum:

    # executable "licadmin" + parameters
    # ---------
    LICADMIN = "licadmin"
    LICADMIN_STAT = "STAT"  # returns the list of tokens used / available

    # executable "sdconvert" + parameters
    # ---------
    SCHRODINGER_MODULE = "module load schrodinger/2021-2-js-aws"
    SDCONVERT = "sdconvert"
    SDCONVERT_CALL = "$SCHRODINGER/utilities/sdconvert"
    SDCONVERT_HELP = ""
    SDCONVERT_HELP_IDENTIFICATION_STRING = "mae : Maestro format"
    SDCONVERT_A = "-a"  # append structures to the output file
    # input; note that the format is directly appended (e.g. "-isd")
    SDCONVERT_I = "-i"
    SDCONVERT_O = (
        # output; note that the format is directly appended (e.g. "-omae")
        "-o"
    )
    SDCONVERT_FORMAT_SD = "sd"  # MDL SDfile format
    SDCONVERT_FORMAT_PDB = "pdb"  # PDB file format
    SDCONVERT_FORMAT_MM = "mm"  # MacroModel (.dat) format
    SDCONVERT_FORMAT_MAE = "mae"  # Maestro format
    SDCONVERT_TITLE = (
        "-title"  # define SD property <prop> as the source of the Maestro title
    )
    SDCONVERT_NOSTEREO = (
        "-nostereo"  # do not record the atom parity info from the input file
    )
    # do not convert aromatic type 4 bonds to single and double bonds (which is the Maestro convention)
    SDCONVERT_NOAROM = "-noarom"

    # executable "structcat" + parameters
    STRUCTCAT = "structcat"
    STRUCT_SPLIT_CALL = "$SCHRODINGER/run split_structure.py"
    STRUCT_SPLIT = "structsplit"
    STRUCTCONVERT = "structconvert"
    STRUCTCAT_CALL = "$SCHRODINGER/utilities/structcat"
    STRUCTCONVERT_CALL = "$SCHRODINGER/utilities/structconvert"
    FMP_STATS = "fmp_stats"
    FMP_STATS_CALL = "$SCHRODINGER/run -FROM scisol fmp_stats.py"
    STRUCTCAT_HELP = "-h"
    PROTEIN_INTERACTION_CALL = "$SCHRODINGER/run protein_interaction_analysis.py"
    PROTEIN_INTERACTION = "protein_interaction"
    STRUCTCAT_HELP_IDENTIFICATION_STRING = "<format> must be one of"
    # input; note that the format is directly appended (e.g. "-isd")
    STRUCTCAT_I = "-i"
    STRUCTCAT_O = (
        # output; note that the format is directly appended (e.g. "-omae")
        "-o"
    )
    STRUCTCAT_FORMAT_MAE = "mae"  # Maestro format
    STRUCTCAT_FORMAT_SD = "sd"  # MDL SDfile format
    STRUCTCAT_FORMAT_SDF = "sdf"
    STRUCTCAT_FORMAT_PDB = "pdb"  # PDB format
    STRUCTCAT_FORMAT_MOL2 = "mol2"  # sybyl (.mol2) format
    POSEVIEWER_FILE_KEY = "pv.maegz"
    PREPWIZARD = "prepwizard"
    PREPWIZARD_CALL = "$SCHRODINGER/utilities/prepwizard"
    MULTISIM_EXEC = "$SCHRODINGER/utilities/multisim"
    AWS_BINARY_LOC = "ssh <location> /opt/schrodinger/suite/installations/default/"

    # try to find the internal value and return
    def __getattr__(self, name):
        if name in self:
            return name
        raise AttributeError

    # prohibit any attempt to set any values
    def __setattr__(self, key, value):
        raise ValueError("No changes allowed.")


class PrimeEnum:

    PRIME_MMGBSA = "prime_mmgbsa"
    PRIME_HELP = "-h"
    PRIME_HELP_IDENTIFICATION_STRING = (
        "run $SCHRODINGER/prime_mmgbsa -h for a complete listing of all options."
    )
    PRIME_NJOBS = "-NJOBS"
    PRIME_WAIT = "-WAIT"

    # settings
    PRIME_OUTTYPE = "-out_type"
    PRIME_OUTTYPE_LIGAND = "LIGAND"

    # tags in output
    PRIME_MMGBSA_TOTAL_ENERGY = (
        "r_psp_MMGBSA_dG_Bind"  # total energy of binding: complex - receptor - ligand
    )
    PRIME_MMGBSA_TOTAL_ENERGY_NS = (
        # as above but without strain energy correction
        "r_psp_MMGBSA_dG_Bind(NS)"
    )

    # try to find the internal value and return
    def __getattr__(self, name):
        if name in self:
            return name
        raise AttributeError

    # prohibit any attempt to set any values
    def __setattr__(self, key, value):
        raise ValueError("No changes allowed.")


class PantherEnum:

    PANTHER_PTYHON2 = "python2"
    PANTHER_ENTRYPOINT = "panther.py"
    PANTHER_CONFIG = "panther_config.in"
    PANTHER_OUTPUT_FILE = "neg_image.mol2"

    # try to find the internal value and return
    def __getattr__(self, name):
        if name in self:
            return name
        raise AttributeError

    # prohibit any attempt to set any values
    def __setattr__(self, key, value):
        raise ValueError("No changes allowed.")


class KallistoEnum:

    KALLISTO = "kallisto"  # Binary name.

    # OPTIONS
    SILENT = "--silent"
    SHIFT = "--shift"  # INTEGER argument
    HELP = "--help"  # Show this message and exit
    HELP_IDENTIFICATION_STRING = "Show this message"

    # COMMANDS
    ALP = "alp"  # Static atomic polarizabilities in Bohr^3.
    BONDS = "bonds"  # Get information about covalent bonding partner.
    CNS = "cns"  # Atomic coordination numbers.
    EEQ = "eeq"  # Electronegativity equilibration atomic partial charges.
    EXS = (
        "exs"  # Exchange a substrate within a transition metal complex with another...
    )
    LIG = "lig"  # Get all substructures (or ligands) that are bound to the center...
    PROX = "prox"  # Atomic proximity shells.
    RMS = "rms"  # Calculate the root mean squared deviation between two structures...
    SORT = "sort"  # Sort input geoemtry according to connectivity.
    STM = "stm"  # Calculate sterimol descriptors using kallisto van der Waals radii.
    VDW = "vdw"  # Charge-dependent atomic van der Waals radii in Bohr.

    # try to find the internal value and return
    def __getattr__(self, name):
        if name in self:
            return name
        raise AttributeError

    # prohibit any attempt to set any values
    def __setattr__(self, key, value):
        raise ValueError("No changes allowed.")


class JazzyEnum:
    JAZZY = "jazzy"
    HELP = "--help"
    HELP_IDENTIFICATION_STRING = "Show this message"

    # "vec" command
    VEC = "vec"  # "generating properties" mode
    VEC_OPT = "--opt"  # optimization
    VEC_OPT_MMF94 = "MMF94"
    VEC_OPT_MMF94S = "MMF94s"
    VEC_OPT_UFF = "UFF"
    VEC_STRENGHT_ONLY = "--strength_only"

    # "vis" command
    VIS = "vis"  # "generating graphics" mode
    VIS_OPT = "--opt"
    VIS_OPT_MMF94 = "MMF94"
    VIS_OPT_MMF94S = "MMF94s"
    VIS_OPT_UFF = "UFF"
    VIS_FIG_SIZE = "--fig_size"  # Size of SVG image in pixels.  [default: 500, 500]
    VIS_SDC_THRESHOLD = (
        "--sdc_threshold"  # Treshold strength to depic Carbon donors. [default: 0.0]
    )
    VIS_SA_THRESHOLD = (
        "--sa_threshold"  # Treshold strength to depic acceptors. [default: 0.0]
    )
    VIS_BASE64 = "--base64"
    VIS_FLATTEN_MOLECULE = "--flatten_molecule"
    VIS_HIGHLIGHT_ATOMS = "--highlight_atoms"
    VIS_IGNORE_SDC = "--ignore_sdc"
    VIS_IGNORE_SDX = "--ignore_sdx"
    VIS_IGNORE_SA = "--ignore_sa"
    VIS_HELP = "--help"

    # result dictionary keys for "vec" command
    RESULT_SDC = "sdc"
    RESULT_SDX = "sdx"
    RESULT_SA = "sa"
    RESULT_DGA = "dga"
    RESULT_DGP = "dgp"
    RESULT_DGTOT = "dgtot"
    RESULT_STATUS = "__status"
    RESULT_STATUS_SUCCESS = "success"
    RESULT_SMILES = "smiles"

    # try to find the internal value and return
    def __getattr__(self, name):
        if name in self:
            return name
        raise AttributeError

    # prohibit any attempt to set any values
    def __setattr__(self, key, value):
        raise ValueError("No changes allowed.")


class ShaepEnum:

    SHAEP_EXECUTABLE = "shaep"
    OUTPUT_SIMILARITY = "similarity.txt"
    NEGATIVE_IMAGE_OUTPUT_FILE = "neg_image.mol2"
    CONFORMER_PATH = "conformer.sdf"
    TAG_SHAPE_SIMILARITY = "shape_similarity"
    TAG_ESP_SIMILARITY = "esp_similarity"

    def __getattr__(self, name):
        if name in self:
            return name
        raise AttributeError

    # prohibit any attempt to set any values
    def __setattr__(self, key, value):
        raise ValueError("No changes allowed.")


class GromacsEnum:

    # gmx programs
    PDB2GMX = "gmx pdb2gmx"
    EDITCONF = "gmx editconf"
    SOLVATE = "gmx solvate"
    GROMPP = "gmx grompp"
    GENION = "gmx genion"
    MDRUN = "gmx mdrun"
    MPI_MDRUN = "gmx_mpi mdrun"
    MAKE_NDX = "gmx make_ndx"
    GENRESTR = "gmx genrestr"
    TRJCAT = "gmx trjcat"
    TRJCONV = "gmx trjconv"
    CLUSTER = "gmx cluster"
    RMS = "gmx rms"
    ANTECHAMBER = "antechamber"
    PDB2GMX_FAIL_ID_STRING = "Required option was not provided"
    ACPYPE_BINARY = "acpype"
    MMPBSA = "gmx_MMPBSA"
    DO_DSSP = "gmx do_dssp"
    SELECT = "gmx select"
    CLUSTER_TS = "Rscript $MDPLOT/MDplot/inst/bash/MDplot_bash.R clusters_ts"
    PRIMARY_COMPONENTS = ["Protein", "DNA", "RNA"]
    # from residuetypes.dat
    AMBER_PARAMETRISED_COMPONENTS = [
        "ABU",
        "ACE",
        "AIB",
        "ALA",
        "ARG",
        "ARGN",
        "ASN",
        "ASN1",
        "ASP",
        "ASP1",
        "ASPH",
        "ASPP",
        "ASH",
        "CT3",
        "CYS",
        "CYS1",
        "CYS2",
        "CYSH",
        "DALA",
        "GLN",
        "GLU",
        "GLUH",
        "GLUP",
        "GLH",
        "GLY",
        "HIS",
        "HIS1",
        "HISA",
        "HISB",
        "HISH",
        "HISD",
        "HISE",
        "HISP",
        "HSD",
        "HSE",
        "HSP",
        "HYP",
        "ILE",
        "LEU",
        "LSN",
        "LYS",
        "LYSH",
        "MELEU",
        "MET",
        "MEVAL",
        "NAC",
        "NME",
        "NHE",
        "NH2",
        "PHE",
        "PHEH",
        "PHEU",
        "PHL",
        "PRO",
        "SER",
        "THR",
        "TRP",
        "TRPH",
        "TRPU",
        "TYR",
        "TYRH",
        "TYRU",
        "VAL",
        "PGLU",
        "HID",
        "HIE",
        "HIP",
        "LYP",
        "LYN",
        "CYN",
        "CYM",
        "CYX",
        "DAB",
        "ORN",
        "HYP",
        "NALA",
        "NGLY",
        "NSER",
        "NTHR",
        "NLEU",
        "NILE",
        "NVAL",
        "NASN",
        "NGLN",
        "NARG",
        "NHID",
        "NHIE",
        "NHIP",
        "NHISD",
        "NHISE",
        "NHISH",
        "NTRP",
        "NPHE",
        "NTYR",
        "NGLU",
        "NASP",
        "NLYS",
        "NORN",
        "NDAB",
        "NLYSN",
        "NPRO",
        "NHYP",
        "NCYS",
        "NCYS2",
        "NMET",
        "NASPH",
        "NGLUH",
        "CALA",
        "CGLY",
        "CSER",
        "CTHR",
        "CLEU",
        "CILE",
        "CVAL",
        "CASN",
        "CGLN",
        "CARG",
        "CHID",
        "CHIE",
        "CHIP",
        "CHISD",
        "CHISE",
        "CHISH",
        "CTRP",
        "CPHE",
        "CTYR",
        "CGLU",
        "CASP",
        "CLYS",
        "CORN",
        "CDAB",
        "CLYSN",
        "CPRO",
        "CHYP",
        "CCYS",
        "CCYS2",
        "CMET",
        "CASPH",
        "CGLUH",
        "DA",
        "DG",
        "DC",
        "DT",
        "DA5",
        "DG5",
        "DC5",
        "DT5",
        "DA3",
        "DG3",
        "DC3",
        "DT3",
        "DAN",
        "DGN",
        "DCN",
        "DTN",
        "A",
        "U",
        "C",
        "G",
        "RA",
        "RU",
        "RC",
        "RG",
        "RA5",
        "RT5",
        "RU5",
        "RC5",
        "RG5",
        "RA3",
        "RT3",
        "RU3",
        "RC3",
        "RG3",
        "RAN",
        "RTN",
        "RUN",
        "RCN",
        "RGN",
        "SOL",
        "WAT",
        "HOH",
        "OHH",
        "TIP",
        "T3P",
        "T4P",
        "T5P",
        "T3H",
        "K",
        "NA",
        "CA",
        "MG",
        "CL",
        "ZN",
        "CU1",
        "CU",
        "LI",
        "NA+",
        "RB",
        "CS",
        "F",
        "CL-",
        "BR",
        "I",
        "OH",
        "Cal",
        "IB+",
    ]
    IONS = ["ZN", "MG", "CU", "CA", "NA", "CL", "RB", "CS", "F", "BR", "I", "OH", "K"]
    LIG_ID = "lig_id.lig"
    LIG_EXT = "lig"
    ATOMS = ["HETATM", "ATOM"]
    ATOMS_DIRECTIVE = "[ atoms ]"
    BONDS = "[ bonds ]"
    ATOMTYPES = "[ atomtypes ]"
    MOLECULETYPES = "[ moleculetype ]"
    MOLECULES = "[ molecules ]"
    SOLVENTS = ["HOH ", "SOL", "WAT"]
    TERMINATIONS = ["ENDMDL", "END"]
    SYSTEM = "[ system ]"
    DEFAULTS = "[ defaults ]"

    def __getattr__(self, name):
        if name in self:
            return name
        raise AttributeError

    # prohibit any attempt to set any values
    def __setattr__(self, key, value):
        raise ValueError("No changes allowed.")


class PMXEnum:

    # $PMX programs (see respective steps for the help strings)
    ABFE = "$PMX abfe"
    ANALYSE = "$PMX analyse"
    ATOMMAPPING = "$PMX atomMapping"
    DOUBLEBOX = "$PMX doublebox"
    GENLIB = "$PMX genlib"
    GENTOP = "$PMX gentop"
    LIGANDHYBRID = "$PMX ligandHybrid"
    MUTATE = "$PMX mutate"

    # custom scripts
    BOX_WATER_IONS = "box_water_ions.py"
    PREPARE_SIMULATIONS = "prepare_simulations.py"
    PREPARE_TRANSITIONS = "prepare_transitions.py"
    RUN_ANALYSIS = "run_analysis.py"
    RUN_SIMULATIONS = "run_simulations.py"
    ASSEMBLE_SYSTEMS = "assemble_systems.py"

    ANALYSE_HELP = "-h"
    ANALYSE_HELP_SUCCESS_STRING = "Calculates free energies from fast"

    # standard file extensions
    PDB = "pdb"

    # file system standards
    HYBRID_STR_TOP = "hybridStrTop"

    def __getattr__(self, name):
        if name in self:
            return name
        raise AttributeError

    # prohibit any attempt to set any values
    def __setattr__(self, key, value):
        raise ValueError("No changes allowed.")


class StepPMXEnum:
    SIM_TYPE = "sim_type"
    BOXSHAPE = "boxshape"
    BOXD = "boxd"
    WATER = "water"
    CONC = "conc"
    PNAME = "pname"
    NNAME = "nname"

    RUN_TYPE = "run_type"
    SIM_TYPES = "sim_types"
    EXEC_MODE = "exec_mode"
    MDRUN_EXECUTABLE = "mdrun_executable"

    ABFE = "abfe"
    RBFE = "rbfe"

    PREV_STEP = "previous_step"
    STRICT = "strict"


class PMXAtomMappingEnum:

    HELP = "--help"  # show this help message and exit. <>
    I1 = "-i1"  # Input ligand structure 1. Default is "lig1.pdb" <1 pdb file>
    I2 = "-i2"  # Input ligand structure 2. Default is "lig2.pdb" <1 pdb file>
    O1 = "-o1"  # Output pairs: column1:mol1, column2:mol2. Default is "pairs1.dat" <1 dat file>
    O2 = "-o2"  # Output pairs: column1:mol2, column2:mol1. Default is "pairs2.dat" <1 dat file>
    OPDB1 = "-opdb1"  # Optional output: superimposed structure 1. <1 pdb file>
    OPDB2 = "-opdb2"  # Optional output: superimposed structure 2. <1 pdb file>
    OPDBM1 = "-opdbm1"  # Optional output: morphable atoms in structure 1 <2 pdb files>
    OPDBM2 = "-opdbm2"  # Optional output: morphable atoms in structure 2. <2 pdb files>
    # Optional output: score of the morph. Default is "out_score.dat" <1 dat file>
    SCORE = "-score"
    N1 = "-n1"  # Optional input: index of atoms to consider for mol1 <1 ndx file>
    N2 = "-n2"  # Optional input: index of atoms to consider for mol2 <1 ndx file>
    LOG = "-log"  # Output: log file. Default is "mapping.log" <1 log file>
    NO_ALIGNMENT = (
        # Should the alignment method be disabled (default enabled) <>
        "--no-alignment"
    )
    # Should the MCS method be disabled (default enabled) <>
    NO_MCS = "--no-mcs"
    # Should non-polar hydrogens be discarded from morphing into any other hydrogen (default True) <>
    NO_H2H = "--no-H2H"
    # Should polar hydrogens be morphed into polar hydrogens (default False) <>
    H2HPOLAR = "--H2Hpolar"
    H2HEAVY = (
        # Should hydrogen be morphed into a heavy atom (default False) <>
        "--H2Heavy"
    )
    # Should rings only be used in the MCS search and alignemnt (default False) <>
    RINGSONLY = "--RingsOnly"
    # Should the distance criterium be also applied in the MCS based search (default False) <>
    DMCS = "--dMCS"
    # Try swapping the molecule order which would be a cross-check and require double execution time (default False) <>
    SWAP = "--swap"
    NO_CHIRALITY = (
        # Perform chirality check for MCS mapping (default True) <>
        "--no-chirality"
    )
    # Distance (nm) between atoms to consider them morphable for alignment approach (default 0.05 nm). <1 numeric value>
    D = "--d"
    # Maximum time (s) for an MCS search (default 10 s). <1 numeric value>
    TIMEOUT = "--timeout"

    LIGAND_DIR = "input/ligands"

    def __getattr__(self, name):
        if name in self:
            return name
        raise AttributeError

    # prohibit any attempt to set any values
    def __setattr__(self, key, value):
        raise ValueError("No changes allowed.")


class PMXLigandHybridEnum:

    HELP = "--help"  # Show this help message and exit. <>
    I1 = "-i1"  # Input ligand structure 1. Default is "lig1.pdb" <1 pdb file>
    I2 = "-i2"  # Input ligand structure 2. Default is "lig2.pdb" <1 pdb file>
    ITP1 = "-itp1"  # Input ligand topology 1. Default is "lig1.itp" <1 itp file>
    ITP2 = "-itp2"  # Input ligand topology 2. Default is "lig2.itp" <1 itp file>
    PAIRS = "-pairs"  # Optional input: atom pair mapping. <1 dat file>
    N1 = "-n1"  # Optional input: index of atoms to consider for mol1. <1 ndx file>
    N2 = "-n2"  # Optional input: index of atoms to consider for mol2. <1 ndx file>
    OA = "-oA"  # Output: hybrid structure based on the ligand 1. Default is "mergedA.pdb" <1 pdb file>
    OB = "-oB"  # Output: hybrid structure based on the ligand 2. Default is "mergedB.pdb" <1 pdb file>
    OITP = "-oitp"  # Output: hybrid topology. Default is "merged.itp". <1 itp file>
    # Output: atomtypes for hybrid topology. Default is "ffmerged.itp" <1 itp file>
    OFFITP = "-offitp"
    LOG = "-log"  # Output: log file. Default is "hybrid.log" <1 log file>
    # Optional: if -pairs not provided, distance (nm) between atoms to consider them morphable
    # for alignment approach (default 0.05 nm). <1 numerical value>
    D = "--d"
    FIT = "--fit"  # Fit mol2 onto mol1, only works if pairs.dat is provided. <>
    SPLIT = "--split"  # Split the topology into separate transitions.
    SCDUMM = (
        "--scDUMm"  # Scale dummy masses using the counterpart atoms. <1 numeric value>
    )
    SCDUMA = "--scDUMa"  # Scale bonded dummy angle parameters. <1 numeric value>
    SCDUMD = "--scDUMd"  # Scale bonded dummy dihedral parameters. <1 numeric value>
    DEANG = "--deAng"  # Decouple angles composed of 1 dummy and 2 non-dummies.

    def __getattr__(self, name):
        if name in self:
            return name
        raise AttributeError

    # prohibit any attempt to set any values
    def __setattr__(self, key, value):
        raise ValueError("No changes allowed.")


class FepPlusEnum:
    FEP_MAPPER = "$SCHRODINGER/run -FROM scisol fep_mapper.py"
    FEP_EXECUTOR = "$SCHRODINGER/fep_plus"
    FEP_ABSOLUTE_EXECUTOR = "$SCHRODINGER/fep_absolute_binding"
    FEP_HELP = "-h"
    JSC_LIST = 'ssh $SCHRODINGER_JOBSERVER "export SCHRODINGER=/opt/schrodinger/suite/installations/default && /opt/schrodinger/suite/installations/default/jsc list"'
    JSC_TAIL_FILE = 'ssh $SCHRODINGER_JOBSERVER "export SCHRODINGER=/opt/schrodinger/suite/installations/default && /opt/schrodinger/suite/installations/default/jsc tail-file'
    DICT = "dict"
    PATH = "path"
    FEP_MAPPER_HELP_SUCCESS_STRING = "If given, the match will be allowed"


class PdbFixerEnum:
    FIXER = "pdbfixer"


class DSSPEnum:
    MKDSSP = "mkdssp"


class SlurmEnum:
    SBATCH = "sbatch"
    STATE = "State"
    COMPLETED = "COMPLETED"
    RUNNING = "RUNNING"
    PENDING = "PENDING"
    CANCELLED = "CANCELLED"
    NODE_FAIL = "NODE_FAIL"
    FAILED = "FAILED"
