"""
Download router logs from ECM.
"""

import datetime
import html
from . import base


class Logs(base.ECMCommand):
    """ Show or clear router logs. """

    use_pager = False
    name = 'logs'
    levels = ['debug', 'info', 'warning', 'error', 'critical']
    level_colors = {
        "debug": 'dim',
        "info": 'blue',
        "warning": 'yellow',
        "error": 'red',
        "critical": 'red'
    }

    def setup_args(self, parser):
        self.add_router_argument('idents', nargs='*')
        self.add_argument('--clear', action='store_true', help="Clear logs")
        self.add_argument('-l', '--level', choices=self.levels)
        self.add_argument('-f', '--follow', action='store_true',
                          help="Follow live logs (online routers only)")
        self.add_argument('-n', '--numlines', type=int, default=20,
                          help="Number of lines to display")
        self.inject_table_factory()

    def table_timestamp_acc(self, record):
        """ Table accessor for log timestamp. """
        dt = datetime.datetime.fromtimestamp(record['timestamp'])
        return '%d:%s' % (dt.hour % 12, dt.strftime('%M:%S %p'))

    def table_levelname_acc(self, record):
        """ Table accessor for colorized log level name. """
        color = self.level_colors[record['levelname'].lower()]
        opentags = ['<%s>' % color]
        closetags = ['</%s>' % color]
        if record['levelname'] == 'CRITICAL':
            opentags.append('<b>')
            closetags.insert(0, '</b>')
        return ''.join(opentags) + record['levelname'] + ''.join(closetags)

    def run(self, args):
        filters = {}
        if args.follow:
            filters['state'] = 'online'
            filters['product__series'] = 3
        if args.idents:
            routers = [self.api.get_by_id_or_name('routers', x, **filters)
                       for x in args.idents]
        else:
            routers = self.api.get_pager('routers', **filters)
        if args.clear:
            self.clear(args, routers)
        else:
            with self.make_table(accessors=(
                self.table_timestamp_acc,
                lambda x: x['router']['name'],
                self.table_levelname_acc,
                'source',
                'message',
                'exc'
            ), columns=(
                {
                    "width": 11,
                    "padding": 1,
                    "align": 'right'
                },
                {
                    "minwidth": 12
                },
                {
                    "minwidth": 5,
                    "padding": 1
                },
                {
                    "minwidth": 6
                },
                None,
                None,
            )) as table:
                if args.follow:
                    self.follow(args, routers, table)
                else:
                    self.view(args, routers, table)

    def clear(self, args, routers):
        for rinfo in routers:
            print("Clearing logs for: %s (%s)" % (rinfo['name'], rinfo['id']))
            self.api.delete('logs', rinfo['id'])

    def view(self, args, routers, table):
        filters = {}
        if args.level:
            filters['levelname'] = args.level.upper()
        for rinfo in routers:
            print("Logs for: %s (%s)" % (rinfo['name'], rinfo['id']))
            for x in self.api.get_pager('logs', rinfo['id'], **filters):
                x['mac'] = rinfo['mac']
                print('%(timestamp)s [%(mac)s] [%(levelname)8s] '
                      '[%(source)18s] %(message)s' % x)

    def follow(self, args, routers, table):
        lastseen = {}
        router_ids = ','.join(x['id'] for x in routers)
        while True:
            logs = []
            for router in self.api.remote('status.log', id__in=router_ids):
                logdata = router['results'][0]['data']
                logdata.sort(key=lambda x: x[0])
                lastshown = lastseen.get(router['id'])
                offt = -args.numlines
                if lastshown is not None:
                    for offt, x in enumerate(logdata):
                        if x == lastshown:
                            offt += 1
                            break
                    else:
                        raise RuntimeError("Did not find tailing edge")
                logs.extend({
                    "router": router['router'],
                    "timestamp": x[0],
                    "levelname": x[1],
                    "source": x[2],
                    "message": x[3] and html.unescape(x[3])
                } for x in logdata[offt:])
                lastseen[router['id']] = logdata[-1]
            logs.sort(key=lambda x: x['timestamp'])
            table.print(logs)

command_classes = [Logs]

@@ -61,16 +61,28 @@
 rt html%0A
+import time%0A
 from . i
@@ -185,16 +185,42 @@
 = False%0A
+    max_follow_sleep = 30%0A
     name
@@ -3773,32 +3773,50 @@
 r x in routers)%0A
+        sleep = 0%0A
         while Tr
@@ -3819,16 +3819,16 @@
 e True:%0A
-
         
@@ -3913,24 +3913,95 @@
 outer_ids):%0A
+                if not router%5B'results'%5D:%0A                    continue%0A
             
@@ -4495,16 +4495,89 @@
  edge%22)%0A
+                updates = logdata%5Bofft:%5D%0A                if updates:%0A    
         
@@ -4598,16 +4598,20 @@
 xtend(%7B%0A
+    
         
@@ -4670,16 +4670,20 @@
         
+  
+  
 %22timesta
@@ -4693,16 +4693,20 @@
 : x%5B0%5D,%0A
+    
         
@@ -4756,16 +4756,20 @@
         
+   
+ 
 %22source%22
@@ -4776,16 +4776,20 @@
 : x%5B2%5D,%0A
+    
         
@@ -4856,16 +4856,20 @@
         
+    
 %7D for x 
@@ -4875,24 +4875,21 @@
  in 
-logdata%5Bofft:%5D)%0A
+updates)%0A    
     
@@ -4937,16 +4937,41 @@
 ata%5B-1%5D%0A
+            if logs:%0A    
         
@@ -5026,16 +5026,20 @@
         
+    
 table.pr
@@ -5047,16 +5047,149 @@
 nt(logs)
+%0A                sleep = 0%0A            else:%0A                sleep += 0.200%0A            time.sleep(min(self.max_follow_sleep, sleep))
 %0A%0Acomman

Add basic basic backoff algo to `logs -f`