# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

"""CounterManager for Mac OSX"""

import subprocess
from cmanager import CounterManager
import sys


def GetProcessData(pid):
    """Runs a ps on the process identified by pid and returns the output line
      as a list (pid, vsz, rss)
    """
    command = ['ps -o pid,vsize,rss -p'+str(pid)]
    try:
        handle = subprocess.Popen(command, stdout=subprocess.PIPE,
                                  universal_newlines=True, shell=True)
        handle.wait()
        data = handle.stdout.readlines()
    except:
        print("Unexpected error executing '%s': %s", (command, sys.exc_info()))
        raise

    # First line is header output should look like:
    # PID      VSZ    RSS
    # 3210    75964    920
    line = data[1]
    line = line.split()
    if line[0] == str(pid):
        return line


def GetPrivateBytes(pid):
    """Calculate the amount of private, writeable memory allocated to a
    process.
    """
    psData = GetProcessData(pid)
    return int(psData[1]) * 1024  # convert to bytes


def GetResidentSize(pid):
    """Retrieve the current resident memory for a given process"""
    psData = GetProcessData(pid)
    return int(psData[2]) * 1024  # convert to bytes


class MacCounterManager(CounterManager):
    """This class manages the monitoring of a process with any number of
       counters.

       A counter can be any function that takes an argument of one pid and
       returns a piece of data about that process.
       Some examples are: CalcCPUTime, GetResidentSize, and GetPrivateBytes
    """

    counterDict = {"Private Bytes": GetPrivateBytes,
                   "RSS": GetResidentSize}

    def __init__(self, process_name, process, counters):
        """Args:
             counters: A list of counters to monitor. Any counters whose name
             does not match a key in 'counterDict' will be ignored.
        """

        CounterManager.__init__(self)

        # the last process is the useful one
        self.pid = process.pid

        self._loadCounters()
        self.registerCounters(counters)

    def getCounterValue(self, counterName):
        """Returns the last value of the counter 'counterName'"""
        if counterName not in self.registeredCounters:
            print("Warning: attempting to collect counter %s and it is not"
                  " registered" % counterName)
            return

        try:
            return self.registeredCounters[counterName][0](self.pid)
        except Exception as e:
            print("Error in collecting counter: %s, pid: %s, exception: %s"
                  % (counterName, self.pid, e))
