# Copyright 2014 ARM Limited
#
# Licensed under the Apache License, Version 2.0
# See LICENSE file for details.

# Utilities for parsing version source URLs, and determining how the
# specified version should be fetched.

# standard library modules, , ,
try:
    from urlparse import urlsplit
except ImportError:
    from urllib.parse import urlsplit #pylint: disable=no-name-in-module,import-error

# version, , represent versions and specifications, internal
from yotta.lib import version

class VersionSource(object):
    def __init__(self, source_type, location, spec):
        assert(source_type in ('registry', 'github', 'git', 'hg'))
        self.source_type = source_type
        self.location = location
        self.spec = spec
        try:
            self.semantic_spec = version.Spec(spec)
        except ValueError:
            # for git/github source URLs the spec is allowed to be a branch
            # name or tag name, as well as a valid semantic version
            # specification
            # !!! TODO: also allow hg here
            if source_type in ('git', 'github'):
                self.semantic_spec = None
            else:
                raise ValueError(
                    "Invalid semantic version spec: \"%s\"" % spec
                )

    def semanticSpec(self):
        return self.semantic_spec or version.Spec('*')

    def semanticSpecMatches(self, v):
        if isinstance(v, str):
            v = version.Version(v)
        if self.semantic_spec is None:
            return True
        else:
            return self.semantic_spec.match(v)


def _splitFragment(url):
    parsed = urlsplit(url)
    if '#' in url:
        return url[:url.index('#')], parsed.fragment
    else:
        return url, None

def _getGithubRef(source_url):
    import re
    # something/something#spec = github
    defragmented, fragment = _splitFragment(source_url)
    github_match = re.match('^[a-z0-9_-]+/([a-z0-9_-]+)$', defragmented, re.I)
    if github_match:
        return github_match.group(1), VersionSource('github', defragmented, fragment)

    # something/something@spec = github
    alternate_github_match = re.match('([a-z0-9_-]+/([a-z0-9_-]+)) *@?([~^><=.0-9a-z\*-]*)$', source_url, re.I)
    if alternate_github_match:
        return alternate_github_match.group(2), VersionSource('github', alternate_github_match.group(1), alternate_github_match.group(3))

    return None, None

def parseSourceURL(source_url):
    ''' Parse the specified version source URL (or version spec), and return an
        instance of VersionSource
    '''
    import re
    parsed = urlsplit(source_url)

    if '#' in source_url:
        without_fragment = source_url[:source_url.index('#')]
    else:
        without_fragment = source_url

    try:
        url_is_spec = version.Spec(source_url)
    except ValueError:
        url_is_spec = None

    if url_is_spec is not None:
        # if the url is an unadorned version specification (including an empty
        # string) then the source is the module registry:
        return VersionSource('registry', '', source_url)
    elif parsed.netloc.endswith('github.com'):
        # any URL onto github should be fetched over the github API, even if it
        # would parse as a valid git URL
        return VersionSource('github', parsed.path, parsed.fragment)
    elif parsed.scheme.startswith('git+') or parsed.path.endswith('.git'):
        # git+anything://anything or anything.git is a git repo:
        return VersionSource('git', without_fragment, parsed.fragment)
    elif parsed.scheme.startswith('hg+') or parsed.path.endswith('.hg'):
        # hg+anything://anything or anything.hg is a hg repo:
        return VersionSource('hg', without_fragment, parsed.fragment)

    # something/something@spec = github
    # something/something#spec = github
    module_name, github_match = _getGithubRef(source_url)
    if github_match:
        return github_match

    raise ValueError("Invalid version specification: \"%s\"" % (source_url))

def parseModuleNameAndSpec(module_name_and_spec):
    ''' Parse modulename[@versionspec] and return a tuple
        (module_name_string, version_spec_string).

        Also accepts raw github version specs (Owner/reponame#whatever), as the
        name can be deduced from these.

        Note that the specification split from the name is not validated. If
        there is no specification (just a module name) passed in, then '*' will
        be returned as the specification.
    '''
    import re
    # fist check if this is a raw github specification that we can get the
    # module name from:
    name, spec = _getGithubRef(module_name_and_spec)
    if name and spec:
        return name, module_name_and_spec

    # next split at the first @, if any
    name = module_name_and_spec.split('@')[0]
    spec = module_name_and_spec[len(name)+1:]

    name = name.strip()

    # if there's no specification, return the explicit any-version
    # specification:
    if not spec:
        spec = '*'

    import sys
    sys.stdout.flush()

    return name, spec
