"""Small module for process lookups"""

from subprocess import Popen, PIPE
import re


# @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
class Processes(object):
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    @staticmethod
    def get():
        """Get the currently running processes.

        Returns
        -------
        processes : `list` of `umls_tools.metamap.ProcessInfo`
            The current processes.
        """
        proc_list = []
        sub_proc = Popen(["ps", "aux"], shell=False, stdout=PIPE)
        # A previous attempt (for reference only)
        # sub_proc = Popen(['ps', 'ao', 'args'], shell=False, stdout=PIPE)

        # Discard the first line (ps aux header)
        sub_proc.stdout.readline()
        for line in sub_proc.stdout:
            try:
                proc_list.append(ProcessInfo(line.strip()))
            except AttributeError as e:
                raise AttributeError(
                    "can't get process info: {0}".format(e.args[0])
                ) from e
        return proc_list

    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    @staticmethod
    def kill(*processes):
        """Attempt to kill all the given processes.

        Parameters
        ----------
        *processes : `umls_tools.metamap.processes.ProcessInfo`
            One or more processes to kill.
        """
        pid = [i.pid for i in processes]
        if len(pid) > 0:
            sub_proc = Popen(["kill"] + pid, shell=False, stdout=PIPE)

        # Discard the first line (ps aux header)
        return sub_proc.stdout.read()


# @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
class ProcessInfo(object):
    """Data structure for a linux processes.

    Parameters
    ----------
    proc_info : `bytes`
        The process line from subprocess run on ``ps -aux``. This is decoded
        into a string internally.

    Notes
    -----
    The class properties are process attributes. This is adapted from
    `this blog <http://andreinc.net/2010/11/07/how-to-get-the-active-process-list-on-a-linux-machine-using-python/>`_ . The returned object will have the following attributes:
    ``user``, ``pid``, ``cpu``, ``mem``, ``vsz``, ``rss``, ``tty``,
    ``stat``, ``start``, ``time``, ``cmd``.
    """

    PROC_REG = re.compile(
        r"""
        ^
        (?P<user>[^\s]+)\s+
        (?P<pid>[^\s]+)\s+
        (?P<cpu>[^\s]+)\s+
        (?P<mem>[^\s]+)\s+
        (?P<vsz>[^\s]+)\s+
        (?P<rss>[^\s]+)\s+
        (?P<tty>[^\s]+)\s+
        (?P<stat>[^\s]+)\s+
        (?P<start>[^\s]+)\s+
        (?P<time>[^\s]+)\s+
        (?P<cmd>.+)
        $
        """,
        re.VERBOSE,
    )
    _ATTS = [
        "user",
        "pid",
        "cpu",
        "mem",
        "vsz",
        "rss",
        "tty",
        "stat",
        "start",
        "time",
        "cmd",
    ]

    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    def __init__(self, proc_info):
        # Convert to string
        self.proc_info = proc_info.decode()

        try:
            match = self.PROC_REG.match(self.proc_info)
            for i in self._ATTS:
                setattr(self, i, match.group(i))
        except AttributeError:
            print(proc_info)
            raise

    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    def __repr__(self):
        """Pretty printing of process info"""
        attrs = []
        for i in self._ATTS:
            attrs.append("{0}='{1}'".format(i, getattr(self, i)))
        return "{0}({1})".format(self.__class__.__name__, ",".join(attrs))
