# Versioning system that fits into an 32-bit int
# (for stuffing into an EPICS 32-bit int channel)
#
# Break up the 32-bit int into 8x4-bit (hexadecimal) fields and assign
# them thusly:
#
# 0 [reserved] (EPICS doesn't actually give the full 32 bits for the value)
# 1 [reserved]
# 2 [reserved]
# 3 MAJOR (0-15)
# 4 MINOR
# 5 MINOR (0-255)
# 6 REV
# 7 REV   (0-255)
#
# FIXME: what should we do if the version string has a suffix?


def check_vers(major, minor, rev):
    if not (major < 16**1 and minor < 16**2 and rev < 16**2):
        raise ValueError("invalid version number: out of range")


def vers_to_str(major, minor, rev):
    """return version string major.minor.rev"""
    check_vers(major, minor, rev)
    return '{0}.{1}.{2}'.format(major, minor, rev)


def vers_to_hex(major, minor, rev):
    """return version hex from major,minor,rev"""
    check_vers(major, minor, rev)
    return '000{0:1x}{1:02x}{2:02x}'.format(
        major,
        minor,
        rev,
        )


def vers_to_int(major, minor, rev, suffix=''):
    """return version int from major,minor,rev"""
    check_vers(major, minor, rev)
    return int(vers_to_hex(major, minor, rev), 16)


def vers_from_str(s):
    """return version info from version string: major,minor,rev,suffix

    major,minor,rev will be ints, where as suffix will be a str

    """
    # check if there's a suffix on the string
    suffix = ''
    vsplit = s.split('+')
    if len(vsplit) > 1:
        suffix = '+'+vsplit[1]
    try:
        major, minor, rev = vsplit[0].split('.')
    except ValueError:
        raise ValueError("Could not parse version string '{}' (should consist of major.minor.rev)".format(s))
    return int(major), int(minor), int(rev), suffix


def vers_from_int(n):
    """return version major,minor,rev from version int"""
    h = hex(n)[2:].zfill(8)
    major = int(h[3], 16)
    minor = int(h[4:6], 16)
    rev = int(h[6:8], 16)
    return major, minor, rev


def vers_str2int(s):
    """return version int from version string"""
    return vers_to_int(*vers_from_str(s))


def vers_int2str(s):
    """return version string from version int"""
    return vers_to_str(*vers_from_int(s))


def _release(s, t=None):
    """Return new release version for given version string and release type

    """
    major, minor, rev, suffix = vers_from_str(s)
    if t == 'major':
        major += 1
        minor = 0
        rev = 0
    elif t == 'minor':
        minor += 1
        rev = 0
    elif t == 'rev':
        rev += 1
    return vers_to_str(major, minor, rev)


if __name__ == '__main__':
    import argparse

    from . import __version__

    parser = argparse.ArgumentParser(
        description="""
With no arguments outputs the current version.  With a version string
or int argument, convert to int or string.  With --release option,
determine appropriate new release version from the specified version,
or from the current version if not specified.""",
        formatter_class=argparse.RawDescriptionHelpFormatter,
    )
    parser.add_argument(
        'VERS', nargs='?',
        help="version string or int")
    vgroup = parser.add_mutually_exclusive_group(required=False)
    vgroup.add_argument(
        '--release', '-r', choices=['major', 'minor', 'rev'],
        help="create new release version")
    args = parser.parse_args()
    if args.release:
        vers = args.VERS or __version__
        print(_release(vers, args.release))
    elif args.VERS:
        try:
            print(vers_int2str(int(args.VERS)))
        except ValueError:
            print(vers_str2int(args.VERS))
    else:
        print(__version__)
