"""
Functions and classes used to extend a GATK tool with Python.

GATK uses two FIFOs to communicate wth Python. The "ack" FIFO is read by GATK
and written by Python code, and is used to signal that a Python command has
completed execution. The "data" FIFO is written by GATK and read by Python,
and is used to pass data to Python from Java.

Most of the functions in this module are intended to be called by GATK via
the StreamingPythonScriptExecutor Java class, and are not called by Python
code directly. The one exception is the readDataFIFO function, which can be
used to read data that had been passed to Python by GATK Java code.
"""

import sys
import os
import cProfile, pstats, io
import traceback
from gatktool import toolconstants

_ackFIFO = None
_dataFIFO = None
_GATKProfiler = None

def initializeGATK(ackFIFOName: str):
    """
    Open the GATK ack FIFO and install the exception handler hook.

    Called by GATK when the StreamingPythonScriptExecutor is initialized,
    which is normally in onTraversalStart. Initializes the ack FIFO and
    installs the exception hook. Since the exception hook uses the ack FIFO,
    it can't be installed until after the FIFO is initialized.
    """
    global _ackFIFO
    _ackFIFO = AckFIFO(ackFIFOName)
    sys.excepthook = gatkExceptionHook


def gatkExceptionHook(exceptionType, value, tracebck):
    """
    GATK Handler for uncaught Python exceptions.

    The is installed by initializeGATK after the ack FIFO has been
    initialized. When an unhandled exception is caught, the handler
    sends a nack to GATK through the FIFO, which results in a
    PythonScriptExecutorException being thrown in the tool.
    """
    sendNackWithMessage(' '.join(map(str, traceback.format_exception(exceptionType, value, tracebck))))
    sys.__excepthook__(exceptionType, value, tracebck)


def sendAck():
    """
    Send a positive acknowledgment to GATK. This should generally only
    be called by python code that is embedded in Java, since the executor
    keeps track of whether an ack request is outstanding.
    """
    global _ackFIFO
    _ackFIFO.writeAck()


def sendNack():
    """
    Send a negative acknowledgment to GATK. Generally only called by the
    installed exception hook. This will result in a Java exception being
    thrown that unless caught by Java code, will terminate the tool.
    """
    global _ackFIFO
    _ackFIFO.writeNack()


def sendNackWithMessage(nckMessage: str):
    """
    Send a negative acknowledgment to GATK, along with a message. Generally only
    called by the installed exception hook. This will result in a Java exception being
    thrown that unless caught by Java code, will terminate the tool.
    """
    global _ackFIFO
    _ackFIFO.writeNackWithMessage(nckMessage)


def terminateGATK():
    """
    Called by GATK when no more Python commands will be executed
    """
    global _ackFIFO
    if _ackFIFO is None:
        raise RuntimeError("ack FIFO has not been initialized")
    _ackFIFO.close()