import os
import time


class RotatingLog:
    """
    An `open()` replacement that will automatically open and write
    to a new file if the prior file is too large or after a time interval.
    """

    def __init__(self, path="./log_file", hourInterval=24, megabyteLimit=1024):
        """
        Args:
            path: a full or partial path with file name.
            hourInterval: the number of hours at which to rotate the file.
            megabyteLimit: the number of megabytes of file size the log may
                grow to, after which the log is rotated.  Note: The log file
                may get a bit larger than limit do to writing out whole lines
                (last line may exceed megabyteLimit or "megabyteGuidline").
        """
        self.path = path
        self.timeInterval = None
        self.timeLimit = None
        self.sizeLimit = None
        if hourInterval is not None:
            self.timeInterval = hourInterval*60*60
            self.timeLimit = time.time()+self.timeInterval
        if megabyteLimit is not None:
            self.sizeLimit = megabyteLimit*1024*1024

    def __del__(self):
        self.close()

    def close(self):
        if hasattr(self, "file"):
            self.file.flush()
            self.file.close()
            self.closed = self.file.closed
            del self.file
        else:
            self.closed = 1

    def shouldRotate(self):
        """
        Returns a bool about whether a new log file should
        be created and written to (while at the same time
        stopping output to the old log file and closing it).
        """
        if not hasattr(self, "file"):
            return 1
        if self.timeLimit is not None and time.time() > self.timeLimit:
            return 1
        if self.sizeLimit is not None and self.file.tell() > self.sizeLimit:
            return 1
        return 0

    def filePath(self):
        dateString=time.strftime("%Y_%m_%d_%H", time.localtime())
        for i in range(26):
            path="%s_%s_%s.log"%(self.path, dateString, chr(i+97))
            if not os.path.exists(path) or os.stat(path)[6] < self.sizeLimit:
                return path
        # Hmm, 26 files are full?  throw the rest in z:
        # Maybe we should clear the self.sizeLimit here... maybe.
        return path

    def rotate(self):
        """
        Rotate the log now.  You normally shouldn't need to call this.
        See write().
        """
        path=self.filePath()
        file=open(path, "a")
        if file:
            self.close()
            # This should be redundant with "a" open() mode,
            # but on some platforms tell() will return 0
            # until the first write:
            file.seek(0, 2)
            self.file=file

            # Some of these data members may be expected by some of our clients:
            self.closed = self.file.closed
            self.mode = self.file.mode
            self.name = self.file.name
            if hasattr(self.file, 'softspace'):
                self.softspace = self.file.softspace
            #self.encoding = self.file.encoding # Python 2.3
            #self.newlines = self.file.newlines # Python 2.3, maybe

            if self.timeLimit is not None and time.time() > self.timeLimit:
                self.timeLimit=time.time()+self.timeInterval
        else:
            # We'll keep writing to the old file, if available.
            print("RotatingLog error: Unable to open new log file \"%s\"." % (path,))

    def write(self, data):
        """
        Write the data to either the current log or a new one,
        depending on the return of shouldRotate() and whether
        the new file can be opened.
        """
        if self.shouldRotate():
            self.rotate()
        if hasattr(self, "file"):
            r = self.file.write(data)
            self.file.flush()
            return r

    def flush(self):
        return self.file.flush()

    def fileno(self):
        return self.file.fileno()

    def isatty(self):
        return self.file.isatty()

    def __next__(self):
        return next(self.file)
    next = __next__

    def read(self, size):
        return self.file.read(size)

    def readline(self, size):
        return self.file.readline(size)

    def readlines(self, sizehint):
        return self.file.readlines(sizehint)

    def xreadlines(self):
        return self.file.xreadlines()

    def seek(self, offset, whence=0):
        return self.file.seek(offset, whence)

    def tell(self):
        return self.file.tell()

    def truncate(self, size):
        return self.file.truncate(size)

    def writelines(self, sequence):
        return self.file.writelines(sequence)
