import psutil
import time
import whatap.util.process_util as process_util
from whatap.util.ip_util import IPUtil as ip_util
import whatap.util.logging_util as logging_util

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()



oldNicTrafficMap = None
nicInfoDict = {}
macDict = {}



def getNicInfo(deviceId):
    global nicInfoDict
    ip = ""
    mac = ""
    if deviceId not in nicInfoDict:
        ifconfig = process_util.executeCommandShellWithTimeout('ifconfig ' + deviceId, timeout=5)

        for l in ifconfig.split('\n'):
            if not l:
                continue
            token = l.split()
            if token[0] == 'inet':
                ip = token[1]
                break

        if deviceId not in macDict:
            lanscan = process_util.executeCommandShellWithTimeout('lanscan -ai', timeout=10)
            for l in lanscan.split('\n'):
                if not l:
                    continue
                token = l.split()
                if not token[0].startswith('0x'):
                    continue

                macValue = token[0][2:]
                macValue = macValue.zfill(12)
                macArr = [macValue[i:i+2] for i in range(0, len(macValue), 2)]

                formattedMac = ":".join(macArr)
                
                macDict[token[1]] = formattedMac 
                mac = formattedMac

        else:
            mac = macDict[deviceId]

        info = (ip, mac)
        nicInfoDict[deviceId] = info

    nicip, hwaddr = nicInfoDict[deviceId]
    return nicip, hwaddr



def GetNicUtil():
    global oldNicTrafficMap
    logging_util.debug("[network] start net_io_counters")
    iocounters = psutil.net_io_counters(pernic=True)
    logging_util.debug("[network] end net_io_counters - len : ", len(iocounters))

    nicTrafficMap = {}

    timestamp = int(time.time() * 1000)

    traffics = []
    for deviceId, iocounter in iocounters.items():
        nicTrafficIoRaw = _NetPerfRaw()
        nicTrafficIoRaw.deviceID = deviceId
        nicTrafficIoRaw.readByteCount = int(iocounter.bytes_recv)
        nicTrafficIoRaw.readCount = int(iocounter.packets_recv)
        nicTrafficIoRaw.readErrorCount = int(iocounter.errin)
        nicTrafficIoRaw.readDroppedCount = int(iocounter.dropin)
        nicTrafficIoRaw.writeByteCount = int(iocounter.bytes_sent)
        nicTrafficIoRaw.writeCount = int(iocounter.packets_sent)
        nicTrafficIoRaw.writeErrorCount = int(iocounter.errout)
        nicTrafficIoRaw.writeDroppedCount = int(iocounter.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 * 1000.0 * 8
            n.trafficOut = float(nicTrafficIoRaw.writeByteCount - oldNicTrafficIoRaw.writeByteCount) / timediff * 1000.0 * 8
            n.packetIn = float(nicTrafficIoRaw.readCount - oldNicTrafficIoRaw.readCount) / timediff * 1000.0
            n.packetOut = float(nicTrafficIoRaw.writeCount - oldNicTrafficIoRaw.writeCount) / timediff * 1000.0
            n.droppedIn = float(nicTrafficIoRaw.readDroppedCount - oldNicTrafficIoRaw.readDroppedCount) / timediff * 1000.0
            n.droppedOut = float(nicTrafficIoRaw.writeDroppedCount - oldNicTrafficIoRaw.writeDroppedCount) / timediff * 1000.0
            n.errorIn = float(nicTrafficIoRaw.readErrorCount - oldNicTrafficIoRaw.readErrorCount) / timediff * 1000.0
            n.errorOut = float(nicTrafficIoRaw.writeErrorCount - oldNicTrafficIoRaw.writeErrorCount) / timediff * 1000.0
            nicip, hwaddr = getNicInfo(deviceId)
            if not nicip or not hwaddr:
                continue
            n.ip = ip_util.toBytes(nicip)
            n.hwAddr = hwaddr

            traffics.append(n)



    oldNicTrafficMap = nicTrafficMap

    return traffics
    

_nicLinkSpeedCache = {}

def GetNicLinkSpeed(iface):
    if iface in _nicLinkSpeedCache:
        return _nicLinkSpeedCache[iface]
    speed_bps = None
    try:
        import re
        m = re.search(r'(\d+)$', iface)
        ppa = m.group(1) if m else iface
        # lanadmin -x <ppa> output: "Speed = 1000 Full-Duplex." (Mbps, '=' separator)
        output = process_util.executeCommandShellWithTimeout('lanadmin -x %s' % ppa, timeout=10)
        for line in output.split('\n'):
            line = line.strip()
            if not line.lower().startswith('speed'):
                continue
            parts = line.split('=', 1)
            if len(parts) < 2:
                continue
            tokens = parts[1].strip().split()
            if not tokens:
                continue
            try:
                mbps = float(tokens[0])
            except ValueError:
                continue
            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
        nicip, hwaddr = getNicInfo(deviceId)
        if not nicip or not hwaddr:
            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()
