import subprocess, shlex, os, time
from StringIO import StringIO
import whatap.util.file_util as file_util
from whatap.util.ip_util import IPUtil as ip_util
from whatap.io.data_outputx import DataOutputX
import whatap.util.process_util as process_util
import whatap.agent.conf.configure as conf
import psutil

import multiprocessing
cpucore = float(multiprocessing.cpu_count())

class ProcessPerfInfo :
    def __init__(self): 
        self.ppid= None
        self.pid= None
        self.cpu= None
        self.memoryBytes= None
        self.memoryPercent= None
        self.readBps= 0.0
        self.writeBps= 0.0
        self.cmd1= None
        self.cmd2= None
        self.readIops= 0.0
        self.writeIops= 0.0
        self.user= None
        self.state= None
        self.createTime= None
        self.net= []
        self.file= []
        self.sharedMemory = 0

    def __str__(self):
        b = StringIO()
        b.write('ppid:')
        b.write(self.ppid)
        b.write('\npid')
        b.write(self.pid)
        b.write('\ncpu')
        b.write(self.cpu)
        b.write('\nmemory')
        b.write(self.memoryBytes)


        return b.getvalue()

class IpPort:
    def __init__(self, localip= 0, localport= 0, conn= 0, ilocaladdr= 0, inode= 0):
        self.localip = localip
        self.localport = localport
        self.ilocaladdr = ilocaladdr
        self.iremoteaddr = 0
        self.conn = conn
        self.inode = inode

    def __str__(self):
        buf = StringIO()
        buf.write("localip=")
        buf.write(self.localip)
        buf.write("\nlocalport=")
        buf.write(self.localport)
        buf.write("\nilocaladdr=")
        buf.write(self.ilocaladdr)
        buf.write("\niremoteaddr=")
        buf.write(self.iremoteaddr)
        buf.write("\nconn=")
        buf.write(self.conn)
        buf.write("\ninode=")
        buf.write(self.inode)

        return buf.getvalue()

class FileInfo:
    def __init__(self, name=None, size=0):
        self.name = name
        self.size = size

class TopCommandListener:
    def __init__(self):
        delay = 20
        self.command = "top -b -n %d"%delay
        self.process = None
        
    def runCommand(self):
        homepath = conf.GetConfig().WhatapHome
        env = os.environ.copy()
        env['HOME'] = homepath
        self.process = subprocess.Popen(shlex.split(self.command), stdout=subprocess.PIPE, env=env)

    def getProcs(self):
        if self.process is None:
            self.runCommand()

        filterpids = []
        procs = []
        i =0
        while True:
            now = int(time.time() * 1000)
            output = self.process.stdout.readline().strip()
            #print output
            if not output:
                if self.process.poll() is not None:
                    self.process = None
                    populateNetworkStatus(procs)
                    return procs, now
                if i < 7:
                    time.sleep(0.1)
                    continue
                else:
                    populateNetworkStatus(procs)
                    return procs, now
            i +=1
            if i < 7:
                #print i, 'skipping', output
                continue
            p = ProcessPerfInfo()
            p.pid, p.user, p.memoryBytes, p.state, p.cpu, p.memoryPercent, p.ppid = output.split()[:7]
            p.pid = int(p.pid)
            p.ppid = int(p.ppid)
            if p.pid == 2 or p.ppid==2:
                continue
            p.cpu= float(p.cpu) / float(cpu_core)
            p.memoryPercent = float(p.memoryPercent)

            p.cmd1 = output.split()[7]
            p.cmd2 = ' '.join(output.split()[7:])
            p.memoryBytes= self.fixByteUnit(p.memoryBytes)
            p.createTime = 0

            if p.cmd1 and (p.cmd1[0] != '[' or p.cmd1[-1]  != ']'):
                procs.append(p)


    def fixByteUnit(self, src):
        unit = {'k': 1024,'m': 1024*1024, 'g': 1024*1024*1024, 't': 1024*1024*1024*1024}
        try:
            src = int(float(src))
            return src
        except ValueError, e:
            src = int(float(src[:-1])) * unit[src[-1].lower()]

            return src

def populateNetworkStatus(processPerfs ):
    localips = []
    ipinfo = process_util.executeCmd("ip addr show")

    for line in ipinfo.split('\n'):
        words = line.split()

        if words and words[0] == 'inet':
            localips.append(words[1].split('/')[0])

    inodeportlookup = {}
    ipinodelookup = {}
    remoteconns =[]
    for tcpfilepath in ("/proc/net/tcp", "/proc/net/tcp6"):
        tcpContent = file_util.readContent(tcpfilepath)
        if tcpContent:
            i=0
            for line in tcpContent.split("\n"):
                i += 1
                if i == 1 or not line:
                    continue

                words = line.split()
                inode = int(words[9])
                localIPBytes = ip_util.parseHexString(words[1])
                if len(localIPBytes) != 6:
                    continue
                localIP = DataOutputX.toInt(localIPBytes[:4])
                localPort = DataOutputX.toShort(localIPBytes[4:])
                localIPPortInt = DataOutputX.toLong6(localIPBytes)
                remoteIPBytes = ip_util.parseHexString(words[2])
                if len(remoteIPBytes) != 6:
                    continue
                remoteIPInt = DataOutputX.toInt(remoteIPBytes[:4])
                remotePortInt = DataOutputX.toShort(remoteIPBytes[4:])
                ipport = IpPort(localip= localIP, localport= localPort, conn= 0, ilocaladdr= localIPPortInt, inode= int(inode))
                if remoteIPInt == 0 and remotePortInt == 0 :
                    inodeportlookup[inode] = ipport
                    if localIPPortInt > 0:
                        ipinodelookup[localIPPortInt] = ipport
                        for ip in localips :
                            ipbytes = ip_util.toBytes(ip)
                            deviceIPPortBytes = StringIO()
                            deviceIPPortBytes.write(ipbytes)
                            deviceIPPortBytes.write(localIPBytes[4:])
                            deviceIPPortInt = DataOutputX.toLong6(deviceIPPortBytes.getvalue())
                            ipinodelookup[deviceIPPortInt] = ipport

                else:
                    remoteconns.append(ipport)

    for pipport in remoteconns:
        if pipport.ilocaladdr in ipinodelookup:
            ipinodelookup[pipport.ilocaladdr].conn += 1

    #print 'ipinodelookup'
    #from pprint import pprint
    #for k, v in ipinodelookup.items():
    #    print k,v

    for processPerf in processPerfs:
        ports=[]
        filesinfo= []
        searchDir = os.path.join("/proc", str(processPerf.pid), "fd")

        if not os.path.exists(searchDir):
            continue
        for filename in os.listdir(searchDir):
            path = os.path.join(searchDir, filename)

            if os.path.islink(path):
                link = os.readlink(path)

                if link.startswith("socket:["):
                    inode = int(link[8:len(link)-1])
                    #print 'inode', inode, inode in inodeportlookup
                    if inode in inodeportlookup:
                        ipport = inodeportlookup[inode]
                        ports.append(IpPort(localip= ipport.localip, localport= ipport.localport, conn= ipport.conn))

                elif len(link) > 0 and not link.startswith("/dev") and link[0] == '/':
                    try:
                        filestat = os.stat(link)
                        #print 'file ',processPerf.cmd1, link, filestat.st_size
                        if filestat and link.endswith(".log"):
                            fileinfo = FileInfo(name= link, size= filestat.st_size)
                            filesinfo.append(fileinfo)
                            #print processPerf.cmd1, link
                    except Exception, e:
                        pass
        processPerf.net = ports
        processPerf.file = filesinfo

def DEPRECATED_GetProc():
    return TopCommandListener()

class _ProcPerfCache:
    def __init__(self, cpu_times = None, proc_map = None):
        self.cpu_times = cpu_times
        self.proc_map = proc_map
        self.timestamp = time.time()


def calcPs(v, lastv, timediff):

    return float(v - lastv) / float(timediff)

oldProcPerfCache =None
jiff = float(100)
def getProcessPerfList():
    cpu_times = psutil.cpu_times(percpu=False)
    proc_map = {}
    for proc in psutil.process_iter():
        try:
            timestamp = time.time()
            iocounters = None
            if conf.GetConfig().serverProcessFDCheck and hasattr(proc, 'io_counters'):
                try:
                    if conf.process_fdcheck:
                        iocounters =proc.io_counters()
                except OSError:
                    pass
            proc_map[proc.pid] = (proc, proc.cpu_times(), iocounters, timestamp)
        except psutil.NoSuchProcess, e:
            pass
    procPerfCache = _ProcPerfCache(cpu_times = cpu_times, proc_map =  proc_map)
    global oldProcPerfCache
    if not oldProcPerfCache:
        oldProcPerfCache= procPerfCache

        return None

    all_cpu = cpu_times.system + cpu_times.user + cpu_times.nice + cpu_times.idle + cpu_times.irq + cpu_times.softirq + cpu_times.steal
    last_all_cpu = oldProcPerfCache.cpu_times.system + oldProcPerfCache.cpu_times.user + oldProcPerfCache.cpu_times.nice + oldProcPerfCache.cpu_times.idle + oldProcPerfCache.cpu_times.irq+ oldProcPerfCache.cpu_times.softirq+ oldProcPerfCache.cpu_times.steal
    cpu_jiff_diff = all_cpu - last_all_cpu
    vmem = psutil.virtual_memory()
    totalMem = vmem.total
    proc_perfs = []
    for (proc, cpu_times, iocounters, timestamp) in proc_map.values():
        try:
            if proc.pid == 2 or proc.ppid() == 2 or proc.pid not in oldProcPerfCache.proc_map:
                continue
            proc_mem_info = proc.memory_info()
            p = ProcessPerfInfo()
            p.pid = proc.pid
            p.ppid = proc.ppid()
            p.user = proc.username()
            p.memoryBytes = proc_mem_info.rss
            p.state = proc.status()
            _, old_cpu_times, old_iocounters, old_timestamp = oldProcPerfCache.proc_map[proc.pid]
            cpu_times = proc.cpu_times()
            p.cpu = jiff * (cpu_times.user + cpu_times.system - old_cpu_times.user - old_cpu_times.system) / cpu_jiff_diff
            p.memoryPercent = 100.0 * p.memoryBytes / totalMem

            p.cmd1 = proc.exe()
            p.cmd2 = ' '.join(proc.cmdline())
            p.createTime = int(proc.create_time())

            if iocounters:
                timediff = timestamp - old_timestamp
                p.readBps = calcPs(iocounters.read_bytes, old_iocounters.read_bytes, timediff)
                p.writeBps = calcPs(iocounters.write_bytes, old_iocounters.write_bytes, timediff)
                p.readIops = calcPs(iocounters.read_count, old_iocounters.read_count, timediff)
                p.writeIops = calcPs(iocounters.write_count, old_iocounters.write_count, timediff)
            else:
                p.readBps = float(0)
                p.writeBps = float(0)
                p.readIops = float(0)
                p.writeIops = float(0)

            p.sharedMemory = proc_mem_info.shared

            proc_perfs.append(p)
        except psutil.NoSuchProcess, e:
            print(e)
    oldProcPerfCache = procPerfCache

    return proc_perfs


def GetProc():
    procPerfs = getProcessPerfList()
    if procPerfs:
        populateNetworkStatus(procPerfs)

    return procPerfs

def test():
    while True:
        procs = GetProc()
        print procs
        if not procs:
            time.sleep(5)
            continue
        time.sleep(5)
       

if __name__ == '__main__':
    test()
