#!/usr/bin/env python
"""
generate cryptographically secure ASCII passwords
"""
import sys
import math
import random
import argparse

xUpper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
xLower = "abcdefghijklmnopqrstuvwxyz"
xNums = "0123456789"
CharacterSets = {
    "safe": "ACDEHLMNPRTabcdeghmnpqrt3479",
    "alnum": xUpper + xLower + xNums,
    "uppercase":  xUpper,
    "lowercase":  xLower,
    "numeric": xNums,
    "all": "!#$%&()*+,-./" + xNums + ":;<=>?@" + xUpper + "[]^_" + xLower + "{|}~",
    "base64": xUpper + xLower + xNums + "+/",
    "de-en": "ABCDEFGHIJKLMNOPQRSTUVWXabcdefghijklmnopqrstuvwx0123456789,.!$%",
}


def int_greater_than_zero(x):
    x = int(x)
    if x < 1: raise ValueError("value must be greater than zero")
    return x

def character_set(x):
    try:
        return CharacterSets[x.lower()]
    except KeyError:
        return x


if __name__ == "__main__":
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("-b", "--bits", metavar="N", type=int_greater_than_zero,
                        help="ensure at least N bits of entropy")
    parser.add_argument("-l", "--length", metavar="N", type=int_greater_than_zero,
                        help="set password length in characters")
    parser.add_argument("-c", "--charset", metavar="NAME", type=character_set,
                        help="specify characters to use, or select a pre-defined character set: " +
                        ', '.join(sorted(CharacterSets)))
    args = parser.parse_args()

    if not(args.charset) and not(args.bits or args.length):
        parser.error("need to specify -c/--charset and either -b/--bits or -l/--length")
    length = args.length or int(math.ceil(args.bits * math.log(2) / math.log(len(args.charset))))

    r = random.SystemRandom()
    print ''.join(r.choice(args.charset) for x in range(length))
