# vim:fileencoding=utf-8:noet
from __future__ import absolute_import

import os
import errno
from threading import Lock
from collections import defaultdict


def generate_directories(path):
	if os.path.isdir(path):
		yield path
	while True:
		if os.path.ismount(path):
			break
		old_path = path
		path = os.path.dirname(path)
		if path == old_path or not path:
			break
		yield path


_file_watcher = None


def file_watcher(create_watcher):
	global _file_watcher
	if _file_watcher is None:
		_file_watcher = create_watcher()
	return _file_watcher


_branch_watcher = None


def branch_watcher(create_watcher):
	global _branch_watcher
	if _branch_watcher is None:
		_branch_watcher = create_watcher()
	return _branch_watcher


branch_name_cache = {}
branch_lock = Lock()
file_status_lock = Lock()


def get_branch_name(directory, config_file, get_func, create_watcher):
	global branch_name_cache
	with branch_lock:
		# Check if the repo directory was moved/deleted
		fw = branch_watcher(create_watcher)
		is_watched = fw.is_watched(directory)
		try:
			changed = fw(directory)
		except OSError as e:
			if getattr(e, 'errno', None) != errno.ENOENT:
				raise
			changed = True
		if changed:
			branch_name_cache.pop(config_file, None)
			# Remove the watches for this repo
			if is_watched:
				fw.unwatch(directory)
				fw.unwatch(config_file)
		else:
			# Check if the config file has changed
			try:
				changed = fw(config_file)
			except OSError as e:
				if getattr(e, 'errno', None) != errno.ENOENT:
					raise
				# Config file does not exist (happens for mercurial)
				if config_file not in branch_name_cache:
					branch_name_cache[config_file] = get_func(directory, config_file)
		if changed:
			# Config file has changed or was not tracked
			branch_name_cache[config_file] = get_func(directory, config_file)
		return branch_name_cache[config_file]


class FileStatusCache(dict):
	def __init__(self):
		self.dirstate_map = defaultdict(set)
		self.ignore_map = defaultdict(set)
		self.keypath_ignore_map = {}

	def update_maps(self, keypath, directory, dirstate_file, ignore_file_name, extra_ignore_files):
		parent = keypath
		ignore_files = set()
		while parent != directory:
			nparent = os.path.dirname(keypath)
			if nparent == parent:
				break
			parent = nparent
			ignore_files.add(os.path.join(parent, ignore_file_name))