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

def executeCmd(command='getconf CLK_TCK'):
    process = subprocess.Popen(shlex.split(command), stdout=subprocess.PIPE)
    buf = StringIO()
    while True:
        output = process.stdout.readline()
        if output == '' and process.poll() is not None:
            break
        buf.write(output)

    return buf.getvalue()

tckTime = float(executeCmd())

import multiprocessing

cpucheck = False
cpulist = None
cpucore = 0

def GetCpuInfo():
    global cpucheck
    global cpulist
    global cpucore


    if cpucheck:
        return cpulist, cpucore

    lines = process_util.executeCommandShellWithTimeout('''psrinfo | grep on-line''')

    if lines == "":
        logging_util.error("psrinfo command error")
        cpucore = int(multiprocessing.cpu_count())
        cpulist = range(cpucore)
        cpucheck = True
        return cpulist, cpucore

    cpulist = []
    for line in lines.splitlines():
        parts = line.split()
        if len(parts) < 2:
            continue

        try:
            cpu_id = int(parts[0])
            cpulist.append(cpu_id)
        except ValueError:
            logging_util.error("psrinfo cpu id error")
        cpucore = len(cpulist)
        cpucheck = True
        logging_util.info("CPU List : ", ', '.join(map(str, cpulist)))
    return cpulist, cpucore


pagesize = os.sysconf('SC_PAGE_SIZE')
jiff = 100



oldCpu = None
# infra 
def GetCpu():
    global jiff
    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
    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.nice = float(0)
    c.irq = float(0)
    c.softirq = float(0)
    c.steal = float(0)
    c.load1, c.load5, c.load15= os.getloadavg()
    oldCpu = cpu_times

    return c

oldCpuList = None
def GetCpuCore():
    global jiff
    cpu_times = psutil.cpu_times(percpu=True)

    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()
    totalfork = 0
    totalexec = 0
    totalrunq = 0
    totalblockq = 0
    totalwaitq = 0

    global k
    if not k:
        k = kstat.Kstat()

    cpulist, cpucore = GetCpuInfo()
    for i in cpulist:
        try:
            km = k['cpu', i, 'sys']
            totalfork += km['sysfork']
            totalexec += km['sysexec']
        except KeyError as e:
            logging_util.error("cpu sys key error", e)
        except Exception as e:
            logging_util.error("cpu sys exception", e)

    km = k['unix', 0, 'sysinfo']
    totalrunq = km['runque']
    totalblockq = km['swpque']
    totalwaitq = km['waiting']

    kernelStats = (totalfork, totalexec, totalrunq, totalblockq, totalwaitq)


    returnFlag = False

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


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

    cs = CPUStat() 
    cs.ctx = float(cpuStats.ctx_switches - oldCpuStats.ctx_switches) / difftime
    cs.interrupt = float(cpuStats.interrupts - oldCpuStats.interrupts) / difftime
    cs.syscall = float(cpuStats.syscalls - oldCpuStats.syscalls) / difftime

    cs.forkCount = float(kernelStats[0] - oldKernelStats[0]) / difftime
    cs.execCount = float(kernelStats[1] - oldKernelStats[1]) / difftime
    cs.runqueue = float(kernelStats[2] - oldKernelStats[2]) / difftime
    cs.blockqueue = float(kernelStats[3] - oldKernelStats[3]) / difftime
    cs.waitqueue = float(kernelStats[4] - oldKernelStats[4]) / difftime
    cs.pendingqueue = CPUStat.UNSUPPORTED

    oldStats = [cpuStats, kernelStats, 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():
    global k
    now = time.time()
    m = smbase_pack.MemoryLinux()
    if conf.GetConfig().SunosMdbEnabled:
        memstat = getMemStat()
        m.total = memstat['total']
        m.free = memstat['free_freelist'] 
        m.cached = memstat['zfs_file_data'] + memstat['free_cachelist']
        if 'page_cache' in memstat:
            m.cached += memstat['page_cache']
        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)
    else:
        if k == None:
            k = kstat.Kstat()
        kstat_now = k['unix', 0, 'system_pages']
        m.total = kstat_now['pagestotal'] * pagesize
        m.free = kstat_now['freemem'] * pagesize
        try:
            zfsStats = k['zfs', 0, 'arcstats']
            m.cached = zfsStats['size']
        except KeyError:
            m.cached = 0
        except TypeError:
            m.cached = 0
        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
    
    swapIn = 0
    swapOut = 0
    fileSystemIn = 0
    fileSystemOut = 0
    scannedPage = 0
    freedPage = 0

    cpulist, cpucore = GetCpuInfo()
    for i in cpulist:
        try:
            km = k['cpu', i, 'vm']
            swapIn += km['pgswapin']
            swapOut += km['pgswapout']
            fileSystemIn += km['fspgin']
            fileSystemOut += km['fspgout']
            scannedPage += km['scan']
            freedPage += km['dfree']
        except KeyError as e:
            logging_util.error("cpu vm key error", e)
        except Exception as e:
            logging_util.error("cpu vm exception", e)
    
    memPageStat = (swapIn, swapOut, fileSystemIn, fileSystemOut, scannedPage, freedPage, now)
    global oldMemoryStat
    if not oldMemoryStat:
        m.swapIn = 0
        m.swapOut = 0
        m.fileSystemIn = 0
        m.fileSystemOut = 0
        m.scannedPage = 0
        m.freedPage = 0
    else:
        difftime = float(now - oldMemoryStat[6])
        m.swapIn = float(memPageStat[0] - oldMemoryStat[0]) / difftime
        m.swapOut = float(memPageStat[1] - oldMemoryStat[1]) / difftime
        m.fileSystemIn = float(memPageStat[2] - oldMemoryStat[2]) / difftime
        m.fileSystemOut = float(memPageStat[3] - oldMemoryStat[3]) / difftime
        m.scannedPage = float(memPageStat[4] - oldMemoryStat[4]) / difftime
        m.freedPage = float(memPageStat[5] - oldMemoryStat[5]) / difftime

    m.buffers = 0
    m.shared = 0
    m.pageFault = 0

    oldMemoryStat = memPageStat
    
    return m

oldVmstat = None
k = None
def GetCpuMemory():
    global jiff

    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 = jiff * float(cpu_times.user - oldVmstat.user) / cpu_diff
    c.system = jiff * float(cpu_times.system - oldVmstat.system) / cpu_diff
    c.iowait = jiff * float(cpu_times.iowait - oldVmstat.iowait) / cpu_diff
    c.idle = jiff * 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= os.getloadavg()
    oldVmstat = cpu_times

    p = smbase_pack.MemoryLinux()
    if conf.GetConfig().SunosMdbEnabled:
        memstat = getMemStat()
        p.total = memstat['total']
        p.free = memstat['free_freelist'] 
        p.cached = memstat['zfs_file_data'] + memstat['free_cachelist']
        if 'page_cache' in memstat:
            p.cached += memstat['page_cache']
        p.available = p.free + p.cached
        p.pavailable = float(p.available) / float(p.total) * float(100.0)
        p.used = p.total - p.available
        p.pused = float(float(p.used)/float(p.total)*100.0)
        
    else:
        global k
        if k == None:
            k = kstat.Kstat()
        kstat_now = k['unix', 0, 'system_pages']
        p.total = kstat_now['pagestotal'] * pagesize
        p.free = kstat_now['freemem'] * pagesize
        try:
            zfsStats = k['zfs', 0, 'arcstats']
            p.cached = zfsStats['size']
        except KeyError:
            p.cached = 0
        except TypeError:
            p.cached = 0
        p.available = p.free + p.cached
        p.pavailable = float(p.available) / float(p.total) * float(100.0)
        p.used = p.total - p.available
        p.pused = float(float(p.used)/float(p.total)*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

"""
Solraris Old
Page Summary                 Pages             Bytes  %Tot
----------------- ----------------  ----------------  ----
Kernel                      162541              1.2G   32%
Guest                            0                 0    0%
ZFS Metadata                 31639            247.1M    6%
ZFS File Data                33225            259.5M    7%
Anon                         64650            505.0M   13%
Exec and libs                 1323             10.3M    0%
Page cache                    7006             54.7M    1%
Free (cachelist)                 1                8k    0%
Free (freelist)             207519              1.5G   41%
Total                       507904              3.8G
"""



"""
Usage Type/Subtype                      Pages    Bytes  %Tot  %Tot/%Subt
---------------------------- ---------------- -------- ----- -----------
Kernel                                 154062     1.1g 14.9%
ZFS                                    389508     2.9g 37.7%
User/Anon                               55774   435.7m  5.4%
Exec and libs                            1287    10.0m  0.1%
Page Cache                               6844    53.4m  0.6%
Free (cachelist)                        20852   162.9m  2.0%
Free                                   403865     3.0g 39.1%
Total                                 1032192     7.8g  100%

"""


memstat_keys = {"Kernel":"kernel",
    "Guest":"guest",
    "ZFS Metadata":"zfs_metadata",
    "ZFS File Data":"zfs_file_data",
    "Anon":"anon",
    "Exec and libs":"exec_and_libs",
    "Page cache":"page_cache",
    "Free (cachelist)":"free_cachelist",
    "Free (freelist)":"free_freelist",
    "Total":"total",
    "ZFS":"zfs_file_data",
    "User/Anon":"anon",
    "Free":"free_freelist",
}


memstats_required_keys = ["total", "free_cachelist", 'free_freelist', 'zfs_file_data']

def parseMemstat(output):
    global memstat_keys
    global pagesize

    if not output:
        return None, None
    for k, v in memstat_keys.items():
        if output.startswith(k):
            line = output.strip(k)
            line = line.strip()
            page = line.split()[0]
            try:
                return v, int(page) * pagesize
            except:
                continue
    return None, None

g_memstat = {"kernel":0,"guest":0,"zfs_metadata":0,"zfs_file_data":0,"anon":0, "exec_and_libs":0, "page_cache":0, "free_cachelist":0, "free_freelist":0, "total":1}

def getMemStat():
    global g_memstat

    return g_memstat

def _getMemStat(command="mdb -k", parser = parseMemstat):
    started=time.time()*1000
    process = subprocess.Popen(shlex.split(command), stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    memoryInfo = {'created': started}
    process.stdin.write('::memstat\n')
    process.stdin.write('::quit\n')
    process.stdin.close()
    endCheck = False
    
    while process.poll() == None:
        output = process.stdout.readline().strip()
        if not output:
            if not endCheck:
                endCheck = True
                continue
            break
        if output:
            #print('getVmStat', output)
            k, v = parser(output)
            if k != None and v != None:
                memoryInfo[k]= v
    logging_util.debug(command, process.stderr.read())
    
    try:
        process.kill()
    except:
        pass

    return memoryInfo

def updateMemstat():
    global g_memstat
    global memstat_keys
    if conf.GetConfig().SunosMdbEnabled:
        memstat_thistime = _getMemStat()
        for required in memstats_required_keys:
            if required not in memstat_thistime:
                return g_memstat
        g_memstat = memstat_thistime
    return g_memstat

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

def getVmStat(command="vmstat -s"):
    started=time.time()*1000
    process = subprocess.Popen(shlex.split(command), stdout=subprocess.PIPE)
    memoryInfo = {'created': started}
    while True:
        output = process.stdout.readline().strip()
        #print 'getVmStat', output
        if not output and process.poll() is not None:
            break
        if output:
            #print 'getVmStat', output
            tokens = output.split()
            v = tokens[0]
            key = ' '.join(tokens[1:])
            memoryInfo[key]= int(v)*1024

    return memoryInfo

def test():
    while True:
        c, p, t = GetCpuMemory()
        if p:
            print('pavailable:', p.pavailable)
        
        time.sleep(5)
    

if __name__ == '__main__':
    test()
