# -*- coding: utf-8 -*- 
import os.path
from subprocess import Popen, PIPE

from rrdtool import *

from settings import *

# py-rrdtool lacks this class
class Cdef:
    def __init__(self, name, expression):
        self._name = name
        self._expression = expression
    def __str__(self):
        return "CDEF:%s=%s" % (self._name, self._expression)

class TrafficRRD(object):
    """rrd for storing bandwidth information"""

    fname_pattern = os.path.join(LIB_DIR, "%s.rrd")
    graph_pattern = os.path.join(OUTPUT_DIR, "%s-%s.png")

    __in_command = 'ifconfig %s |grep bytes|cut -d":" -f2|cut -d" " -f1'
    __out_command = 'ifconfig %s |grep bytes|cut -d":" -f3|cut -d" " -f1'

    def __init__(self, interface, label=''):
        self.interface = interface
        self.label = label
        self.fname = self.fname_pattern % (self.interface)
        self.rrd = RoundRobinDatabase(self.fname)
        if not os.path.exists(os.path.join(LIB_DIR, self.fname)):
            self.rrd.create(
                    DataSource("in", type=DeriveDST, heartbeat=600, min=0, max=12500000),
                    DataSource("out", type=DeriveDST, heartbeat=600, min=0, max=12500000),
                    RoundRobinArchive(cf=AverageCF, xff=0.5, steps=1, rows=576),  # last 48 hours, every 5 mins
                    RoundRobinArchive(cf=AverageCF, xff=0.5, steps=6, rows=672),  # last 2 weeks, 30 mins average
                    RoundRobinArchive(cf=AverageCF, xff=0.5, steps=24, rows=732),  # last 2 months, 2 hours average
                    RoundRobinArchive(cf=AverageCF, xff=0.5, steps=144, rows=1460),  # last 2 years, 12 hours average
                    step=300
                    )

    def get_data(self):
        in_bytes = Popen(self.__in_command % (self.interface,), stdout=PIPE, shell=True).communicate()[0].strip()
        out_bytes = Popen(self.__out_command % (self.interface,), stdout=PIPE, shell=True).communicate()[0].strip()
        if not (in_bytes and out_bytes):
            raise ValueError("Error getting interface stats for %s", self.interface)
        print "Values %s for %s" % ((in_bytes, out_bytes), self.interface)
        return (in_bytes, out_bytes)

    def update(self, values=None, timestamp=None):
        """update data in rrd with given values or current, if None"""
        try:
            if not values:
                values = self.get_data()
            if timestamp:
                kwargs = {'timestamp': timestamp}
            else:
                kwargs = {}
            template = ("in", "out")
            self.rrd.update(Val(*values, **kwargs), template=template)
            return self
        except ValueError, e:
            print "updating %s failed (%s)" % (self.interface, e.message,)

    def graph(self):
        for period in ['day', 'week', 'month', 'year']:
            self._graph(period)
        return self

    def _graph(self, period):
        """create a graph for the given period"""

        title = "Traffic on %s" % (self.interface,)
        if self.label:
            title += " :: " + self.label
        graph = RoundRobinGraph(self.graph_pattern % (self.interface, period))
        graph.graph(
                Def("in", self.fname, data_source="in", cf=AverageCF),
                Def("out", self.fname, data_source="out", cf=AverageCF),
                Cdef("out_neg", "out,-1,*"),
                AREA("in", rrggbb="32CD32", legend="Incoming"),
                LINE1("in", rrggbb="336600"),
                GPRINT("in", cf=MaxCF, format=r"  Max\: %5.1lf %S"),
                GPRINT("in", cf=AverageCF, format=r" Avg\: %5.1lf %S"),
                GPRINT("in", cf=LastCF, format=r" Current\: %5.1lf %Sbytes/sec\n"),

                AREA("out_neg", rrggbb="4169E1", legend="Outgoing"),
                LINE1("out_neg", rrggbb="0033CC"),
                GPRINT("out", cf=MaxCF, format=r"  Max\: %5.1lf %S"),
                GPRINT("out", cf=AverageCF, format=r" Avg\: %5.1lf %S"),
                GPRINT("out", cf=LastCF, format=r" Current\: %5.1lf %Sbytes/sec\n"),
                HRULE("0", rrggbb="000000"),

                vertical_label="bytes/sec",
                lower_limit=0,
                start="-1%s" % (period,),
                title=title,
                width=600,
                height=80,
                lazy=None,
                )

class GaugeRRD(object):
    """rrd for storing any gauge-measured values"""

    fname_pattern = os.path.join(LIB_DIR, "%s.rrd")
    graph_pattern = os.path.join(OUTPUT_DIR, "%s-%s.png")

    _command = ''
    _min = _max = 0
    _units = ''

    def __init__(self, name, label=''):
        self.name = name
        self.label = label
        self.fname = self.fname_pattern % (self.name)
        self.rrd = RoundRobinDatabase(self.fname)
        if not self._command:
            raise NotImplementedError("GaugeRRD must be subclassed")
        if not os.path.exists(os.path.join(LIB_DIR, self.fname)):
            self.rrd.create(
                    DataSource("value", type=GaugeDST, heartbeat=600, min=self._min, max=self._max),
                    RoundRobinArchive(cf=AverageCF, xff=0.5, steps=1, rows=576),  # last 48 hours, every 5 mins
                    RoundRobinArchive(cf=AverageCF, xff=0.5, steps=6, rows=672),  # last 2 weeks, 30 mins average
                    RoundRobinArchive(cf=AverageCF, xff=0.5, steps=24, rows=732),  # last 2 months, 2 hours average
                    RoundRobinArchive(cf=AverageCF, xff=0.5, steps=144, rows=1460),  # last 2 years, 12 hours average
                    step=300
                    )

    def get_data(self):
        value = Popen(self._command % (self.name,), stdout=PIPE, shell=True).communicate()[0].strip()
        if not value:
            raise ValueError("Error getting value for %s", self.name)
        print "Values %s for %s" % (value, self.name)
        return (value,)

    def update(self, values=None, timestamp=None):
        """update data in rrd with given values or current, if None"""
        try:
            if not values:
                values = self.get_data()
            if timestamp:
                kwargs = {'timestamp': timestamp}
            else:
                kwargs = {}
            template = ("value",)
            self.rrd.update(Val(*values, **kwargs), template=template)
            return self
        except ValueError, e:
            print "updating failed (%s)" % (e.message,)

    def graph(self):
        for period in ['day', 'week', 'month', 'year']:
            self._graph(period)
        return self

    def _graph(self, period):
        """create a graph for the given period"""

        title = "Information on %s" % (self.name,)
        if self.label:
            title += " :: " + self.label
        graph = RoundRobinGraph(self.graph_pattern % (self.name, period))
        graph.graph(
                Def("value", self.fname, data_source="value", cf=AverageCF),
                LINE1("value", rrggbb="4116EC"),
                GPRINT("value", cf=MinCF, format=r"  Min\: %2.lf %S"),
                GPRINT("value", cf=MaxCF, format=r"  Max\: %2.lf %S"),
                GPRINT("value", cf=AverageCF, format=r" Avg\: %4.lf %S"),
                GPRINT("value", cf=LastCF, format=r" Current\: %2.lf %S" + self._units +"\n"),

                vertical_label=self._units,
                start="-1%s" % (period,),
                title=title,
                width=600,
                height=80,
                lazy=None,
                )

class HddTempRRD(GaugeRRD):
    """rrd for storing hdd temp information (requires hddtemp utility)"""
    _command = 'hddtemp -n /dev/%s'
    _max = 1000
    _units = "C"

class GoogleReaderSubscribersRRD(GaugeRRD):
    """rrd for storing info on the number of google reader subscribers"""
    _command = "tail %s -n 1000 | grep Feedfetcher | tail -n1 | sed 's/.* ([0-9]+) subscribers.*/\\1/' -r"
    _max = 1000
    _units = 'subscribers'


    def _graph(self, *args, **kwargs):
        oldname = self.name
        self.name = self.name.split('/')[-1]
        result = super(GoogleReaderSubscribersRRD, self)._graph(*args, **kwargs)
        self.name = oldname
        return result

    # property here for not having /var/lib/rrd/ + /var/log/apache/access_log
    def set_fname(self, value):
        self._fname = value
    def get_fname(self):
        return os.path.join(LIB_DIR, self._fname.split('/')[-1])
    fname = property(get_fname, set_fname)

class MadwifiAssociatedRRD(GaugeRRD):
    """rrd for storing the number of wifi clients associated with madwifi AP"""
    _command = 'cat /proc/net/madwifi/%s/associated_sta  | grep macaddr | wc -l'
    _max = 100
    _units = 'clients'

    def _graph(self, *args, **kwargs):
        oldname = self.name
        self.name = 'madwifi_assoc_' + self.name.split('/')[-1]
        result = super(MadwifiAssociatedRRD, self)._graph(*args, **kwargs)
        self.name = oldname
        return result

    def set_fname(self, value):
        self._fname = value
    def get_fname(self):
        return os.path.join(LIB_DIR, 'madwifi_assoc_' + self._fname.split('/')[-1])
    fname = property(get_fname, set_fname)