#!/usr/bin/env python3
# ip-subnet - print subnets of a network for given prefix length
import sys
import getopt
from ipaddress import ip_network, ip_interface
from nullroute.core import Core

def usage():
    print("Usage: %s [-s] <ip_network> /<prefix>" % Core.arg0)
    print("       %s [-s] <ip_network> +<increment>" % Core.arg0)

try:
    opt_list = False
    opt_short = False
    opts, args = getopt.getopt(sys.argv[1:], "ls", ["list", "short"])
    for opt, optarg in opts:
        if opt in {"-s", "--short"}:
            opt_short = True
        elif opt in {"-l", "--list"}:
            opt_list = True

    # parse network arg
    if args[0].startswith("/"):
        # assume IPv4 because "::/x" is already short enough
        args[0] = "0.0.0.0" + args[0]
        opt_short = True
    net = ip_network(args[0])

    # parse increment arg
    if args[1].startswith("/"):
        increment = int(args[1][1:]) - net.prefixlen
    elif args[1].startswith("+"):
        increment = int(args[1])
    elif args[1].startswith("-"):
        increment = int(args[1])
    else:
        Core.die("submask should be /x or +x")

    # special-case args
    if net.network_address.packed[0:4] == b"\0\0\0\0":
        opt_short = True

    if opt_list:
        opt_short = False

    # show the corresponding supernet and same-level subnets
    if increment < 0:
        increment *= -1
        net = net.supernet(increment)

    # print header
    if not (opt_list or opt_short):
        print(net)

    subnets = net.subnets(increment)
    count = 0
    total = 2**increment

    if opt_short:
        subnet = next(subnets)
    else:
        for subnet in subnets:
            count += 1
            if opt_list:
                print(subnet)
                if count > 2**24:
                    Core.die("output limit of 2^24 subnets reached (total %d)" % total)
            else:
                print("   ", subnet)
                if count > 2**5:
                    last_net = ip_interface("%s/%s" % (net.broadcast_address,
                                                       subnet.prefixlen)).network
                    print("   ", "... (%d more subnets)" % (total - count - 1))
                    print("   ", last_net)
                    break

    if not opt_list:
        plural = lambda x, n, s: (f"{x} {n}" if x == 1 else f"{x} {n}{s}")

        if net.version == 6 and subnet.prefixlen < 64:
            totalnets = 2 ** (64 - subnet.prefixlen)
            msg = "%d (/%d) ranges × %d (/64) subnets" % (total, subnet.prefixlen,
                                                          totalnets)
        elif subnet.prefixlen == 64:
            msg = plural(total, "subnet", "s")
        elif subnet.prefixlen == subnet.max_prefixlen:
            msg = plural(total, "address", "es")
        else:
            msg = plural(total, "subnet", "s")
            if net.version == 4:
                msg += " (%s)" % subnet.netmask
            msg += " × "
            msg += plural(subnet.num_addresses, "address", "es")

        if net.version == 4 and subnet.prefixlen < subnet.max_prefixlen - 1:
            msg += " (total %d hosts)" % (total * (subnet.num_addresses - 2))

        print(msg)

    Core.exit()
except IndexError:
    usage()
    Core.die("not enough arguments")
except (ValueError, getopt.GetoptError) as e:
    Core.die(str(e))
