"""
memcached

"""
import logging

from newrelic_python_agent.plugins import base

LOGGER = logging.getLogger(__name__)


class Memcached(base.SocketStatsPlugin):

    GUID = 'com.meetme.newrelic_memcached_agent'
    DEFAULT_PORT = 11211
    KEYS = ['curr_connections',
            'curr_items',
            'connection_structures',
            'cmd_get',
            'cmd_set',
            'cmd_flush',
            'get_hits',
            'get_misses',
            'delete_hits',
            'delete_misses',
            'incr_hits',
            'incr_misses',
            'decr_hits',
            'decr_misses',
            'cas_hits',
            'cas_misses',
            'cas_badval',
            'auth_cmds',
            'auth_errors',
            'bytes_read',
            'bytes_written',
            'bytes',
            'total_items',
            'evictions',
            'rusage_user',
            'conn_yields',
            'rusage_system']

    SOCKET_RECV_MAX = 32768

    def add_datapoints(self, stats):
        """Add all of the data points for a node

        :param dict stats: all of the nodes

        """
        self.command_value('CAS', 'cas', stats)
        self.add_derive_value('Command/Requests/Flush', 'flush',
                              stats['cmd_flush'])
        self.add_derive_value('Command/Errors/CAS', 'errors',
                              stats['cas_badval'])
        self.command_value('Decr', 'decr', stats)
        self.command_value('Delete', 'delete', stats)
        self.command_value('Get', 'get', stats)
        self.command_value('Incr', 'incr', stats)
        self.add_derive_value('Command/Requests/Set', 'requests', stats['cmd_set'])

        self.add_gauge_value('Connection/Count', 'connections',
                             stats['curr_connections'])
        self.add_gauge_value('Connection/Structures', 'connection structures',
                             stats['connection_structures'])
        self.add_derive_value('Connection/Yields', 'yields',
                              stats['conn_yields'])
        self.add_derive_value('Evictions', 'items', stats['evictions'])
        self.add_gauge_value('Items', 'items', stats['curr_items'])

        self.add_derive_value('Network/In', 'bytes', stats['bytes_read'])
        self.add_derive_value('Network/Out', 'bytes', stats['bytes_written'])

        self.add_derive_value('System/CPU/System', 'seconds',
                              stats['rusage_user'])
        self.add_derive_value('System/CPU/User', 'seconds',
                              stats['rusage_user'])
        self.add_gauge_value('System/Memory', 'bytes', stats['bytes'])

    def command_value(self, name, prefix, stats):
        """Process commands adding the command and the hit ratio.

        :param str name: The command name
        :param str prefix: The command prefix
        :param dict stats: The request stats

        """
        total = stats['%s_hits' % prefix] + stats['%s_misses' % prefix]
        if total > 0:
            ratio = (float(stats['%s_hits' % prefix]) / float(total)) * 100
        else:
            ratio = 0
        self.add_derive_value('Command/Requests/%s' % name, 'requests', total)
        self.add_gauge_value('Command/Hit Ratio/%s' % name, 'ratio', ratio)

    def fetch_data(self, connection):
        """Loop in and read in all the data until we have received it all.

        :param  socket connection: The connection

        """
        connection.send("stats\n")
        data = super(Memcached, self).fetch_data(connection)
        data_in = []
        for line in data.replace('\r', '').split('\n'):
            if line == 'END':
                return self.process_data(data_in)
            data_in.append(line.strip())
        return None

    def process_data(self, data):
        """Loop through all the rows and parse each line, looking to see if it
        is in the data points we would like to process, adding the key => value
        pair to values if it is.

        :param list data: The list of rows
        :returns: dict

        """
        values = dict()
        for row in data:
            parts = row.split(' ')
            if parts[1] in self.KEYS:
                try:
                    values[parts[1]] = int(parts[2])
                except ValueError:
                    try:
                        values[parts[1]] = float(parts[2])
                    except ValueError:
                        LOGGER.warning('Could not parse line: %r', parts)
                        values[parts[1]] = 0

        # Back fill any missed data
        for key in self.KEYS:
            if key not in values:
                LOGGER.info('Populating missing element with 0: %s', key)
                values[key] = 0

        # Return the values dict
        return values
