# SPDX-License-Identifier: BSD-3-Clause
#
# Copyright 2025 Raritan Inc. All rights reserved.
#
# This is an auto-generated file.

#
# Section generated by IdlC from "Log.idl"
#

import raritan.rpc
from raritan.rpc import Interface, Structure, ValueObject, Enumeration, typecheck, DecodeException
import raritan.rpc.logging


# structure
class LogInfo(Structure):
    idlType = "logging.LogInfo:1.0.0"
    elements = ["creationTime", "idFirst", "idNext"]

    def __init__(self, creationTime=0, idFirst=0, idNext=0):
        typecheck.is_long(creationTime, AssertionError)
        typecheck.is_int(idFirst, AssertionError)
        typecheck.is_int(idNext, AssertionError)

        self.creationTime = creationTime
        self.idFirst = idFirst
        self.idNext = idNext

    @classmethod
    def decode(cls, json, agent, useDefaults=False):
        obj = cls(
            creationTime = int(json['creationTime']) if 'creationTime' in json or not useDefaults else 0,
            idFirst = json['idFirst'] if 'idFirst' in json or not useDefaults else 0,
            idNext = json['idNext'] if 'idNext' in json or not useDefaults else 0,
        )
        return obj

    def encode(self):
        json = {}
        json['creationTime'] = self.creationTime
        json['idFirst'] = self.idFirst
        json['idNext'] = self.idNext
        return json

# structure
class LogEntry(Structure):
    idlType = "logging.LogEntry:1.0.0"
    elements = ["id", "timestamp", "eventClass", "message"]

    def __init__(self, id=0, timestamp=None, eventClass="", message=""):
        typecheck.is_int(id, AssertionError)
        typecheck.is_time(timestamp, AssertionError)
        if not typecheck._is_int(eventClass):
            typecheck.is_string(eventClass, AssertionError)
        if not typecheck._is_int(message):
            typecheck.is_string(message, AssertionError)

        self.id = id
        self.timestamp = timestamp
        self.eventClass = eventClass
        self.message = message

    @classmethod
    def decode(cls, json, agent, useDefaults=False):
        obj = cls(
            id = json['id'] if 'id' in json or not useDefaults else 0,
            timestamp = raritan.rpc.Time.decode(json['timestamp']) if 'timestamp' in json or not useDefaults else None,
            eventClass = json['eventClass'] if 'eventClass' in json or not useDefaults else "",
            message = json['message'] if 'message' in json or not useDefaults else "",
        )
        return obj

    def encode(self):
        json = {}
        json['id'] = self.id
        json['timestamp'] = raritan.rpc.Time.encode(self.timestamp)
        json['eventClass'] = str(self.eventClass)
        json['message'] = str(self.message)
        return json

# structure
class LogChunk(Structure):
    idlType = "logging.LogChunk:1.0.0"
    elements = ["logCreationTime", "idFirst", "allEntryCnt", "selEntries"]

    def __init__(self, logCreationTime=0, idFirst=0, allEntryCnt=0, selEntries=[]):
        typecheck.is_long(logCreationTime, AssertionError)
        typecheck.is_int(idFirst, AssertionError)
        typecheck.is_int(allEntryCnt, AssertionError)
        for x0 in selEntries:
            typecheck.is_struct(x0, raritan.rpc.logging.LogEntry, AssertionError)

        self.logCreationTime = logCreationTime
        self.idFirst = idFirst
        self.allEntryCnt = allEntryCnt
        self.selEntries = selEntries

    @classmethod
    def decode(cls, json, agent, useDefaults=False):
        obj = cls(
            logCreationTime = int(json['logCreationTime']) if 'logCreationTime' in json or not useDefaults else 0,
            idFirst = json['idFirst'] if 'idFirst' in json or not useDefaults else 0,
            allEntryCnt = json['allEntryCnt'] if 'allEntryCnt' in json or not useDefaults else 0,
            selEntries = [raritan.rpc.logging.LogEntry.decode(x0, agent, useDefaults=useDefaults) for x0 in json['selEntries']] if 'selEntries' in json or not useDefaults else [],
        )
        return obj

    def encode(self):
        json = {}
        json['logCreationTime'] = self.logCreationTime
        json['idFirst'] = self.idFirst
        json['allEntryCnt'] = self.allEntryCnt
        json['selEntries'] = [raritan.rpc.logging.LogEntry.encode(x0) for x0 in self.selEntries]
        return json

# enumeration
class RangeDirection(Enumeration):
    idlType = "logging.RangeDirection:1.0.0"
    values = ["FORWARD", "BACKWARD"]

RangeDirection.FORWARD = RangeDirection(0)
RangeDirection.BACKWARD = RangeDirection(1)

#
# Section generated by IdlC from "DebugLog.idl"
#

import raritan.rpc
from raritan.rpc import Interface, Structure, ValueObject, Enumeration, typecheck, DecodeException
import raritan.rpc.logging


# interface
class DebugLog(Interface):
    idlType = "logging.DebugLog:2.0.0"

    class _clear(Interface.Method):
        name = 'clear'

        @staticmethod
        def encode():
            args = {}
            return args

        @staticmethod
        def decode(rsp, agent, useDefaults=False):
            return None

    class _getInfo(Interface.Method):
        name = 'getInfo'

        @staticmethod
        def encode():
            args = {}
            return args

        @staticmethod
        def decode(rsp, agent, useDefaults=False):
            _ret_ = raritan.rpc.logging.LogInfo.decode(rsp['_ret_'], agent, useDefaults=useDefaults)
            typecheck.is_struct(_ret_, raritan.rpc.logging.LogInfo, DecodeException)
            return _ret_

    class _getChunk(Interface.Method):
        name = 'getChunk'

        @staticmethod
        def encode(refId, count, direction):
            typecheck.is_int(refId, AssertionError)
            typecheck.is_int(count, AssertionError)
            typecheck.is_enum(direction, raritan.rpc.logging.RangeDirection, AssertionError)
            args = {}
            args['refId'] = refId
            args['count'] = count
            args['direction'] = raritan.rpc.logging.RangeDirection.encode(direction)
            return args

        @staticmethod
        def decode(rsp, agent, useDefaults=False):
            _ret_ = raritan.rpc.logging.LogChunk.decode(rsp['_ret_'], agent, useDefaults=useDefaults)
            typecheck.is_struct(_ret_, raritan.rpc.logging.LogChunk, DecodeException)
            return _ret_
    def __init__(self, target, agent):
        super(DebugLog, self).__init__(target, agent)
        self.clear = DebugLog._clear(self)
        self.getInfo = DebugLog._getInfo(self)
        self.getChunk = DebugLog._getChunk(self)

#
# Section generated by IdlC from "EventLog.idl"
#

import raritan.rpc
from raritan.rpc import Interface, Structure, ValueObject, Enumeration, typecheck, DecodeException
import raritan.rpc.event

import raritan.rpc.logging


# value object
class EventLogClearedEvent(raritan.rpc.event.UserEvent):
    idlType = "logging.EventLogClearedEvent:1.0.0"

    def __init__(self, actUserName="", actIpAddr="", source=None):
        super(raritan.rpc.logging.EventLogClearedEvent, self).__init__(actUserName, actIpAddr, source)

    def encode(self):
        json = super(raritan.rpc.logging.EventLogClearedEvent, self).encode()
        return json

    @classmethod
    def decode(cls, json, agent, useDefaults=False):
        obj = cls(
            # for event.UserEvent
            actUserName = json['actUserName'] if 'actUserName' in json or not useDefaults else "",
            actIpAddr = json['actIpAddr'] if 'actIpAddr' in json or not useDefaults else "",
            # for idl.Event
            source = Interface.decode(json['source'], agent) if 'source' in json or not useDefaults else None,
        )
        return obj

    def listElements(self):
        elements = []
        elements = elements + super(raritan.rpc.logging.EventLogClearedEvent, self).listElements()
        return elements

# interface
class EventLog(Interface):
    idlType = "logging.EventLog:2.0.0"

    class _clear(Interface.Method):
        name = 'clear'

        @staticmethod
        def encode():
            args = {}
            return args

        @staticmethod
        def decode(rsp, agent, useDefaults=False):
            return None

    class _getInfo(Interface.Method):
        name = 'getInfo'

        @staticmethod
        def encode():
            args = {}
            return args

        @staticmethod
        def decode(rsp, agent, useDefaults=False):
            _ret_ = raritan.rpc.logging.LogInfo.decode(rsp['_ret_'], agent, useDefaults=useDefaults)
            typecheck.is_struct(_ret_, raritan.rpc.logging.LogInfo, DecodeException)
            return _ret_

    class _getChunk(Interface.Method):
        name = 'getChunk'

        @staticmethod
        def encode(refId, count, direction, categories):
            typecheck.is_int(refId, AssertionError)
            typecheck.is_int(count, AssertionError)
            typecheck.is_enum(direction, raritan.rpc.logging.RangeDirection, AssertionError)
            for x0 in categories:
                if not typecheck._is_int(x0):
                    typecheck.is_string(x0, AssertionError)
            args = {}
            args['refId'] = refId
            args['count'] = count
            args['direction'] = raritan.rpc.logging.RangeDirection.encode(direction)
            args['categories'] = [str(x0) for x0 in categories]
            return args

        @staticmethod
        def decode(rsp, agent, useDefaults=False):
            _ret_ = raritan.rpc.logging.LogChunk.decode(rsp['_ret_'], agent, useDefaults=useDefaults)
            typecheck.is_struct(_ret_, raritan.rpc.logging.LogChunk, DecodeException)
            return _ret_
    def __init__(self, target, agent):
        super(EventLog, self).__init__(target, agent)
        self.clear = EventLog._clear(self)
        self.getInfo = EventLog._getInfo(self)
        self.getChunk = EventLog._getChunk(self)

#
# Section generated by IdlC from "WlanLog.idl"
#

import raritan.rpc
from raritan.rpc import Interface, Structure, ValueObject, Enumeration, typecheck, DecodeException
import raritan.rpc.logging


# interface
class WlanLog(Interface):
    idlType = "logging.WlanLog:1.0.0"

    class _clear(Interface.Method):
        name = 'clear'

        @staticmethod
        def encode():
            args = {}
            return args

        @staticmethod
        def decode(rsp, agent, useDefaults=False):
            return None

    class _getInfo(Interface.Method):
        name = 'getInfo'

        @staticmethod
        def encode():
            args = {}
            return args

        @staticmethod
        def decode(rsp, agent, useDefaults=False):
            _ret_ = raritan.rpc.logging.LogInfo.decode(rsp['_ret_'], agent, useDefaults=useDefaults)
            typecheck.is_struct(_ret_, raritan.rpc.logging.LogInfo, DecodeException)
            return _ret_

    class _getChunk(Interface.Method):
        name = 'getChunk'

        @staticmethod
        def encode(refId, count, direction):
            typecheck.is_int(refId, AssertionError)
            typecheck.is_int(count, AssertionError)
            typecheck.is_enum(direction, raritan.rpc.logging.RangeDirection, AssertionError)
            args = {}
            args['refId'] = refId
            args['count'] = count
            args['direction'] = raritan.rpc.logging.RangeDirection.encode(direction)
            return args

        @staticmethod
        def decode(rsp, agent, useDefaults=False):
            _ret_ = raritan.rpc.logging.LogChunk.decode(rsp['_ret_'], agent, useDefaults=useDefaults)
            typecheck.is_struct(_ret_, raritan.rpc.logging.LogChunk, DecodeException)
            return _ret_
    def __init__(self, target, agent):
        super(WlanLog, self).__init__(target, agent)
        self.clear = WlanLog._clear(self)
        self.getInfo = WlanLog._getInfo(self)
        self.getChunk = WlanLog._getChunk(self)

# from raritan/rpc/logging/__extend__.py
import datetime
import raritan.rpc.logging

class LogHelper():

    error_tag = "[ERR]"
    warning_tag = "[WRN]"

    def __init__(self, agent, logtype):
        self.agent = agent
        self.logtype = logtype
        entries, self.nextid, self.creationtime = self.get_entries(self.agent, self.logtype)
        self.last_entry = entries[-1] if entries else None

    def get_next_entries(self):
        entries, self.nextid, creationtime = self.get_entries(self.agent, self.logtype, self.nextid, self.creationtime)
        if creationtime != self.creationtime:
            self.creationtime = creationtime
            if self.last_entry:
                # this is needed for following the event log over logd restarts;
                # the restart resets chunk timestamps but doesn't reset id numbering, so we'll get the whole log again
                last_entry_index = next((i for i in reversed(range(len(entries))) if entries[i] == self.last_entry), None)
                if last_entry_index is not None:
                    entries = entries[last_entry_index+1:]
        if entries:
            self.last_entry = entries[-1]
        return entries

    @staticmethod
    def log_proxy(agent, log_type):
        if log_type == "debug":
            return raritan.rpc.logging.DebugLog("/debuglog", agent)
        elif log_type == "event":
            return raritan.rpc.logging.EventLog("/eventlog", agent)
        elif log_type == "wlan":
            return raritan.rpc.logging.WlanLog("/wlanlog", agent)
        else:
            raise ValueError("Not a log type: %s" % log_type)

    @staticmethod
    def format_entry(entry, tz=None):
        timefmt = "%m/%d/%Y %H:%M:%S %Z"
        timestr = entry.timestamp.replace(tzinfo=datetime.timezone.utc).astimezone(tz).strftime(timefmt)
        return "[%s]:%s" % (timestr, entry.message)

    @classmethod
    def get_entries(cls, agent, log_type, ref_id=None, creation_time=None):
        log = cls.log_proxy(agent, log_type)
        info = log.getInfo()
        # remember most recent entry at this point,
        # ignore entries logged after it to circumvent infinite loop
        next_id = info.idNext
        if ref_id is None:
            ref_id = info.idFirst
        if creation_time is None:
            creation_time = info.creationTime
        entries = []

        while(True):
            # count -1 means all entries of the chunk
            args = [ref_id, -1, raritan.rpc.logging.RangeDirection.FORWARD]
            if log_type == "event":
                # argument for event categories filtering required
                args.append([])
            chunk = log.getChunk(*args)
            if chunk.logCreationTime != creation_time:
                # log timestamp was reset, begin from start
                info = log.getInfo()
                next_id = info.idNext
                ref_id = info.idFirst
                creation_time = info.creationTime
                continue
            if chunk.allEntryCnt == 0:
                # reached end of log
                break
            if chunk.idFirst + chunk.allEntryCnt >= next_id:
                # chunk contains our most recent entry
                entries += [entry for entry in chunk.selEntries if entry.id < next_id]
                ref_id = next_id
                break
            entries += chunk.selEntries
            ref_id = chunk.idFirst + chunk.allEntryCnt

        return entries, ref_id, creation_time

    @classmethod
    def filter_entries(cls, entries, search_strings=None):
        if not search_strings:
            search_strings = [cls.error_tag, cls.warning_tag]
        return [entry for entry in entries if any(string in entry.message for string in search_strings)]
