#!/usr/bin/python
# -*- coding: utf-8 -*-
"""
    Monitor a StarConflict Log Directory.
"""
import sys, os
import time
import logging
from watchdog.observers import Observer
from watchdog.events import LoggingEventHandler

class SconEventHandler(LoggingEventHandler):
    def __init__(self, monitor, *args, **kwargs):
        self.monitor = monitor
        return super(SconEventHandler, self).__init__(*args, **kwargs)
    
    def on_moved(self, event):
        super(SconEventHandler, self).on_moved(event)
        if not event.is_directory:
            self.monitor.close(event.src_path)
        self.monitor.notify_event('moved', {'src': event.src_path, 
                                            'is_dir': event.is_directory, 
                                            'dest': event.dest_path})

    def on_created(self, event):
        super(SconEventHandler, self).on_created(event)
        if not event.is_directory:
            self.monitor.open(event.src_path)
        self.monitor.notify_event('created', {'src': event.src_path, 
                                              'is_dir': event.is_directory})

    def on_deleted(self, event):
        super(SconEventHandler, self).on_deleted(event)
        if not event.is_directory:
            self.monitor.close(event.src_path)
        self.monitor.notify_event('deleted', {'src': event.src_path, 
                                              'is_dir': event.is_directory})

    def on_modified(self, event):
        super(SconEventHandler, self).on_modified(event)
        self.monitor.notify_event('modified', {'src': event.src_path, 
                                              'is_dir': event.is_directory})
        
        

class SconMonitor(object):
        
    def notify(self, filename, lines):
        # notify somebody, filename has a few lines.
        # this is basicly the function you want to overwrite.
        # @see: self.run for how to integrate monitor into your main loop.
        if self.notifier is not None:
            self.notifier.notify(filename, lines)
    
    def notify_event(self, event_type, data):
        if self.notifier is not None:
            self.notifier.notify_event(event_type, data)
    
    def __init__(self, path=None, notifier=None):
        # if you initialize path directly, you lose success boolean.
        # albeit atm. this is always true.
        if path is not None:
            self.initialize(path)
        self.notifier = notifier
    
    def initialize(self, path):
        # initialize the monitor with a path to observe.
        self.files = {}
        self.event_handler = SconEventHandler(self)
        self.observer = Observer()
        self.observer.schedule(self.event_handler, 
                               path, 
                               recursive=True)
        return True
    
    def open(self, filename=None):
        # open a logfile and add it to the read-list...
        f = open(filename, 'r')
        new_file = { 'file': f,
                     'cursor': f.tell() }
        self.files[filename] = new_file
        return new_file
    
    def close(self, filename):
        # close a single file by key, does not do anything if not found.
        if filename in self.files.keys():
            close_file = self.files.pop(filename)
            close_file['file'].close()
            del close_file
    
    def close_all(self):
        """ closes all open files in the monitor """
        for key in self.files.keys():
            self.close(key)
    
    def read_line(self, afile):
        # read a single line in a file.
        f = afile.get('file', None)
        if f is None:
            return
        afile['cursor'] = f.tell()
        line = f.readline()
        if not line:
            f.seek(afile['cursor'])
            return None
        else:
            return line
    
    def do(self):
        ''' Monitor main task handler, call this in your mainloop in ~1 sec intervals '''
        # read all file changes.
        for key, value in self.files.items():
            lines = []
            data = self.read_line(value)
            while data is not None:
                lines.append(data)
                data = self.read_line(value)
            if lines:
                self.notify(key, lines)
            # 
    
    def initialize_loop(self):
        ''' initializes the main loop for the monitor '''
        self.observer.start()
    
    def break_loop(self):
        ''' call this if you want to break the monitors tasks '''
        self.observer.stop()
    
    def end_loop(self):
        ''' always call this before exiting your main loop '''
        self.close_all()
        self.observer.join()
        
    def run(self):
        ''' Basic Standalone Main Loop implementation, do not call this in your app. '''
        # if you want to run this on its own.
        # everytime any logfile is updated, print it.
        self.initialize_loop()
        try:
            while True:
                self.do()
                time.sleep(1)
        except KeyboardInterrupt:
            self.break_loop()
        self.end_loop()

if __name__ == '__main__':
    monitor = SconMonitor()
    logging.basicConfig(level=logging.INFO,
                        format='%(asctime)s - %(message)s',
                        datefmt='%Y-%m-%d %H:%M:%S')
    path = os.path.join(os.path.expanduser('~'),
                                     'Documents',
                                     'My Games',
                                     'StarConflict',
                                     'logs'
                                     )
    if monitor.initialize(path) is True:
        monitor.run()
    