import os, time
import socket, fcntl, struct
import whatap.util.process_util as process_util
from whatap.util.hash_util import HashUtil as hash
from whatap.util.ip_util import IPUtil as ip_util
from whatap.pack.pack_enum import PackEnum
import whatap.pack.smnetperf_pack as smnetperf_pack
from StringIO import StringIO

class _NetPerfRaw:
    def __init__(self):
        self.deviceID = None
        self.readCount = None
        self.readByteCount = None
        self.readDroppedCount = None
        self.readErrorCount = None
        self.writeCount = None
        self.writeByteCount = None
        self.writeDroppedCount = None
        self.writeErrorCount = None
        self.timestamp = None

class _NetPerf:
    def __init__(self):
        self.desc = None
        self.ip = None
        self.hwAddr = None
        self.trafficIn = None
        self.trafficOut = None
        self.packetIn = None
        self.packetOut = None
        self.errorIn = None
        self.errorOut =  None
        self.droppedOut = None
        self.droppedIn = None
        self.linkUsageMax = None
        self.nicName = None

    def __str__(self):
        buf = StringIO()
        buf.write('\ndesc=')
        buf.write(self.desc)
        buf.write('\nip=')
        buf.write(self.ip)
        buf.write('\nhwAddr=')
        buf.write(self.hwAddr)
        buf.write('\ntrafficIn=')
        buf.write(self.trafficIn)
        buf.write('\ntrafficOut=')
        buf.write(self.trafficOut)
        buf.write('\npacketIn=')
        buf.write(self.packetIn)
        buf.write('\npacketOut=')
        buf.write(self.packetOut)
        buf.write('\nerrorIn=')
        buf.write(str(self.errorIn))
        buf.write('\nerrorOut=')
        buf.write(str(self.errorOut))
        buf.write('\ndroppedOut=')
        buf.write(str(self.droppedOut))
        buf.write('\ndroppedIn=')
        buf.write(str(self.droppedIn))

        return buf.getvalue()

def getIp(ifname):
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    ip= fcntl.ioctl(
        s.fileno(),
        0x8915,  # SIOCGIFADDR
        struct.pack('256s', ifname[:15])
    )[20:24]

    info = fcntl.ioctl(s.fileno(), 0x8927,  struct.pack('256s', ifname[:15]))
    hwaddr = ':'.join(['%02x' % ord(char) for char in info[18:24]])

    return ip, hwaddr


oldNicTrafficMap = None
import psutil
def GetNicUtil():
    global oldNicTrafficMap
    kstatMap = {}

    kstatMap = psutil.net_io_counters(pernic=True)
    nicTrafficMap = {}

    ipinfo = {}
    ipmpList = set()
    ipmpMemberList = set()

    # IP / MAC via psutil (no subprocess)
    try:
        import socket as _socket
        _AF_LINK = getattr(psutil, 'AF_LINK', None)
        for iface, addr_list in psutil.net_if_addrs().items():
            if iface not in ipinfo:
                ipinfo[iface] = {}
            for addr in addr_list:
                if addr.family == _socket.AF_INET:
                    ipinfo[iface]['ip'] = addr.address
                elif _AF_LINK is not None and addr.family == _AF_LINK:
                    ipinfo[iface]['hwaddr'] = addr.address
    except Exception:
        pass

    # IPMP group membership
    try:
        ipmp_out = process_util.executeCommandShellWithTimeout('ipadm show-ipmp -p -o IPMPIF,MEMBER 2>/dev/null', timeout=10)
        for line in ipmp_out.split('\n'):
            line = line.strip()
            if ':' not in line:
                continue
            parts = line.split(':')
            if len(parts) < 2:
                continue
            group, member = parts[0].strip(), parts[1].strip()
            if group:
                ipmpList.add(group)
            if member and member != group:
                ipmpMemberList.add(member)
    except Exception:
        try:
            ifconfig = process_util.executeCommandShellWithTimeout('ifconfig -a', timeout=10)
            deviceId = None
            for l in ifconfig.split('\n'):
                if not l:
                    continue
                token = l.split()
                if not token:
                    continue
                if token[0].endswith(':'):
                    deviceId = token[0].split(':')[0]
                elif token[0] == 'groupname' and deviceId:
                    groupname = token[1]
                    ipmpList.add(groupname)
                    if groupname != deviceId:
                        ipmpMemberList.add(deviceId)
        except Exception:
            pass

    timestamp = time.time()
    traffics = []

    for deviceId, v in kstatMap.items():
        nicTrafficIoRaw = _NetPerfRaw()
        nicTrafficIoRaw.deviceId = deviceId
        nicTrafficIoRaw.readByteCount = int(v.bytes_recv)
        nicTrafficIoRaw.readCount = int(v.packets_recv)
        nicTrafficIoRaw.readErrorCount = int(v.errin)
        nicTrafficIoRaw.readDroppedCount = int(v.dropin)
        nicTrafficIoRaw.writeByteCount = int(v.bytes_sent)
        nicTrafficIoRaw.writeCount = int(v.packets_sent)
        nicTrafficIoRaw.writeErrorCount = int(v.errout)
        nicTrafficIoRaw.writeDroppedCount = int(v.dropout)
        nicTrafficIoRaw.timestamp = timestamp

        nicTrafficMap[deviceId] = nicTrafficIoRaw 
        if oldNicTrafficMap and deviceId in oldNicTrafficMap:
            oldNicTrafficIoRaw = oldNicTrafficMap[deviceId]
            timediff = nicTrafficIoRaw.timestamp - oldNicTrafficIoRaw.timestamp
            n = _NetPerf()
            n.desc = deviceId
            n.trafficIn = float(nicTrafficIoRaw.readByteCount - oldNicTrafficIoRaw.readByteCount) / timediff * 8
            n.trafficOut = float(nicTrafficIoRaw.writeByteCount - oldNicTrafficIoRaw.writeByteCount) / timediff * 8
            n.packetIn = float(nicTrafficIoRaw.readCount - oldNicTrafficIoRaw.readCount) / timediff 
            n.packetOut = float(nicTrafficIoRaw.writeCount - oldNicTrafficIoRaw.writeCount) / timediff
            n.droppedIn = float(nicTrafficIoRaw.readDroppedCount - oldNicTrafficIoRaw.readDroppedCount) / timediff
            n.droppedOut = float(nicTrafficIoRaw.writeDroppedCount - oldNicTrafficIoRaw.writeDroppedCount) / timediff
            n.errorIn = float(nicTrafficIoRaw.readErrorCount - oldNicTrafficIoRaw.readErrorCount) / timediff
            n.errorOut = float(nicTrafficIoRaw.writeErrorCount - oldNicTrafficIoRaw.writeErrorCount) / timediff
            if deviceId not in ipinfo or deviceId in ipmpMemberList or 'ip' not in ipinfo[deviceId]:
                continue
            n.ip = ip_util.toBytes(ipinfo[deviceId]['ip'])
            if 'hwaddr' not in ipinfo[deviceId]:
                n.hwAddr = ""
            else:
                n.hwAddr = ipinfo[deviceId]['hwaddr']


            traffics.append(n)
    oldNicTrafficMap = nicTrafficMap

    return traffics


_nicLinkSpeedCache = {}

def GetNicLinkSpeed(iface):
    if iface in _nicLinkSpeedCache:
        return _nicLinkSpeedCache[iface]
    speed_bps = None
    try:
        stats = psutil.net_if_stats()
        if iface in stats and stats[iface].speed and stats[iface].speed > 0:
            speed_bps = float(stats[iface].speed) * 1000000.0
    except Exception:
        speed_bps = None
    if speed_bps is None:
        try:
            output = process_util.executeCommandShellWithTimeout('dladm show-phys -p -o LINK,SPEED %s' % iface, timeout=10)
            for line in output.split('\n'):
                line = line.strip()
                if ':' in line:
                    parts = line.split(':')
                    if len(parts) >= 2:
                        val_str = parts[-1].strip()
                        if val_str and val_str.isdigit():
                            mbps = float(val_str)
                            if mbps > 0:
                                speed_bps = mbps * 1000000.0
                    break
        except Exception:
            speed_bps = None
    _nicLinkSpeedCache[iface] = speed_bps
    return speed_bps


_oldNetInfoPerNicMap = None

def GetNetInfo():
    global _oldNetInfoPerNicMap
    now = time.time()
    iocounters = psutil.net_io_counters(pernic=True)

    newMap = {}
    for deviceId, counter in iocounters.items():
        raw = _NetPerfRaw()
        raw.deviceId = deviceId
        raw.readByteCount = int(counter.bytes_recv)
        raw.readCount = int(counter.packets_recv)
        raw.readErrorCount = int(counter.errin)
        raw.readDroppedCount = int(counter.dropin)
        raw.writeByteCount = int(counter.bytes_sent)
        raw.writeCount = int(counter.packets_sent)
        raw.writeErrorCount = int(counter.errout)
        raw.writeDroppedCount = int(counter.dropout)
        raw.timestamp = now
        newMap[deviceId] = raw

    if not _oldNetInfoPerNicMap:
        _oldNetInfoPerNicMap = newMap
        return None

    total_rx_bps = 0.0
    total_tx_bps = 0.0
    total_rx_pps = 0.0
    total_tx_pps = 0.0
    max_usage = -1.0
    max_nic_name = None

    for deviceId, raw in newMap.items():
        if deviceId not in _oldNetInfoPerNicMap:
            continue
        old = _oldNetInfoPerNicMap[deviceId]
        timediff = raw.timestamp - old.timestamp
        if timediff <= 0:
            continue
        rx_bps = float(raw.readByteCount - old.readByteCount) / timediff * 8
        tx_bps = float(raw.writeByteCount - old.writeByteCount) / timediff * 8
        rx_pps = float(raw.readCount - old.readCount) / timediff
        tx_pps = float(raw.writeCount - old.writeCount) / timediff
        total_rx_bps += rx_bps
        total_tx_bps += tx_bps
        total_rx_pps += rx_pps
        total_tx_pps += tx_pps

        if not deviceId.startswith('lo'):
            link_speed = GetNicLinkSpeed(deviceId)
            if link_speed and link_speed > 0:
                usage = (rx_bps + tx_bps) / link_speed * 100.0
                if usage > max_usage:
                    max_usage = usage
                    max_nic_name = deviceId

    _oldNetInfoPerNicMap = newMap

    n = _NetPerf()
    n.trafficIn = total_rx_bps
    n.trafficOut = total_tx_bps
    n.packetIn = total_rx_pps
    n.packetOut = total_tx_pps
    if max_usage >= 0:
        n.linkUsageMax = min(max_usage, 100.0)
        n.nicName = max_nic_name
    return n


def test():
    print 'test'
    print GetNicUtil()
    time.sleep(5)
    for netperf in GetNicUtil():
        print netperf

if __name__ == '__main__':
    test()
