#!/usr/bin/env python
#-*- coding: utf-8 -*-
"""


Dojotools - tools for your coding dojo session

Copyright (C) 2009 Flávio Amieiro <amieiro.flavio@gmail.com>

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 dated June, 1991.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Library General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>.

If you find any bugs or have any suggestions email: amieiro.flavio@gmail.com
"""

import os
import sys
from fnmatch import fnmatch
import subprocess
from time import ctime
import gobject

class Timer(object):

    def __init__(self, round_time=300):
        self.round_time = round_time
        self.time_left = self.round_time

        gobject.timeout_add(1000, self.update)
        self.running = False

    def start(self):
        self.running = True

    def pause(self):
        self.running = False

    def update(self):
        if self.running:
            if self.time_left:
                self.time_left -= 1
            else:
                self.time_left = self.round_time

        return True


class Monitor(object):

    def __init__(self, ui, directory, commands, patterns_file, commit=False):
        """
        'directory' is the directory to be watched for changes.

        --

        'commands' is a list with the commands to run when a file changes

        --

        'patterns_files' is the path to a file which contains a list that will
        be used to filter the files we're watching.
        """
        self.old_sum = 0
        self.directory = directory
        self.commands = commands
        self.patterns = self._get_patterns(patterns_file)
        self.ui = ui
        self.commit = commit

        gobject.timeout_add(1000, self.check)

    def _get_patterns(self, patterns_file):
        """
        Reads `patterns_file' and returns a list of patterns found in
        the patterns file, so they can be used in fnmatch.

        Patterns should be Unix style.

        For more information, type help(fnmatch) in a python shell
        """
        try:
            with open(patterns_file, 'r') as f:
                patterns = [pattern.strip() for pattern in  f.readlines()]
        except IOError:
            sys.stdout.write(
                'Could not find %s. Patterns will not be ignored\n'
                % patterns_file
            )
            patterns = []

        # Add patterns_file to the ignored patterns so it won't be tracked
        patterns += [os.path.basename(patterns_file)]

        return patterns

    def _filter_files(self, files):
        """
        Filter a list of strings based on each item in 'self.patterns'

        This function must be called every time `check` is called so we
        will not ignore newly created files in the directory (that is why
        files is not an instance attribute)
        """
        for p in self.patterns:
            files = [f for f in files if not fnmatch(f, p)]
        return files

    def git_commit_all(self):
        """
        Adds all files and commits them using git
        """
        msg = ctime()
        process = subprocess.Popen(
            "git add . && git commit -m '%s'" % msg,
            shell=True,
            cwd=self.directory,
        )

        #if git returns 128 it means 'command not found' or 'not a git repo'
        if process.wait() == 128:
            error = ('Impossible to commit to repository. '
                    'Make sure git is installed an this is a valid repository')
            raise OSError(error)

    def run_command(self, test_cmd):
        """
        As the name says, runs a command and waits for it to finish
        """
        test_cmd = self.commands
        process = subprocess.Popen(
            test_cmd,
            shell = True,
            cwd = self.directory,
            stdout = subprocess.PIPE,
            stderr = subprocess.PIPE,
        )

        output = process.stdout.read()
        output += process.stderr.read()
        status = process.wait()

        self.ui.show_command_results(status, output)

    def check(self):
        """
        Monitor self.directory for changes, ignoring files matching any item
        in self.patterns and runs any command in self.commands when a file has
        changed.
        """
        m_time_list = []
        for root, dirs, files in os.walk(self.directory):
            # We must ignore all the files in .git directory because
            # any commit changes them. Taking this directory into
            # consideration would cause an infinite loop.
            if '.git' in root:
                continue
            files = self._filter_files(files)
            # Be careful. The += operator works as the extend method
            # on mutable objects. For more information refer to
            # http://zephyrfalcon.org/labs/python_pitfalls.html
            m_time_list += [
                os.stat(os.path.join(root, f)).st_mtime for f in files
            ]
        new_sum = sum(m_time_list)
        if new_sum != self.old_sum:
            self.run_command(self.commands)
            if self.commit:
                self.git_commit_all()
            self.old_sum = new_sum

        # This method must return True so gobject.timeout_add runs it again
        return True


