import re
import sublime

from .settings import ClientConfig, client_configs
from .logging import debug
from .workspace import get_project_config
from .windows import ViewLike

assert ClientConfig

try:
    from typing import Any, List, Dict, Tuple, Callable, Optional
    assert Any and List and Dict and Tuple and Callable and Optional
    assert ViewLike
except ImportError:
    pass


# window_client_configs = dict()  # type: Dict[int, List[ClientConfig]]


def get_scope_client_config(view: 'sublime.View', configs: 'List[ClientConfig]') -> 'Optional[ClientConfig]':
    # When there are multiple server configurations, all of which are for
    # similar scopes (e.g. 'source.json', 'source.json.sublime.settings') the
    # configuration with the most specific scope (highest ranked selector)
    # in the current position is preferred.
    scope_score = 0
    scope_client_config = None
    for config in configs:
        for scope in config.scopes:
            sel = view.sel()
            if len(sel) > 0:
                score = view.score_selector(sel[0].begin(), scope)
                if score > scope_score:
                    scope_score = score
                    scope_client_config = config
    return scope_client_config


def register_client_config(config: ClientConfig) -> None:
    # todo: how to propagate global config changes to all window-specific configs.
    # window_client_configs.clear()
    client_configs.add_external_config(config)


def get_global_client_config(view: sublime.View) -> 'Optional[ClientConfig]':
    return get_scope_client_config(view, client_configs.all)


def get_default_client_config(view: sublime.View) -> 'Optional[ClientConfig]':
    return get_scope_client_config(view, client_configs.defaults)


# def get_window_client_config(view: sublime.View) -> 'Optional[ClientConfig]':
#     window = view.window()
#     if window:
#         configs_for_window = window_client_configs.get(window.id(), [])
#         return get_scope_client_config(view, configs_for_window)
#     else:
#         return None


# def config_for_scope(view: sublime.View) -> 'Optional[ClientConfig]':
#     # check window_client_config first
#     window_client_config = get_window_client_config(view)
#     if not window_client_config:
#         global_client_config = get_global_client_config(view)

#         if global_client_config:
#             window = view.window()
#             if window:
#                 window_client_config = apply_window_settings(global_client_config, window)
#                 add_window_client_config(window, window_client_config)
#                 return window_client_config
#             else:
#                 # always return a client config even if the view has no window anymore
#                 return global_client_config

#     return window_client_config


# def add_window_client_config(window: 'sublime.Window', config: 'ClientConfig'):
#     window_client_configs.setdefault(window.id(), []).append(config)


# def clear_window_client_configs(window: 'sublime.Window'):
#     if window.id() in window_client_configs:
#         del window_client_configs[window.id()]


def create_window_configs(window: 'sublime.Window') -> 'List[ClientConfig]':
    return list(map(lambda c: apply_window_settings(c, window), client_configs.all))


def apply_window_settings(client_config: 'ClientConfig', window: 'sublime.Window') -> 'ClientConfig':
    window_config = get_project_config(window)

    if client_config.name in window_config:
        overrides = window_config[client_config.name]
        debug('window has override for', client_config.name, overrides)
        return ClientConfig(
            client_config.name,
            overrides.get("command", client_config.binary_args),
            overrides.get("tcp_port", client_config.tcp_port),
            overrides.get("scopes", client_config.scopes),
            overrides.get("syntaxes", client_config.syntaxes),
            overrides.get("languageId", client_config.languageId),
            overrides.get("enabled", client_config.enabled),
            overrides.get("initializationOptions", client_config.init_options),
            overrides.get("settings", client_config.settings),
            overrides.get("env", client_config.env)
        )

    return client_config


def is_supportable_syntax(syntax: str) -> bool:
    # TODO: filter out configs disabled by the user.
    for config in client_configs.defaults:
        if re.search(r'|'.join(r'\b%s\b' % re.escape(s) for s in config.syntaxes), syntax, re.IGNORECASE):
            return True
    return False


def is_supported_syntax(syntax: str) -> bool:
    for config in client_configs.all:
        if re.search(r'|'.join(r'\b%s\b' % re.escape(s) for s in config.syntaxes), syntax, re.IGNORECASE):
            return True
    return False


class ConfigManager(object):

    def for_window(self, window: 'Any') -> 'WindowConfigManager':
        return WindowConfigManager(create_window_configs(window))
    # def is_supported(self, view: 'Any') -> bool:
    #     # todo: calls config_for_scope immediately.
    #     return is_supported_view(view)

    # def scope_config(self, view: 'Any') -> 'Optional[ClientConfig]':
    #     return config_for_scope(view)


class WindowConfigManager(object):
    def __init__(self, configs: 'List[ClientConfig]') -> None:
        self._configs = configs

    def is_supported(self, view: 'Any') -> bool:
        return self.scope_config(view) is not None

    def scope_config(self, view: 'Any') -> 'Optional[ClientConfig]':
        return get_scope_client_config(view, self._configs)

@@ -386,82 +386,8 @@
 s%0A%0A%0A
-# window_client_configs = dict()  # type: Dict%5Bint, List%5BClientConfig%5D%5D%0A%0A%0A
 def 
@@ -1682,1399 +1682,8 @@
 )%0A%0A%0A
-# def get_window_client_config(view: sublime.View) -%3E 'Optional%5BClientConfig%5D':%0A#     window = view.window()%0A#     if window:%0A#         configs_for_window = window_client_configs.get(window.id(), %5B%5D)%0A#         return get_scope_client_config(view, configs_for_window)%0A#     else:%0A#         return None%0A%0A%0A# def config_for_scope(view: sublime.View) -%3E 'Optional%5BClientConfig%5D':%0A#     # check window_client_config first%0A#     window_client_config = get_window_client_config(view)%0A#     if not window_client_config:%0A#         global_client_config = get_global_client_config(view)%0A%0A#         if global_client_config:%0A#             window = view.window()%0A#             if window:%0A#                 window_client_config = apply_window_settings(global_client_config, window)%0A#                 add_window_client_config(window, window_client_config)%0A#                 return window_client_config%0A#             else:%0A#                 # always return a client config even if the view has no window anymore%0A#                 return global_client_config%0A%0A#     return window_client_config%0A%0A%0A# def add_window_client_config(window: 'sublime.Window', config: 'ClientConfig'):%0A#     window_client_configs.setdefault(window.id(), %5B%5D).append(config)%0A%0A%0A# def clear_window_client_configs(window: 'sublime.Window'):%0A#     if window.id() in window_client_configs:%0A#         del window_client_configs%5Bwindow.id()%5D%0A%0A%0A
 def 
@@ -3458,16 +3458,16 @@
 nager':%0A
+
         
@@ -3528,266 +3528,8 @@
 w))%0A
-    # def is_supported(self, view: 'Any') -%3E bool:%0A    #     # todo: calls config_for_scope immediately.%0A    #     return is_supported_view(view)%0A%0A    # def scope_config(self, view: 'Any') -%3E 'Optional%5BClientConfig%5D':%0A    #     return config_for_scope(view)%0A
 %0A%0Acl

Clean up configurations