#!/usr/bin/env python
#
# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
# Copyright (C) 2013 by philemon <mmgen-py@yandex.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

"""
mmgen-addrgen: Generate a list or range of addresses from a mmgen
               deterministic wallet.
               Call as 'btc-keygen' to allow key generation as well.
"""

import sys

from mmgen.Opts   import *
from mmgen.config import *
from mmgen.license import *
from mmgen.utils  import *
from mmgen.addr   import *

invoked_as = sys.argv[0].split("-")[-1]
gen_what = "addresses" if invoked_as == "addrgen" else "keys"

if invoked_as == "keygen":
	extra_help_data = (
		"\n-A, --no-addresses       Print only secret keys, no addresses",
		"\n-x, --b16                Print secret keys in hexadecimal too",
		"\nBy default, both addresses and secret keys are generated."
	)
else: extra_help_data = ("","","")

help_data = {
	'prog_name': sys.argv[0].split("/")[-1],
	'desc': """Generate a list or range of {} from a {} wallet,
	              mnemonic, seed or password""".format(gen_what,proj_name),
	'usage':"[opts] [infile] <address list>",
	'options': """
-h, --help               Print this help message{}
-d, --outdir          d  Specify an alternate directory 'd' for output
-e, --echo-passphrase    Display passphrase or mnemonic on screen upon entry
-K, --no-keyconv         Use internal libraries for address generation
                         instead of 'keyconv'
-l, --seed-len        N  Length of seed.  Options: {}
                         (default: {})
-p, --hash-preset     p  Use scrypt.hash() parameters from preset 'p'
                         when hashing password (default: '{}')
-P, --show-hash-presets  Show information on available hash presets
-q, --quiet              Suppress warnings; overwrite files without asking
-S, --stdout             Print {W} to stdout
-v, --verbose            Produce more verbose output{}

-b, --from-brain     l,p Generate {W} from a user-created password,
                         i.e. a "brainwallet", using seed length 'l' and
                         hash preset 'p' (comma-separated)
-m, --from-mnemonic      Generate {W} from an electrum-like mnemonic
-s, --from-seed          Generate {W} from a seed in .{S} format

Addresses are given in a comma-separated list.  Hyphen-separated
ranges are also allowed.{}

If available, the external 'keyconv' program will be used for address
generation.

Data for the --from-<what> options will be taken from <infile> if <infile>
is specified.  Otherwise, the user will be prompted to enter the data.

For passphrases all combinations of whitespace are equal, and leading and
trailing space are ignored.  This permits reading passphrase data from a
multi-line file with free spacing and indentation.  This is particularly
convenient for long brainwallet passphrases, for example.

BRAINWALLET NOTE:

As brainwallets require especially strong hashing to thwart dictionary
attacks, the brainwallet hash preset must be specified by the user, using
the 'p' parameter of the '--from-brain' option

The '--from-brain' option also requires the user to specify a seed length
(the 'l' parameter)

For a brainwallet passphrase to always generate the same keys and addresses,
the same 'l' and 'p' parameters to '--from-brain' must be used in all future
invocations with that passphrase
""".format(
		extra_help_data[0],
		", ".join([str(i) for i in seed_lens]),
		seed_len,
		hash_preset,
		extra_help_data[1],
		extra_help_data[2],
		W=gen_what,
		S=seed_ext
	)
}

so = "h","A","d:","e","K","l:","p:","P","q","S","v","x","b:","m","s"
lo = "help","no_addresses","outdir=","echo_passphrase","no_keyconv",\
		"seed_len=","hash_preset=","show_hash_presets","quiet","stdout",\
		"verbose","b16","from_brain=","from_mnemonic","from_seed"

if invoked_as == "addrgen":
	short_opts = so[0:1] + so[2:10] + so[11:]
	long_opts  = lo[0:1] + lo[2:10] + lo[11:]
else:
	short_opts,long_opts = so,lo

opts,cmd_args = process_opts(sys.argv,help_data,"".join(short_opts),long_opts)

if 'show_hash_presets' in opts: show_hash_presets()

# Sanity checking and processing of command-line arguments:

opts['gen_what'] = gen_what

set_if_unset_and_typeconvert(opts,(
	('hash_preset',hash_preset,'str'),
	('seed_len',seed_len,'int')
))

# Exits on invalid input
check_opts(opts,('hash_preset','seed_len','outdir','from_brain'))

if debug:
	print "Processed options:     %s" % repr(opts)
	print "Cmd args:              %s" % repr(cmd_args)

if   len(cmd_args) == 1 and (
			'from_mnemonic' in opts or
			'from_brain' in opts or
			'from_seed' in opts
		): infile,addr_list_arg = "",cmd_args[0]
elif len(cmd_args) == 2:
	infile,addr_list_arg = cmd_args
	check_infile(infile)
else: usage(help_data)

addr_list = parse_address_list(addr_list_arg)

if not 'quiet' in opts: do_license_msg()

# Interact with user:
if invoked_as == "keygen" and not 'quiet' in opts:
	confirm_or_exit(cmessages['unencrypted_secret_keys'], 'continue')

# Generate data:

if invoked_as == "addrgen":
	opts['print_addresses_only'] = True
else:
	if not 'no_addresses' in opts: opts['print_secret'] = True

seed          = get_seed(infile,opts)
seed_id       = make_chksum_8(seed)
addr_data     = generate_addrs(seed, addr_list, opts)
addr_data_str = format_addr_data(addr_data, seed_id, opts)

# Output data:
if 'stdout' in opts:
	if invoked_as == "keygen" and not 'quiet' in opts:
		confirm = True
	else: confirm = False
	write_to_stdout(addr_data_str,"secret keys",confirm)
elif not sys.stdout.isatty():
	write_to_stdout(addr_data_str,"secret keys",confirm=False)
else:
	write_addr_data_to_file(seed, addr_data_str, addr_list, opts)