import os
import subprocess
import datetime
import pprint
from collections import defaultdict	

class Repository(object):

    def __init__(self,path):
        self._path = path

    @property
    def path(self):
        return self._path

    @path.setter
    def set_path(self,path):
        self._path = path

    def add_remote(self,url,name = "origin"):
        return subprocess.call(["git","remote","add",name,url],cwd = self.path)

    def get_remotes(self):
        return [x.strip() for x in subprocess.check_output(["git","remote"],cwd = self.path).split("\n") if x.strip()]

    def init(self):
        return subprocess.call(["git","init"],cwd = self.path)

    def pull(self,remote = "origin",branch = "master"):
        return subprocess.call(["git","pull",remote,branch],cwd = self.path)

    def get_branch(self):
        return subprocess.call("git branch | sed -n '/\* /s///p'",shell = True,cwd = self.path)

    def set_branch(self,branch):
        return subprocess.call(["git","checkout",branch],cwd = self.path)

    def get_commits(self,branch = "master"):

        self.set_branch(branch)

        split_sequence = '---a4337bc45a8fc544c03f52dc550cd6e1e87021bc896588bd79e901e2---'

        raw_log_output = subprocess.check_output(["git","log","--date=raw","--pretty=format:%H,%ct,%cn,%ce,%at,%an,%ae,%T,%n%B%n"+split_sequence+"%n"],cwd = self.path)

        headers_and_logs_str = map(lambda x:x.strip().split("\n",1),raw_log_output.split(split_sequence))[:-1]
        headers_and_logs = map(lambda x:(x[0].split(","),x[1]),headers_and_logs_str)

        commits = sorted(map(lambda x:
                {
                    'sha': x[0][0],
                    'committer_date': datetime.datetime.fromtimestamp(int(x[0][1])),
                    'committer_date_ts' : int(x[0][1]),
                    'committer_name' : x[0][2],
                    'committer_email': x[0][3],
                    'author_date': datetime.datetime.fromtimestamp(int(x[0][4])),
                    'branch' : branch,
                    'author_date_ts' : int(x[0][4]),
                    'author_name': x[0][5],
                    'author_email': x[0][6],
                    'tree_sha' : x[0][7],
                    'log' : x[1],
                }
                ,headers_and_logs),key = lambda x:x['committer_date_ts'])

        return commits

    def get_submodules(self):
        submodules = map(lambda x:x.split(" ")[1],subprocess.check_output(["git","submodule"],cwd = self.path).split("\n")[:-1])
        return submodules

    def get_modifications_by_author(self,commits):
    	modifications_by_author = defaultdict(lambda : defaultdict(lambda : {'lines_added': 0, 'lines_removed':0,'commits' : 0}))
    	for commit in commits:
            author_email = commit['author_email']
            sha = commit['sha']
            try:
                modified_files = [(int(v[0]),int(v[1]),v[2]) for v in [s.split("\t") for s in subprocess.check_output(["git","diff",r"--numstat","{sha}^..{sha}".format(sha = sha)],cwd = self.path).strip().split("\n")] if len(v) == 3 and v[2] != '' and v[0] != '-' and v[1] != '-']
            except subprocess.CalledProcessError:
            	continue
            for (lines_added,lines_removed,path) in modified_files:
            	d = modifications_by_author[author_email][path]
            	d['lines_added'] += lines_added
            	d['lines_removed'] += lines_removed
            	d['commits'] += 1
            return modifications_by_author

    def get_files_in_commit(self,commit_sha):
    	files = [dict(zip(['mode','type','sha','path'],f.strip().split())) for f in subprocess.check_output(["git","ls-tree","--full-tree","-r",commit_sha],cwd = self.path).split("\n")[1:] if f]
    	return files

    def get_file_details(self,commit_sha,path):
        (file_mode,file_type,file_sha,file_path) = subprocess.check_output(["git","ls-tree",commit_sha,path],cwd = self.path).split()
        return {'mode':file_mode,'type':file_type,'sha':file_sha,'path':file_path} 

    def get_diffs(self,commit_sha_a,commit_sha_b = None):
        if not commit_sha_b:
            files = self.get_files_in_commit(commit_sha_a)
            return [['A',file['path']] for file in files]
        else:
            diffs = subprocess.check_output(["git","diff","--name-status",commit_sha_a,commit_sha_b],cwd = self.path).split("\n")
            diffs = map(lambda x:x.split("\t"),diffs)
            return [x for x in diffs if len(x) == 2]

    def get_file_content(self,commit_sha,path):
        try:
            file_content = subprocess.check_output(["git","show","%s:%s" % (commit_sha,path)],cwd = self.path)
        except subprocess.CalledProcessError:
            raise IOError
        return file_content