"""  # noqa
   ncalls  tottime  percall  cumtime  percall filename:lineno(function)

4440645/863938    5.173    0.000   24.374    0.000 /Users/spinlock/src/wrmsr/omnibus/omnibus/_vendor/antlr4/atn/LexerATNSimulator.py:317(closure)
  4993206    4.551    0.000   13.223    0.000 /Users/spinlock/src/wrmsr/omnibus/omnibus/_vendor/antlr4/atn/LexerATNSimulator.py:361(getEpsilonTarget)
  4440646    3.578    0.000    7.626    0.000 /Users/spinlock/src/wrmsr/omnibus/omnibus/_vendor/antlr4/atn/ATNConfig.py:114(__init__)
10004289/3007740    2.878    0.000    5.898    0.000 {built-in method builtins.hash}
  4451670    2.615    0.000    2.615    0.000 /Users/spinlock/src/wrmsr/omnibus/omnibus/_vendor/antlr4/atn/ATNConfig.py:25(__init__)
 14442146    1.596    0.000    1.596    0.000 {built-in method builtins.isinstance}
  1394624    1.310    0.000    2.503    0.000 /Users/spinlock/src/wrmsr/omnibus/omnibus/_vendor/antlr4/atn/ATNConfig.py:62(__eq__)
  2790697    1.190    0.000    4.100    0.000 /Users/spinlock/src/wrmsr/omnibus/omnibus/_vendor/antlr4/atn/ATNConfig.py:124(__hash__)
  1397105    1.150    0.000    5.438    0.000 /Users/spinlock/src/wrmsr/omnibus/omnibus/_vendor/antlr4/atn/ATNConfigSet.py:71(add)
  1397105    1.016    0.000    4.135    0.000 /Users/spinlock/src/wrmsr/omnibus/omnibus/_vendor/antlr4/atn/ATNConfigSet.py:96(getOrAdd)
  3608771    0.936    0.000    1.443    0.000 /Users/spinlock/src/wrmsr/omnibus/omnibus/_vendor/antlr4/atn/ATNConfig.py:152(checkNonGreedyDecision)
  1393552    0.931    0.000    3.533    0.000 /Users/spinlock/src/wrmsr/omnibus/omnibus/_vendor/antlr4/atn/ATNConfig.py:129(__eq__)
     8577    0.876    0.000   24.974    0.003 /Users/spinlock/src/wrmsr/omnibus/omnibus/_vendor/antlr4/atn/LexerATNSimulator.py:300(computeStartState)
  2803257    0.815    0.000    1.112    0.000 /Users/spinlock/src/wrmsr/omnibus/omnibus/_vendor/antlr4/atn/SemanticContext.py:108(__hash__)
1556284/569333    0.782    0.000    0.940    0.000 /Users/spinlock/src/wrmsr/omnibus/omnibus/_vendor/antlr4/PredictionContext.py:134(__eq__)
   790613    0.593    0.000    1.532    0.000 /Users/spinlock/src/wrmsr/omnibus/omnibus/_vendor/antlr4/PredictionContext.py:119(__init__)
   790612    0.367    0.000    1.899    0.000 /Users/spinlock/src/wrmsr/omnibus/omnibus/_vendor/antlr4/PredictionContext.py:111(create)
  1394771    0.324    0.000    2.944    0.000 /Users/spinlock/src/wrmsr/omnibus/omnibus/_vendor/antlr4/atn/ATNConfig.py:142(hashCodeForConfigSet)
   790635    0.312    0.000    0.771    0.000 /Users/spinlock/src/wrmsr/omnibus/omnibus/_vendor/antlr4/PredictionContext.py:71(calculateHashCode)
    79143    0.307    0.000    3.850    0.000 /Users/spinlock/src/wrmsr/omnibus/omnibus/_vendor/antlr4/atn/ATNConfigSet.py:132(__eq__)
  1399180    0.277    0.000    2.555    0.000 /Users/spinlock/src/wrmsr/omnibus/omnibus/_vendor/antlr4/atn/ATNConfigSet.py:156(<lambda>)
  1777084    0.250    0.000    0.250    0.000 /Users/spinlock/src/wrmsr/omnibus/omnibus/_vendor/antlr4/PredictionContext.py:144(__hash__)
  1821081    0.244    0.000    0.244    0.000 /Users/spinlock/src/wrmsr/omnibus/omnibus/_vendor/antlr4/PredictionContext.py:169(__hash__)
    17090    0.225    0.000    2.780    0.000 {built-in method _functools.reduce}
    17163    0.198    0.000    0.321    0.000 /Users/spinlock/src/wrmsr/omnibus/omnibus/_vendor/antlr4/atn/LexerATNSimulator.py:522(<genexpr>)
  1462292    0.193    0.000    6.834    0.000 {method 'get' of 'dict' objects}
     8577    0.187    0.000   34.498    0.004 /Users/spinlock/src/wrmsr/omnibus/omnibus/_vendor/antlr4/atn/LexerATNSimulator.py:88(match)
   790624    0.168    0.000    0.168    0.000 /Users/spinlock/src/wrmsr/omnibus/omnibus/_vendor/antlr4/PredictionContext.py:52(__init__)
    78417    0.166    0.000    0.322    0.000 /Users/spinlock/src/wrmsr/omnibus/omnibus/_vendor/antlr4/atn/LexerATNSimulator.py:293(getReachableTarget)
  1522265    0.162    0.000    0.162    0.000 {method 'append' of 'list' objects}

        3    0.000    0.000   34.668   11.556 /Users/spinlock/src/wrmsr/omnibus/omnibus/_vendor/antlr4/BufferedTokenStream.py:298(fill)
       13    0.013    0.001   34.668    2.667 /Users/spinlock/src/wrmsr/omnibus/omnibus/_vendor/antlr4/BufferedTokenStream.py:119(fetch)
     7292    0.021    0.000   34.653    0.005 /Users/spinlock/src/wrmsr/omnibus/omnibus/asts/_antlr/Python3Lexer.py:765(nextToken)
     7292    0.041    0.000   34.620    0.005 /Users/spinlock/src/wrmsr/omnibus/omnibus/_vendor/antlr4/Lexer.py:105(nextToken)
     8577    0.187    0.000   34.498    0.004 /Users/spinlock/src/wrmsr/omnibus/omnibus/_vendor/antlr4/atn/LexerATNSimulator.py:88(match)
     8577    0.022    0.000   34.305    0.004 /Users/spinlock/src/wrmsr/omnibus/omnibus/_vendor/antlr4/atn/LexerATNSimulator.py:111(matchATN)
     8577    0.876    0.000   24.974    0.003 /Users/spinlock/src/wrmsr/omnibus/omnibus/_vendor/antlr4/atn/LexerATNSimulator.py:300(computeStartState)
4440645/863938    5.173    0.000   24.374    0.000 /Users/spinlock/src/wrmsr/omnibus/omnibus/_vendor/antlr4/atn/LexerATNSimulator.py:317(closure)
  4993206    4.551    0.000   13.223    0.000 /Users/spinlock/src/wrmsr/omnibus/omnibus/_vendor/antlr4/atn/LexerATNSimulator.py:361(getEpsilonTarget)
  4440646    3.578    0.000    7.626    0.000 /Users/spinlock/src/wrmsr/omnibus/omnibus/_vendor/antlr4/atn/ATNConfig.py:114(__init__)
    16785    0.051    0.000    7.053    0.000 /Users/spinlock/src/wrmsr/omnibus/omnibus/_vendor/antlr4/atn/LexerATNSimulator.py:519(addDFAState)
  1462292    0.193    0.000    6.834    0.000 {method 'get' of 'dict' objects}
10004289/3007740    2.878    0.000    5.898    0.000 {built-in method builtins.hash}
  1397105    1.150    0.000    5.438    0.000 /Users/spinlock/src/wrmsr/omnibus/omnibus/_vendor/antlr4/atn/ATNConfigSet.py:71(add)
  1397105    1.016    0.000    4.135    0.000 /Users/spinlock/src/wrmsr/omnibus/omnibus/_vendor/antlr4/atn/ATNConfigSet.py:96(getOrAdd)
  2790697    1.190    0.000    4.100    0.000 /Users/spinlock/src/wrmsr/omnibus/omnibus/_vendor/antlr4/atn/ATNConfig.py:124(__hash__)
    87719    0.050    0.000    3.907    0.000 /Users/spinlock/src/wrmsr/omnibus/omnibus/_vendor/antlr4/dfa/DFAState.py:100(__eq__)
    79143    0.307    0.000    3.850    0.000 /Users/spinlock/src/wrmsr/omnibus/omnibus/_vendor/antlr4/atn/ATNConfigSet.py:132(__eq__)
  1393552    0.931    0.000    3.533    0.000 /Users/spinlock/src/wrmsr/omnibus/omnibus/_vendor/antlr4/atn/ATNConfig.py:129(__eq__)
  1394771    0.324    0.000    2.944    0.000 /Users/spinlock/src/wrmsr/omnibus/omnibus/_vendor/antlr4/atn/ATNConfig.py:142(hashCodeForConfigSet)
    17085    0.005    0.000    2.811    0.000 /Users/spinlock/src/wrmsr/omnibus/omnibus/_vendor/antlr4/dfa/DFAState.py:86(__hash__)
    17085    0.009    0.000    2.799    0.000 /Users/spinlock/src/wrmsr/omnibus/omnibus/_vendor/antlr4/atn/ATNConfigSet.py:148(__hash__)
    17085    0.010    0.000    2.790    0.000 /Users/spinlock/src/wrmsr/omnibus/omnibus/_vendor/antlr4/atn/ATNConfigSet.py:155(hashConfigs)
    17090    0.225    0.000    2.780    0.000 {built-in method _functools.reduce}
     8576    0.127    0.000    2.717    0.000 /Users/spinlock/src/wrmsr/omnibus/omnibus/_vendor/antlr4/atn/LexerATNSimulator.py:133(execATN)
  4451670    2.615    0.000    2.615    0.000 /Users/spinlock/src/wrmsr/omnibus/omnibus/_vendor/antlr4/atn/ATNConfig.py:25(__init__)
  1399180    0.277    0.000    2.555    0.000 /Users/spinlock/src/wrmsr/omnibus/omnibus/_vendor/antlr4/atn/ATNConfigSet.py:156(<lambda>)
  1394624    1.310    0.000    2.503    0.000 /Users/spinlock/src/wrmsr/omnibus/omnibus/_vendor/antlr4/atn/ATNConfig.py:62(__eq__)
     8486    0.018    0.000    2.205    0.000 /Users/spinlock/src/wrmsr/omnibus/omnibus/_vendor/antlr4/atn/LexerATNSimulator.py:222(computeTargetState)
   790612    0.367    0.000    1.899    0.000 /Users/spinlock/src/wrmsr/omnibus/omnibus/_vendor/antlr4/PredictionContext.py:111(create)
     8486    0.092    0.000    1.696    0.000 /Users/spinlock/src/wrmsr/omnibus/omnibus/_vendor/antlr4/atn/LexerATNSimulator.py:255(getReachableConfigSet)
 14442146    1.596    0.000    1.596    0.000 {built-in method builtins.isinstance}
   790613    0.593    0.000    1.532    0.000 /Users/spinlock/src/wrmsr/omnibus/omnibus/_vendor/antlr4/PredictionContext.py:119(__init__)


from ..._vendor.antlr4.atn.ATNSimulator import ATNConfigSet
from ..._vendor.antlr4.atn.LexerATNSimulator import ATNConfigSet
from ..._vendor.antlr4.atn.LexerATNSimulator import LexerATNConfig
from ..._vendor.antlr4.atn.LexerATNSimulator import OrderedATNConfigSet
from ..._vendor.antlr4.atn.ParserATNSimulator import ATNConfig
from ..._vendor.antlr4.atn.ParserATNSimulator import ATNConfigSet
from ..._vendor.antlr4.atn.PredictionMode import ATNConfig
from ..._vendor.antlr4.atn.PredictionMode import ATNConfigSet
from ..._vendor.antlr4.dfa.DFA import ATNConfigSet
from ..._vendor.antlr4.dfa.DFAState import ATNConfigSet
from ..._vendor.antlr4.error.DiagnosticErrorListener import ATNConfigSet
from ..._vendor.antlr4.LL1Analyzer import ATNConfig


  2790697    1.190    0.000    4.100    atn/ATNConfig.py:124(__hash__)
  2803257    0.815    0.000    1.112    atn/SemanticContext.py:108(__hash__)
  1394771    0.324    0.000    2.944    atn/ATNConfig.py:142(hashCodeForConfigSet)
  1777084    0.250    0.000    0.250    PredictionContext.py:144(__hash__)
  1821081    0.244    0.000    0.244    PredictionContext.py:169(__hash__)

"""
import contextlib
import functools
import logging
import textwrap
import typing as ta

from .. import lang
from .._vendor import antlr4


T = ta.TypeVar('T')


log = logging.getLogger(__name__)


def _patch_cache_hash(cls: type, fn_name: str = '__hash__') -> type:
    orig_fn = getattr(cls, fn_name)
    if orig_fn is getattr(object, fn_name, None):
        raise TypeError(cls)

    cache_attr = f'__hash_cached__{fn_name}'

    ns = {'orig': orig_fn}
    exec(textwrap.dedent(f"""
    def {fn_name}(self):
        try:
            return self.{cache_attr}
        except AttributeError:
            val = self.{cache_attr} = orig(self)
            return val
    """), ns)
    cache_fn = functools.wraps(orig_fn)(ns[fn_name])

    return lang.setattr_context(cls, fn_name,  cache_fn)


def _patch_const_hash(obj: T, fn_name: str = '__hash__') -> T:
    cls = type(obj)
    orig_fn = getattr(cls, fn_name)
    if orig_fn is getattr(object, fn_name, None):
        raise TypeError(cls)

    val = hash(obj)

    ns = {}
    exec(textwrap.dedent(f"""
    def {fn_name}(self):
        return {val!r}
    """), ns)
    cache_fn = functools.wraps(orig_fn)(ns[fn_name])

    return lang.setattr_context(cls, fn_name, cache_fn)


@contextlib.contextmanager
def patch_hash_context():
    with contextlib.ExitStack() as es:
        from .._vendor.antlr4.atn.ATNConfig import ATNConfig
        es.enter_context(_patch_cache_hash(ATNConfig))
        es.enter_context(_patch_cache_hash(ATNConfig, 'hashCodeForConfigSet'))

        from .._vendor.antlr4.atn.SemanticContext import Predicate
        es.enter_context(_patch_cache_hash(Predicate))

        from .._vendor.antlr4.PredictionContext import SingletonPredictionContext
        es.enter_context(_patch_cache_hash(SingletonPredictionContext))

        from .._vendor.antlr4.PredictionContext import EmptyPredictionContext
        es.enter_context(_patch_cache_hash(EmptyPredictionContext))

        yield


@contextlib.contextmanager
def patch_simulator_context():
    """FIXME: write full self-contained impl in pure py, then cythonize"""

    try:
        from .._ext.cy import antlr as cy
    except ImportError:
        log.exception('Exception importing antlr cython ext4ensions')
        yield
        return

    with contextlib.ExitStack() as es:
        es.enter_context(lang.setattr_context(
            antlr4.LexerATNSimulator,
            'closure',
            cy.LexerATNSimulator__closure,
        ))
        es.enter_context(lang.setattr_context(
            antlr4.LexerATNSimulator,
            'computeStartState',
            cy.LexerATNSimulator__computeStartState,
        ))

        yield
