"""
Module to query system stats data.

"""
from __future__ import print_function

import subprocess
import string
import time
import os
import platform

__author__ = "Andrew Gillis"


# Seconds that stats are good before needing to be refreshed.
STATS_TTL = 60


class SystemStats(object):

    _last_uptime = 0
    _uptime = {}
    _load = {}

    def __init__(self, show_bytes=False, verbose_du=False):
        """
        Arguments:
        show_bytes -- If True, show absolute bytes in size values.  If False,
                      only show sizes rounded to the highest significant power
                      of two.
        short_du   -- Show shorter disk usage strings.

        """
        self._last_disk = 0
        self._disk_stats = None

        self._last_mem = 0
        self._mem_stats = None
        self._show_bytes = show_bytes
        self._verbose_du = verbose_du

    def __str__(self):
        """Get stats information as string."""
        st = [self.uptime_str(),
              self.disk_usage_str(),
              self.cpu_load_str(),
              self.memory_usage_str(),
              self.logical_cpu_count_str()]
        return '\n\n'.join(st)

    def stats(self):
        return {'disk_usage': self.disk_usage(),
                'cpu_load': self.cpu_load(),
                'uptime': self.uptime(),
                'memory_usage': self.memory_usage(),
                'logical_cpu_count': self.logical_cpu_count()}

    def uptime_str(self):
        """Return uptime string."""
        title = 'Uptime:'
        st = [title]
        st.append('-'*len(title))
        up = self.uptime()
        st.append('Up for %s days, %s hours and %s minutes'
                  % (up['days'], up['hours'], up['minutes']))
        return '\n'.join(st)

    def disk_usage_str(self):
        """Return disk usage information as string."""
        title = 'Disk Usage:'
        st = [title]
        st.append('-'*len(title))
        disks = self.disk_usage()
        if self._verbose_du:
            for mount in sorted(disks):
                disk_info = disks[mount]
                st.append('Usage for: %s' % mount)
                for datum in ('partition', 'size', 'used', 'available',
                              'capacity'):
                    st.append('  %s: %s' % (datum, disk_info[datum]))
                st.append('')
            if disks:
                st.pop()
        else:
            for mount in sorted(disks):
                disk_info = disks[mount]
                st.append('%s\tsize=%s used=%s available=%s capacity=%s' % (
                    mount, disk_info['size'], disk_info['used'],
                    disk_info['available'], disk_info['capacity']))
        return '\n'.join(st)

    def cpu_load_str(self):
        """Return CPU load average information as string."""
        title = 'CPU Load Average:'
        st = [title]
        st.append('-'*len(title))
        load_stats = self.cpu_load()
        st.append('last one minute: %s' % load_stats['one'])
        st.append('last five minutes: %s' % load_stats['five'])
        st.append('last fifteen minutes: %s' % load_stats['fifteen'])
        return '\n'.join(st)

    def memory_usage_str(self):
        """Return memory usage information as string."""
        title = 'Memory usage:'
        st = [title]
        st.append('-'*len(title))
        mem_info = self.memory_usage()
        for mem_type in ('total', 'available', 'used', 'free', 'swap_total',
                         'swapped'):
            st.append('%s: %s' % (mem_type, mem_info.get(mem_type, 'n/a')))
        return '\n'.join(st)

    def logical_cpu_count_str(self):
        title = 'Logical CPU Count:'
        st = [title]
        st.append('-'*len(title))
        st.append(str(self.logical_cpu_count()))
        return '\n'.join(st)

    def uptime(self):
        """Return uptime info dictionary."""
        up, load = self._uptime_load()
        return up

    def cpu_load(self):
        """Return CPU load information dictionary."""
        up, load = self._uptime_load()
        return load

    def disk_usage(self):
        """Return disk usage dictionary.

        The top-level disk-usage dictionary has keys consisting of each
        mount point on the file sysem and values consisting of a dictionary of
        information pertaining to that mount point.

        Each mount point dictionary containing the following keys: partition,
        size, used, available, capacity.  Each value is a string describing
        the corresponding data.

        """
        now = int(time.time())
        if now - self._last_disk < STATS_TTL:
            return self._disk_stats

        proc = subprocess.Popen(('df', '-P'), stdout=subprocess.PIPE,
                                stderr=subprocess.PIPE)
        out, err = proc.communicate()
        df = out.decode('utf-8').strip().split('\n')
        block_size = int(df[0].split()[1].split('-')[0])
        ds = {}
        for l in df[1:]:
            part, size_bks, used_bks, avail_bks, cap, mount = l.split()[:6]
            size = int(size_bks) * block_size
            used = int(used_bks) * block_size
            avail = int(avail_bks) * block_size
            info = {'partition': part, 'capacity': cap}
            ds[mount] = info
            if self._show_bytes:
                # Show absolute bytes as well as short size value.
                info['size'] = '%s (%s)' % (size, size_str(size))
                info['used'] = '%s (%s)' % (used, size_str(used))
                info['available'] = '%s (%s)' % (
                    avail, size_str(avail))
            else:
                # Do not show absolute bytes in size values.
                info['size'] = size_str(size)