# Written by Jie Yang
# see LICENSE.txt for license information

import sys
import os
import time
import socket
from traceback import print_exc
            
DEBUG = False

log_separator = ' '
logger = None

# To be compatible with Logger from http://linux.duke.edu/projects/mini/logger/ 
# for 2fastbt (revision <=825). 
def create_logger(file_name):
    global logger

    logger = Logger(3, file_name) 


def get_logger():    
    global logger

    if logger is None:
        create_logger("global.log")
        
    return logger

def get_today():    # UTC based
    return time.gmtime(time.time())[:3]

class Logger:
    """
    Atrributes (defulat value):
      threshold (): message will not be logged if its output_level is bigger 
                     than this threshould
      file_name (): log file name
      file_dir ('.'): diectory of log file. It can be absolute or relative path.
      prefix (''): prefix of log file
      prefix_date (False): if it is True, insert 'YYYYMMDD-' between prefix 
                     and file_name, e.g., sp-20060302-buddycast.log given 
                     prefix = 'sp-' and file_name = 'buddycast.log'
      open_mode ('a+b'): mode for open.
    """
    
    def __init__(self, threshold, file_name, file_dir = '.', prefix = '', 
                 prefix_date = False, open_mode = 'a+b'):
        
        self.threshold = threshold            
        self.Log = self.log
        if file_name == '':
            self.logfile = sys.stderr
        else:
            try:
                if not os.access(file_dir, os.F_OK):
                    try: 
                        os.mkdir(file_dir)
                    except os.error, msg:
                        raise "logger: mkdir error: " + msg
                file_path = self.get_file_path(file_dir, prefix,
                                               prefix_date, file_name)
                self.logfile = open(file_path, open_mode)
            except Exception, msg:
                self.logfile = None
                print >> sys.stderr, "logger: cannot open log file", \
                         file_name, file_dir, prefix, prefix_date, msg
                print_exc() 
                
    def __del__(self):
        self.close()
        
    def get_file_path(self, file_dir, prefix, prefix_date, file_name):
        if prefix_date is True:    # create a new file for each day
            today = get_today()
            date = "%04d%02d%02d" % today
        else:
            date = ''
        return os.path.join(file_dir, prefix + date + file_name)
    
    def log(self, level, msg, showtime=True):
        if level <= self.threshold:
            if self.logfile is None: 
                return
            if showtime:
                time_stamp = "%.01f"%time.time()
                self.logfile.write(time_stamp + log_separator)
            if isinstance(msg, str):
                self.logfile.write(msg)
            else:
                self.logfile.write(repr(msg))
            self.logfile.write('\n')
            self.logfile.flush()
            
    def close(self):
        if self.logfile is not None:
            self.logfile.close()
            

class OverlayLogger:
    __single = None
    
    def __init__(self, file_name, file_dir = '.'):
        if OverlayLogger.__single:
            raise RuntimeError, "OverlayLogger is singleton"
        self.file_name = file_name
        self.file_dir = file_dir
        OverlayLogger.__single = self
        self.Log = self.log
        self.__call__ = self.log

    def getInstance(*args, **kw):
        if OverlayLogger.__single is None:
            OverlayLogger(*args, **kw)
        return OverlayLogger.__single
    getInstance = staticmethod(getInstance)
    
    def log(self, *msgs):
        """
        # MSG must be the last one. Permid should be in the rear to be readable
        BuddyCast log for superpeer format: (V2)    
          CONN_TRY IP PORT PERMID
          CONN_ADD IP PORT PERMID SELVERSION
          CONN_DEL IP PORT PERMID REASON
          SEND_MSG IP PORT PERMID SELVERSION MSG_ID MSG
          RECV_MSG IP PORT PERMID SELVERSION MSG_ID MSG
          
          #BUCA_CON Permid1, Permid2, ...
          
          BUCA_STA xx xx xx ...    # BuddyCast status
              1  Pr    # nPeer
              2  Pf    # nPref
              3  Tr    # nTorrent
              
              4  Cc    # nConntionCandidates
              5  Bs    # nBlockSendList
              6  Br    # nBlockRecvList
              
              7  SO    # nConnectionsInSecureOver
              8  Co    # nConnectionsInBuddyCast
              
              9  Ct    # nTasteConnectionList
              10  Cr   # nRandomConnectionList
              11  Cu   # nUnconnectableConnectionList
        """
        
        log_msg = ''
        nmsgs = len(msgs)
        if nmsgs < 2:
            print >> sys.stderr, "Error message for log", msgs
            return
        
        else:
            for i in range(nmsgs):
                if isinstance(msgs[i], tuple) or isinstance(msgs[i], list):
                    log_msg += log_separator
                    for msg in msgs[i]:
                        try:
                            log_msg += str(msg)
                        except:
                            log_msg += repr(msg)
                        log_msg += log_separator
                else:
                    try:
                        log_msg += str(msgs[i])
                    except:
                        log_msg += repr(msgs[i])
                    log_msg += log_separator
                
        if log_msg:
            self._write_log(log_msg)
        
    def _write_log(self, msg):
        # one logfile per day. 
        today = get_today()
        if not hasattr(self, 'today'):
            self.logger = self._make_logger(today)
        elif today != self.today:    # make a new log if a new day comes
            self.logger.close()
            self.logger = self._make_logger(today)
        self.logger.log(3, msg)
            
    def _make_logger(self, today):
        self.today = today
        hostname = socket.gethostname()
        logger = Logger(3, self.file_name, self.file_dir, hostname, True)
        logger.log(3, '# Tribler Overlay Log Version 2', showtime=False)    # mention the log version at the first line
        logger.log(3, '# BUCA_STA: nRound   nPeer nPref nTorrent   ' + \
                   'nConntionCandidates nBlockSendList nBlockRecvList   ' + \
                   'nConnectionsInSecureOver nConnectionsInBuddyCast  ' + \
                   'nTasteConnectionList nRandomConnectionList nUnconnectableConnectionList', 
                   showtime=False)
        logger.log(3, '# BUCA_STA: Rd  Pr Pf Tr  Cc Bs Br  SO Co  Ct Cr Cu', showtime=False)
        return logger
        
if __name__ == '__main__':
    create_logger('test.log')
    get_logger().log(1, 'abc' + ' ' + str(['abc', 1, (2,3)]))
    get_logger().log(0, [1,'a',{(2,3):'asfadf'}])
    #get_logger().log(1, open('log').read())
    
    ol = OverlayLogger('overlay.log')
    ol.log('CONN_TRY', '123.34.3.45', 34, 'asdfasdfasdfasdfsadf')
    ol.log('CONN_ADD', '123.34.3.45', 36, 'asdfasdfasdfasdfsadf', 3)
    ol.log('CONN_DEL', '123.34.3.45', 38, 'asdfasdfasdfasdfsadf', 'asbc')
    ol.log('SEND_MSG', '123.34.3.45', 39, 'asdfasdfasdfasdfsadf', 2, 'BC', 'abadsfasdfasf')
    ol.log('RECV_MSG', '123.34.3.45', 30, 'asdfasdfasdfasdfsadf', 3, 'BC', 'bbbbbbbbbbbbb')
    ol.log('BUCA_STA', (1,2,3), (4,5,6), (7,8), (9,10,11))
    ol.log('BUCA_CON', ['asfd','bsdf','wevs','wwrewv'])
    