import sublime
import sublime_plugin
import functools
import threading
import subprocess
import sys
import re
import tempfile
import codecs
import os
import time

from SideBarGit import SideBarGit
from SideBarItem import SideBarItem
from Utils import Object

Object.running = False
Object.timing = time.time()

class SideBarGitGutterDiff(sublime_plugin.EventListener):

	def on_load(self, view):
		self.run(view)

	def on_modified(self, view):
		now = time.time()
		if now - Object.timing > 0.1:
			Object.timing = now
			self.run(view)
		else:
			Object.timing = now

	def on_post_save(self, view):
		self.run(view)

	def run(self, view):
		if Object.running == False and view.file_name() != None and view.file_name() != '':
			Object.running = True
			# cache file repository ( if any )
			if not view.settings().has('SideBarGitGutterRepository'):
				item = SideBarItem(view.file_name(), False)
				_item = SideBarItem(view.file_name(), False)
				repos = SideBarGit().getSelectedRepos([_item])
				if len(repos) > 0:
					view.settings().set('SideBarGitGutterRepository', repos[0].repository.path())
					view.settings().set('SideBarGitGutterCWD', repos[0].repository.path())
					view.settings().set('SideBarGitGutterPath', item.forCwdSystemPathRelativeFrom(repos[0].repository.path()))
				else:
					view.settings().set('SideBarGitGutterRepository', '')
			
			# if in a repo check for modifications
			repo =  view.settings().get('SideBarGitGutterRepository')
			if repo != '':
				SideBarGitGutterDiffThread(
																		view,
																		repo,
																		view.settings().get('SideBarGitGutterCWD'),
																		view.settings().get('SideBarGitGutterPath'),
																		view.substr(sublime.Region(0, view.size()))
																		).start()
			else:
				Object.running = False

class SideBarGitGutterDiffThread(threading.Thread):

	def __init__(self, view, repo, cwd, path, content):
		threading.Thread.__init__(self)
		self.view = view
		self.repo = repo
		self.cwd = cwd
		self.path = path
		self.content = content

	def run(self):

		tmp = tempfile.NamedTemporaryFile(delete=False)
		codecs.open(tmp.name, 'w+', 'utf-8').write(self.content)

		comand = ['git', 'diff', '-p', '--unified=0', '--no-color', '--ignore-all-space', '--ignore-space-at-eol', '--ignore-space-change', 'HEAD:'+self.path, tmp.name]

		process = subprocess.Popen(
																comand,
																cwd=self.cwd,
																stdout=subprocess.PIPE,
																stderr=subprocess.STDOUT,
																shell=sys.platform == 'win32',
																universal_newlines=True)

		stdout, stderr = process.communicate()
		if stdout != '' and stdout.find('fatal:') != 0:

			hunk = re.finditer('\n@@ -([0-9]+),?([0-9]*) \+([0-9]+),?([0-9]*) @@', stdout)

			additions = []
			deletions = []
			changes = []

			for change in hunk:
				g = []
				for group in change.groups():
					if group == '':
						g.append(1)
					else:
						g.append(int(group))
				deleted = g[1]
				added   = g[3]

				if deleted == added and added == 1:
					changes.append([g[2]-1,  g[2]]);
				else:
					if deleted > 0:
						if deleted == 1:
							if added > deleted:
								deletions.append([g[2], g[2]+deleted-added]);
							else:
								deletions.append([g[2], g[2]+1]);
						else:
							deletions.append([g[2]-1, g[2]+deleted-1])
					if added > 0:
						if added == 1:
							additions.append([g[2], g[2]+added]);
						else:
							additions.append([g[2]-1, g[2]+added-1])
			tmp.close();
			os.remove(tmp.name)
			sublime.set_timeout(functools.partial(self.add_regions, additions, deletions, changes), 0)
		else:
			tmp.close();
			os.remove(tmp.name)
			sublime.set_timeout(functools.partial(self.erase_regions), 0)
			if stdout.find('fatal:'):
				sublime.set_timeout(functools.partial(self.mark_not_in_a_repository), 0)
		Object.running = False

	def add_regions(self, additions, deletions, changes):

		self.erase_regions()

		rs = []
		for r in additions:
			while r[0] != r[1]:
				rs.append(sublime.Region(self.view.text_point(r[0], 0)))
				r[0] = r[0]+1
		if len(rs):
			self.view.add_regions("git.diff.additions", rs, "number", "dot", sublime.HIDDEN)

		rs = []
		for r in deletions:
			while r[0] != r[1]:
				rs.append(sublime.Region(self.view.text_point(r[0], 0)))
				r[0] = r[0]+1
		if len(rs):
			self.view.add_regions("git.diff.deletions", rs, "entity.name.class", "dot", sublime.HIDDEN)

		rs = []
		for r in changes:
			while r[0] != r[1]:
				rs.append(sublime.Region(self.view.text_point(r[0], 0)))
				r[0] = r[0]+1
		if len(rs):
			self.view.add_regions("git.diff.changes", rs, "string", "dot", sublime.HIDDEN)


	def erase_regions(self):
		self.view.erase_regions("git.diff.additions")
		self.view.erase_regions("git.diff.deletions")
		self.view.erase_regions("git.diff.changes")

	def mark_not_in_a_repository(self):
		self.view.settings().set('SideBarGitGutterRepository', '')