import subprocess, shlex
import whatap.util.thread_util as thread_util
import whatap.util.logging_util as logging_util
import whatap.util.process_util as process_util
import whatap.pack.smbase_pack as smbase_pack
import whatap.agent.conf.configure as conf
import time, os
import psutil
import psutil._psutil_posix
from . import CPUStat


class _vmstat:
    def __init__(self):
        self.sys = 0
        self.user = 0
        self.idle = 0
        self.wait = 0



initVmstatFlag = False
initPollVmstatFlag = False
infraVmstat = _vmstat()
serverVmstat = _vmstat()

# polling process 
def pollVmstatForInfra(vmInterval = 2):
    global infraVmstat
    command = "vmstat -I {} 30".format(vmInterval)
    #command = "vmstat -I {} 1800".format(vmInterval)
    process = process_util.pollCommand(command)

    while True:
        line = process.stdout.readline()
        if line:
            fields = line.split()
            if len(fields) < 18 or fields[0] == "kthr" or fields[0].startswith("---") or fields[0] == "r":
                logging_util.debug(fields)
                continue
            vm = _vmstat()
            vm.user = float(fields[14])
            vm.sys = float(fields[15])
            vm.idle = float(fields[16])
            vm.wait = float(fields[17])
            infraVmstat = vm

        else:
            if process.poll() is not None:
                break

    process.wait()


def pollVmstatForServer(vmInterval = 5):
    global serverVmstat
    command = "vmstat -I {} 12".format(vmInterval)
    #command = "vmstat -I {} 720".format(vmInterval)
    process = process_util.pollCommand(command)

    while True:
        line = process.stdout.readline()
        if line:
            fields = line.split()
            if len(fields) < 18 or fields[0] == "kthr" or fields[0].startswith("---") or fields[0] == "r":
                logging_util.debug(fields)
                continue
            vm = _vmstat()
            vm.user = float(fields[14])
            vm.sys = float(fields[15])
            vm.idle = float(fields[16])
            vm.wait = float(fields[17])
            serverVmstat = vm


        else:
            if process.poll() is not None:
                break

    process.wait()



def pollVmstat():
    infraThread = None
    serverThread = None
    while True:
        if conf.GetConfig().CategoryV2Enabled and (infraThread == None or not infraThread.is_alive()):
            infraThread = thread_util.async(pollVmstatForInfra, 2)
        if serverThread == None or not serverThread.is_alive():
            serverThread = thread_util.async(pollVmstatForServer, 5)
        
        time.sleep(1)

def initPollVmstat():
    global initPollVmstatFlag
    if not initPollVmstatFlag:
        thread_util.async(pollVmstat)
        initPollVmstatFlag = True


# non polling process 
def runVmstatForInfra(vmInterval = 2):
    global infraVmstat
    command = "vmstat -I {} 1".format(vmInterval)
    output = process_util.executeCommandShellWithTimeout(command, 5)
    vmstat = output.split("\n")[-1]
    vmstat = vmstat.split()

    if len(vmstat) < 18:
        logging_util.error("vmstat error command : {}, result : {}".format(command, output))
        return None

    vm = _vmstat()
    vm.user = float(vmstat[14])
    vm.sys = float(vmstat[15])
    vm.idle = float(vmstat[16])
    vm.wait = float(vmstat[17])
    infraVmstat = vm

    return vmstat


def runVmstatForServer(vmInterval = 5):
    global serverVmstat
    command = "vmstat -I {} 1".format(vmInterval)
    output = process_util.executeCommandShellWithTimeout(command, 10)
    vmstat = output.split("\n")[-1]
    vmstat = vmstat.split()

    if len(vmstat) < 18:
        logging_util.error("vmstat error command : {}, result : {}".format(command, output))
        return None

    vm = _vmstat()
    vm.user = float(vmstat[14])
    vm.sys = float(vmstat[15])
    vm.idle = float(vmstat[16])
    vm.wait = float(vmstat[17])
    serverVmstat = vm

    return vmstat

def initRunVmstat():
    global initVmstatFlag
    if not initVmstatFlag:
        try:
            if conf.GetConfig().CategoryV2Enabled:
                thread_util.asyncLoop(runVmstatForInfra, 2)
            thread_util.asyncLoop(runVmstatForServer, 5)
        except Exception, e:
            logging_util.error(e)
    
        initVmstatFlag = True



oldCpu = None
# infra
def GetCpu():
    #initRunVmstat()
    initPollVmstat
    global infraVmstat
    cpu_times = psutil.cpu_times(percpu=False)

    global oldCpu
    if not oldCpu:
        oldCpu = cpu_times
        return None

    c = smbase_pack.CpuLinux()
    #all_cpu = cpu_times.user + cpu_times.iowait + cpu_times.idle + cpu_times.system
    #last_all_cpu = oldCpu.user + oldCpu.iowait + oldCpu.idle + oldCpu.system
    jiff = float(100)
    #cpu_diff = float(all_cpu - last_all_cpu)
    #c.user = jiff * float(cpu_times.user - oldCpu.user) / cpu_diff
    #c.system = jiff * float(cpu_times.system - oldCpu.system) / cpu_diff
    #c.iowait = jiff * float(cpu_times.iowait - oldCpu.iowait) / cpu_diff
    #c.idle = jiff * float(cpu_times.idle - oldCpu.idle) / cpu_diff
    c.user = infraVmstat.user
    c.system = infraVmstat.sys
    c.idle = infraVmstat.idle
    c.iowait = infraVmstat.wait

    c.nice = float(0)
    c.irq = float(0)
    c.softirq = float(0)
    c.steal = float(0)
    c.load1, c.load5, c.load15= psutil.cpu_load()
    oldCpu = cpu_times

    return c

oldCpuList = None
def GetCpuCore():
    cpu_times = psutil.logical_cpu_times(percpu=True)
    jiff = float(100)

    global oldCpuList
    if not oldCpuList:
        oldCpuList = cpu_times
        return None

    cpuPerf = []
    for index, ctimes in enumerate(cpu_times):
        c = smbase_pack.CpuLinux()
        all_cpu = ctimes.user + ctimes.iowait + ctimes.idle + ctimes.system
        last_all_cpu = oldCpuList[index].user + oldCpuList[index].iowait + oldCpuList[index].idle + oldCpuList[index].system
        cpu_diff = float(all_cpu - last_all_cpu)

        if cpu_diff == 0:
            c.user = 0
            c.system = 0
            c.iowait = 0
            c.idle = 0
        else:
            c.user = jiff * float(ctimes.user - oldCpuList[index].user) / cpu_diff
            c.system = jiff * float(ctimes.system - oldCpuList[index].system) / cpu_diff
            c.iowait = jiff * float(ctimes.iowait - oldCpuList[index].iowait) / cpu_diff
            c.idle = jiff * float(ctimes.idle - oldCpuList[index].idle) / cpu_diff

        c.nice = float(0)
        c.irq = float(0)
        c.softirq = float(0)
        c.steal = float(0)

        cpuPerf.append(c) 

    oldCpuList = cpu_times

    return cpuPerf


oldStats = None
def GetCpuStat():
    now = time.time()

    cpuStats = psutil.cpu_stats_detail()

    global oldStats
    if not oldStats:
        oldStats = [cpuStats, now]
        return None

    difftime = float(now - oldStats[1])
    oldCpuStats = oldStats[0]

    cs = CPUStat() 
    cs.ctx = float(cpuStats[0] - oldCpuStats[0]) / difftime
    cs.interrupt = float(cpuStats[1] - oldCpuStats[1]) / difftime
    cs.syscall = float(cpuStats[2] - oldCpuStats[2]) / difftime
    cs.forkCount = float(cpuStats[3] - oldCpuStats[3]) / difftime
    cs.execCount = float(cpuStats[4] - oldCpuStats[4]) / difftime
    cs.runqueue = float(cpuStats[5] - oldCpuStats[5]) / difftime
    cs.blockqueue = float(cpuStats[6] - oldCpuStats[6]) / difftime
    cs.pendingqueue = float(cpuStats[7] - oldCpuStats[7]) / difftime
    cs.waitqueue = CPUStat.UNSUPPORTED

    oldStats = [cpuStats, now]

    return cs

def GetSwapMemory():
    m = smbase_pack.MemoryLinux()
    swapmem = psutil.swap_memory()
    m.swapUsed = swapmem.used
    m.swapTotal = swapmem.total

    if m.swapTotal > 0:
        m.swapPused = swapmem.percent
    else:
        m.swapPused = 0

    return m


oldMemoryStat = None
def GetMemory():
    memory = psutil.virtual_memory_detail()
    now = time.time()

    global oldMemoryStat
    if not oldMemoryStat:
        oldMemoryStat = [memory, now]
        return None


    m = smbase_pack.MemoryLinux()
    m.total = memory[0] # total
    m.free = memory[2] # free
    m.cached = memory[5] #numperm
    m.available = m.free+ m.cached
    m.pavailable = float(m.available) / float(m.total) * float(100.0)
    m.used = m.total - m.available
    m.pused = float(float(m.used)/float(m.total)*100.0)
    m.buffers = 0
    m.shared = 0
    m.pageFault = 0
 
    oldMemory = oldMemoryStat[0]
    difftime = float(now - oldMemoryStat[1])

    m.swapIn = float(memory[6] - oldMemory[6]) / difftime
    m.swapOut = float(memory[7] - oldMemory[7]) / difftime
    m.fileSystemIn = float(memory[8] - oldMemory[8]) / difftime
    m.fileSystemOut = float(memory[9] - oldMemory[9]) / difftime
    m.scannedPage = float(memory[10] - oldMemory[10]) / difftime
    m.freedPage = float(memory[11] - oldMemory[11]) / difftime

    oldMemoryStat = [memory, now]
    return m

oldVmstat = None
def GetCpuMemory():
    #initRunVmstat()
    initPollVmstat()
    global serverVmstat

    cpu_times = psutil.cpu_times(percpu=False)
    global oldVmstat
    if not oldVmstat:
        oldVmstat = cpu_times
        return None, None, None

    c = smbase_pack.CpuLinux()
    #all_cpu = cpu_times.user + cpu_times.iowait + cpu_times.idle + cpu_times.system
    #last_all_cpu = oldVmstat.user + oldVmstat.iowait + oldVmstat.idle + oldVmstat.system
    #cpu_diff = float(all_cpu - last_all_cpu)
    
    c.user = serverVmstat.user
    c.system = serverVmstat.sys
    c.idle = serverVmstat.idle
    c.iowait = serverVmstat.wait
    #c.user = float(100) * float(cpu_times.user - oldVmstat.user) / cpu_diff
    #c.system = float(100) * float(cpu_times.system - oldVmstat.system) / cpu_diff
    #c.iowait = float(100) * float(cpu_times.iowait - oldVmstat.iowait) / cpu_diff
    #c.idle = float(100) * float(cpu_times.idle - oldVmstat.idle) / cpu_diff
    c.nice = float(0)
    c.irq = float(0)
    c.softirq = float(0)
    c.steal = float(0)
    c.load1, c.load5, c.load15= psutil.cpu_load()
    oldVmstat = cpu_times

    

    p = smbase_pack.MemoryLinux()
    memstat = getMemStat()
    p.total = memstat['total']
    p.free = memstat['free']  
    p.cached = memstat['cached']
    p.available = p.total - p.cached- p.free
    p.pavailable = float(p.available) / float(p.total) * float(100.0)
    p.used = memstat['used']
    p.pused = float(p.used) / float(p.total) * float(100.0)
       
    p.buffers = 0
    p.shared = 0

    swapmem = psutil.swap_memory()
    p.swapUsed = swapmem.used
    p.swapTotal = swapmem.total
    if p.swapTotal > 0:
        p.swapPused = swapmem.percent
    else:
        p.swapPused = 0

    p.pageFault = 0

    uptime= int(time.time()) - int(psutil.boot_time())
    #print 'cpu_memory', int(time.time()) , int(vmstat['boot time']/1024.0), uptime
    return c, p, uptime

g_memstat = {"total":1,"cached":0,"used":0,"free":0}

def getMemStat():
    global g_memstat

    return g_memstat


import svmon_parser
def getSVMon(pagesize = psutil._psutil_posix.getpagesize(), command="svmon -G"):
    started=time.time()*1000
    process = subprocess.Popen(shlex.split(command), stdout=subprocess.PIPE)
    
    lines = ""
    while True:
        output = process.stdout.readline()
        #print 'getVmStat', output
        if not output and process.poll() is not None:
            break
        if output:
            lines += output

    svmon_output = svmon_parser.parse_svmon_output(lines)
    memory_paragraph = svmon_output["memory"]
    pininuse_paragraph = svmon_output["pin_in_use"]
    inuse_paragraph = svmon_output["in_use"]
    mem_total = memory_paragraph["size"] * pagesize
    mem_free = memory_paragraph["free"] * pagesize
    mem_cached = inuse_paragraph['clnt'] * pagesize
    mem_used = inuse_paragraph['inuse'] * pagesize

    return {"total":mem_total,"cached":mem_cached,"used":mem_used,"free":mem_free}

def updateMemstat():
    global g_memstat
    global memstat_keys
    
    memstat_thistime = getSVMon()
    
    g_memstat = memstat_thistime

    return g_memstat


try:
    updateMemstat()
    thread_util.asyncLoop(updateMemstat, conf.GetConfig().MemstatInterval)
except Exception, e:
    print(e)


