#-------------------------------------------------------------------------
# Copyright (c) Steve Dower
# All rights reserved.
#
# Distributed under the terms of the MIT License
#-------------------------------------------------------------------------

__all__ = ['Environment', 'findall', 'find', 'findone']

from itertools import count
from pythonfinder._vendor.pep514tools._registry import open_source, REGISTRY_SOURCE_LM, REGISTRY_SOURCE_LM_WOW6432, REGISTRY_SOURCE_CU
import re
import sys

# These tags are treated specially when the Company is 'PythonCore'
_PYTHONCORE_COMPATIBILITY_TAGS = {
    '2.0', '2.1', '2.2', '2.3', '2.4', '2.5', '2.6', '2.7',
    '3.0', '3.1', '3.2', '3.3', '3.4', '3.5', '3.6', '3.7',
    '3.8', '3.9'
}

_IS_64BIT_OS = None
def _is_64bit_os():
    global _IS_64BIT_OS
    if _IS_64BIT_OS is None:
        if sys.maxsize > 2**32:
            import platform
            _IS_64BIT_OS = (platform.machine() == 'AMD64')
        else:
            _IS_64BIT_OS = False
    return _IS_64BIT_OS

class Environment(object):
    def __init__(self, source, company, tag, guessed_arch=None):
        self._source = source
        self.company = company
        self.tag = tag
        self._guessed_arch = guessed_arch
        self._orig_info = company, tag
        self.info = {}

    def load(self):
        if not self._source:
            raise ValueError('Environment not initialized with a source')
        self.info = info = self._source[self.company][self.tag].get_all_values()
        if self.company == 'PythonCore':
            info._setdefault('DisplayName', 'Python ' + self.tag)
            info._setdefault('SupportUrl', 'http://www.python.org/')
            info._setdefault('Version', self.tag[:3])
            info._setdefault('SysVersion', self.tag[:3])
            if self._guessed_arch:
                info._setdefault('SysArchitecture', self._guessed_arch)

    def save(self, copy=False):
        if not self._source:
            raise ValueError('Environment not initialized with a source')
        if (self.company, self.tag) != self._orig_info:
            if not copy:
                self._source[self._orig_info[0]][self._orig_info[1]].delete()
            self._orig_info = self.company, self.tag

        src = self._source[self.company][self.tag]
        src.set_all_values(self.info)

        self.info = src.get_all_values()

    def delete(self):
        if (self.company, self.tag) != self._orig_info:
            raise ValueError("cannot delete Environment when company/tag have been modified")

        if not self._source:
            raise ValueError('Environment not initialized with a source')
        self._source.delete()

    def __repr__(self):
        return '<environment {}\\{}>'.format(self.company, self.tag)

def _get_sources(include_per_machine=True, include_per_user=True):
    if _is_64bit_os():
        if include_per_user:
            yield open_source(REGISTRY_SOURCE_CU), None
        if include_per_machine:
            yield open_source(REGISTRY_SOURCE_LM), '64bit'
            yield open_source(REGISTRY_SOURCE_LM_WOW6432), '32bit'
    else:
        if include_per_user:
            yield open_source(REGISTRY_SOURCE_CU), '32bit'
        if include_per_machine:
            yield open_source(REGISTRY_SOURCE_LM), '32bit'

def findall(include_per_machine=True, include_per_user=True):
    for src, arch in _get_sources(include_per_machine=include_per_machine, include_per_user=include_per_user):
        for company in src:
            for tag in company:
                try:
                    env = Environment(src, company.name, tag.name, arch)
                    env.load()
                except OSError:
                    pass
                else:
                    yield env

def find(company_or_tag, tag=None, include_per_machine=True, include_per_user=True, maxcount=None):
    if not tag:
        env = Environment(None, 'PythonCore', company_or_tag)
    else:
        env = Environment(None, company_or_tag, tag)

    results = []
    for src, arch in _get_sources(include_per_machine=include_per_machine, include_per_user=include_per_user):
        try:
            env._source = src
            env._guessed_arch = arch
            env.load()
        except OSError:
            pass
        else:
            results.append(env)
    return results

def findone(company_or_tag, tag=None, include_per_machine=True, include_per_user=True):
    found = find(company_or_tag, tag, include_per_machine, include_per_user, maxcount=1)
    if found:
        return found[0]
