import threading, time, os, re

import whatap.agent.conf.configure as config
from StringIO import StringIO
from datetime import datetime
import glob
import whatap.util.logging_util as logging_util
from whatap.util.date_util import DateUtil


start = False
lock = threading.Lock()

def initFileLogWatcher():
    global start
    if not start :
        lock.acquire()
        if not start :
            start = True
            t = threading.Thread(target=checkFileLogConfig)
            t.daemon = True
            t.start()
            t = threading.Thread(target=startTail)
            t.daemon = True
            t.start()
        lock.release()

def parseLogConfig(logConfig ):
    words = logConfig.split(",")
    #print 'parseLogConfig step -1', logConfig
    word_count = len(words)
    if word_count < 3:
        return None, None, None
    severity = int(words[0])
    logfile = words[1]
    regexPattern = words[2]
    regexExcludePattern = None
    duration = None
    repeat = None
    if word_count > 3:
        duration = int(words[3])
    if word_count > 4:
        repeat = int(words[4])
    if word_count > 5:
        regexExcludePattern = words[5]

    return int(severity), logfile, regexPattern, duration, repeat, regexExcludePattern

class LogCondition(object):
    def __init__(self, regexPattern = None, regexExcludePattern = None, severity = None, triggerId=None, duration = None, repeat = None):
        self.regexPattern = regexPattern
        self.regexExcludePattern = regexExcludePattern
        self.severity = severity
        self.triggerId = triggerId
        self.duration = duration
        self.repeat = repeat

    def clone(self):
        return LogCondition(regexPattern = self.regexPattern, regexExcludePattern = self.regexExcludePattern, severity = self.severity, triggerId=self.triggerId, duration = self.duration, repeat = self.repeat)

    def  __str__(self):
        buf = StringIO()
        buf.write('regexPattern=')
        buf.write(self.regexPattern)
        buf.write('regexExcludePattern=')
        buf.write(self.regexExcludePattern)
        buf.write('severity=')
        buf.write(self.severity)
        buf.write('triggerId=')
        buf.write(self.triggerId)
        buf.write('duration=')
        buf.write(self.duration)
        buf.write('repeat=')
        buf.write(self.repeat)

class LogStatus:
    def __init__(self, filePos = 0, filePath = None, realFilePath = None, logConditions = None, fileInfo = None, watchCount = None):
        self.filePos  = filePos
        self.filePath = filePath
        self.realFilePath = realFilePath
        self.logConditions = logConditions
        self.watchCount  = watchCount
        self.fileInfo = fileInfo
        self.realFileCheckIdx = 0

    def clone(self):
        clonedLogConditions = {}
        if self.logConditions:
            clonedLogConditions = { k : v.clone() for k, v in self.logConditions.items()}

        l = LogStatus(filePos = self.filePos, filePath = self.filePath, realFilePath = self.realFilePath, logConditions= clonedLogConditions, fileInfo = self.fileInfo, watchCount = self.watchCount)
        return l

    def __str__(self):
        buf = StringIO()
        buf.write('filePos=')
        buf.write(self.filePos)
        buf.write('\nfilePath=')
        buf.write(self.filePath)
        buf.write('\nrealFilePath=')
        buf.write(self.realFilePath)
        if self.logConditions:
            for k, v in self.logConditions.items():
                buf.write(k)
                buf.write('=LogCondition')
                buf.write(v)

        buf.write('\nwatchCount=')
        buf.write(self.watchCount)
        buf.write('\nfileInfo=')
        buf.write(self.fileInfo)

        return buf.getvalue()

class LogEventCount(object):
    def __init__(self, count = 0, firstOccured = None):
        self.count = count
        self.firstOccured = firstOccured

    def __str__(self):
        buf = StringIO()
        buf.write("count=")
        buf.write(self.count)
        buf.write("firstOccured=")
        buf.write(self.firstOccured)

        return buf.getvalue()

logStatus = {}
logMatches = {}
logCounter = {}
loglock = threading.Lock()

def addLogMatch(logfile , reallogfile , logline, logCondition ):
    if logfile not in logMatches:
        logMatches[logfile] = []
    logMatches[logfile].append((reallogfile, logline, logCondition))

class LogEvent:
    def __init__(self, filePath = None, logContent = None, severity = None, triggerId = None, target=None, keyword = None):
        self.filePath = filePath
        self.logContent = logContent
        self.severity = severity
        self.triggerId = triggerId
        self.target = target
        self.keyword = keyword

    def __str__(self):
        buf = StringIO()
        buf.write("filePath=")
        buf.write(self.filePath)
        buf.write("\nlogContent=")
        buf.write(self.logContent)
        buf.write("\nseverity=")
        buf.write(self.severity)
        buf.write("\ntriggerId=")
        buf.write(self.triggerId)
        buf.write("\ntarget=")
        buf.write(self.target)
        buf.write("\nkeyword=")
        buf.write(self.keyword)

        return buf.getvalue()

def getNewLogEvents():
    global loglock
    global logMatches
    global logStatus
    loglock.acquire()
    try:
        logevents=[]
        #print('getNewLogEvents step -1')
        for logfile, logmatches in logMatches.items():
            #print('getNewLogEvents step -2')
            for (reallogfile, logline, logCondition) in logmatches:
                #print('getNewLogEvents step -3')
                if logCondition.regexExcludePattern:
                    target = logCondition.regexPattern + " e:" + logCondition.regexExcludePattern
                else:
                    target = logCondition.regexPattern
                #print('getNewLogEvents step -4')
                logevents.append(LogEvent(filePath= reallogfile, logContent= logline, severity= logCondition.severity,\
                                          triggerId= logCondition.triggerId, target= target, keyword = logCondition.regexPattern))

            del logMatches[logfile]
        #print('getNewLogEvents step -5', logevents)
        return logevents
    finally:
        loglock.release()

def startTail():
    logStatusClone = {}
    i =0
    while True:
        i += 1
        t = datetime.now()

        for v in logStatus.values():
            evaluatedPath = t.strftime(v.filePath)
            #print('startTail step -2')
            matches = glob.glob(evaluatedPath)
            for evaluatedPath in matches:
                if evaluatedPath not in logStatusClone:
                    vv = v.clone()
                    vv.realFilePath = evaluatedPath
                    logStatusClone[evaluatedPath] = vv
                else:
                    logStatusClone[evaluatedPath].logConditions = v.clone().logConditions
                logStatusClone[evaluatedPath].realFileCheckIdx = i
        #print('startTail step -3')
        obsoleted = []
        for k, v in logStatusClone.items():
            if v.realFileCheckIdx != i:
                obsoleted.append(k)
        for k in obsoleted:
            del logStatusClone[k]
        #print('startTail step -4')
        for v in logStatusClone.values():
            logfile = v.realFilePath

            try:
                f = open(logfile)
            except IOError, e:
                continue
            #print('startTail step -5')
            fi = os.stat(logfile)

            if i == 0 or not v.fileInfo:
                v.fileInfo = fi
                f.seek(0, 2)
                v.filePos = f.tell()
                f.close()
                continue
            #print('startTail step -6')
            if fi.st_ino == v.fileInfo.st_ino:
                v.fileInfo = fi
                if v.filePos == fi.st_size:
                    f.close()
                    continue
            else:
                v.fileInfo = fi
                v.filePos = 0
            #print('startTail step -7')
            f.seek(0, 2)
            pos = f.tell()

            if v.filePos < pos:
                #print('startTail step -8')
                nbyteleft = pos - v.filePos
                f.seek(v.filePos, 0)
                pos = watchInc(f, v, nbyteleft)
                v.filePos = pos

            elif v.filePos > pos:
                nbyteleft = pos
                f.seek(0, 0)
                #print('startTail step -9')
                v.filePos = watchInc(f, v, nbyteleft)

            if f:
                f.close()
        #print('startTail step -10')
        time.sleep(1)
    


def watchInc(f , logrule , limit):
    lineLimit = config.GetConfig().LogLineLimit
    lineCount = 0
    while lineCount < lineLimit:
        lineCount+=1
        curr_position = f.tell()
        #print 'watchInc step -1'
        line = f.readline()
        if not line:
            break
        #print 'watchInc step -1', line
        for logCondition in logrule.logConditions.values():
            regexpatterncompile = None
            try:
                if logCondition.regexPattern:
                    regexpatterncompile = re.compile(logCondition.regexPattern)
            except Exception ,e:
                logging_util.debugStack(e)
                continue
            regexexcludepatterncompile = None
            try:
                if logCondition.regexExcludePattern:
                    regexexcludepatterncompile = re.compile(logCondition.regexExcludePattern)
            except Exception ,e:
                logging_util.debugStack(e)
                continue

            if not line:
                f.seek(curr_position,0)
                break
            else:
                #print('watchInc step -2')
                if not regexpatterncompile or len(regexpatterncompile.findall(line)) > 0:
                    if regexexcludepatterncompile and len(regexexcludepatterncompile.findall(line)) > 0:
                        time.sleep(0.001)
                        continue
                    #print('watchInc step -4')
                    logCounterKey = f.name + "\\" + logCondition.regexPattern
                    now = DateUtil.now()
                    logEventCount = logCounter.get(logCounterKey)
                    if logEventCount:
                        logging_util.debug("duration Check ", logCounterKey, " time from first occured:",
                                    now - logEventCount.firstOccured, " duration:", (logCondition.duration * 1000))
                    if not logEventCount or now-logEventCount.firstOccured > (logCondition.duration * 1000):
                        logEventCount = LogEventCount(firstOccured= now)
                        logCounter[logCounterKey] = logEventCount
                        logging_util.debug("log match created ", logCounterKey)
                    #print ('watchInc step -5')
                    logEventCount.count +=1
                    logCounter[logCounterKey] = logEventCount
                    logging_util.debug("log ", logCounterKey, " ", logEventCount.count, "/", logCondition.repeat)
                    logging_util.debug("log repeat condition ", logEventCount.count == logCondition.repeat,
                                    now - logEventCount.firstOccured <= (logCondition.duration * 1000))
                    if logEventCount.count == logCondition.repeat and \
                        now - logEventCount.firstOccured <= (logCondition.duration * 1000):
                        addLogMatch(logrule.filePath, logrule.realFilePath, line, logCondition)

        time.sleep(0.001)

    pos = f.tell()

    return pos

logStatus = {}
logMatches = {}


def __checkFileLogConfig():
    global logStatus
    global logMatches
    logStatusClone = {}
    for k, v in logStatus.items():
        logStatusClone[k] = v.clone()
    logfiles = config.GetConfig().searchKey(config.LogFileWatch)
    for key, logfilerule in logfiles.items():
        #print("checkFileLogConfig ", key, logfilerule)
        if len(key.split('.')) < 4:
            continue
        triggerId, triggerHash = key.split(".")[2:4]
        if logfilerule:
            severity, logfile, regexpattern, duration, repeat, regexexcludepattern = parseLogConfig(logfilerule)
            if logfile in logStatusClone:
                logConditions = logStatusClone[logfile].logConditions
                if triggerHash in logConditions:
                    del logConditions[triggerHash]
                logStatusClone[logfile].logConditions = logConditions
                if not logStatusClone[logfile].logConditions:
                    del logStatusClone[logfile ]
            logCondition = LogCondition(regexPattern= regexpattern,regexExcludePattern=  regexexcludepattern,\
                                   severity= severity,triggerId= triggerId, duration= duration, repeat= repeat)
            if logfile not in logStatus:
                logConditions = {}
                logStatus[logfile] = LogStatus(filePos= -1, filePath= logfile, logConditions= logConditions)

            logStatus[logfile].logConditions[triggerHash] = logCondition

    for logfile in logStatusClone.keys() :
        if logStatusClone[logfile].logConditions:
            for triggerHash in logStatusClone[logfile].logConditions :
                del logStatus[logfile].logConditions[triggerHash]
        else:
            del logStatus[logfile]

def checkFileLogConfig():
    global loglock
    while True:
        try:
            loglock.acquire()
            __checkFileLogConfig()
        except Exception, e:
            logging_util.debugStack(e)
        finally:
            loglock.release()
        time.sleep(5)


def test():
    initFileLogWatcher()
    while True:
        print getNewLogEvents()
        time.sleep(1)

if __name__ == '__main__':
    test()
