#!/usr/bin/env python3
#
# mmgen = Multi-Mode GENerator, a command-line cryptocurrency wallet
# Copyright (C)2013-2024 The MMGen Project <mmgen@tuta.io>
# Licensed under the GNU General Public License, Version 3:
#   https://www.gnu.org/licenses
# Public project repositories:
#   https://github.com/mmgen/mmgen-wallet
#   https://gitlab.com/mmgen/mmgen-wallet

"""
wallet.__init__: wallet class initializer
"""

import importlib
from collections import namedtuple

from ..util import die,get_extension
from ..objmethods import MMGenObject
from ..seed import Seed

_wd = namedtuple('wallet_data', ['type','name','ext','base_type','enc','fmt_codes'])

wallet_data = {
	'bip39':       _wd('bip39',       'BIP39Mnemonic',    'bip39',  'mnemonic',  False,('bip39',)),
	'brain':       _wd('brain',       'Brainwallet',      'mmbrain',None,        True, ('mmbrain','brainwallet','brain','bw')),
	'dieroll':     _wd('dieroll',     'DieRollWallet',    'b6d',    None,        False,('b6d','die','dieroll')),
	'incog':       _wd('incog',       'IncogWallet',      'mmincog','incog_base',True, ('mmincog','incog','icg','i')),
	'incog_hex':   _wd('incog_hex',   'IncogWalletHex',   'mmincox','incog_base',True, ('mmincox','incox','incog_hex','ix','xi')),
	'incog_hidden':_wd('incog_hidden','IncogWalletHidden',None,     'incog_base',True, ('incog_hidden','hincog','ih','hi')),
	'mmgen':       _wd('mmgen',       'MMGenWallet',      'mmdat',  None,        True, ('wallet','w')),
	'mmhex':       _wd('mmhex',       'MMGenHexSeedFile', 'mmhex',  None,        False,('seedhex','hexseed','mmhex')),
	'plainhex':    _wd('plainhex',    'PlainHexSeedFile', 'hex',    None,        False,('hex','rawhex','plainhex')),
	'seed':        _wd('seed',        'MMGenSeedFile',    'mmseed', None,        False,('mmseed','seed','s')),
	'words':       _wd('words',       'MMGenMnemonic',    'mmwords','mnemonic',  False,('mmwords','words','mnemonic','mn','m')),
}

def get_wallet_data(
		wtype       = None,
		fmt_code    = None,
		ext         = None,
		die_on_fail = False ):

	if wtype:
		return wallet_data[wtype]
	elif fmt_code:
		for v in wallet_data.values():
			if fmt_code in v.fmt_codes:
				return v
	elif ext is not None: # ext could be the empty string
		for v in wallet_data.values():
			if ext == v.ext:
				return v
	else:
		die(4,'no argument supplied!')

	if die_on_fail:
		if fmt_code:
			die(3, f'{fmt_code!r}: unrecognized wallet format code')
		else:
			die('BadFileExtension', f'{ext!r}: unrecognized wallet file extension')

def get_wallet_cls(
		wtype       = None,
		fmt_code    = None,
		ext         = None,
		die_on_fail = False ):

	return getattr(
		importlib.import_module( 'mmgen.wallet.{}'.format(
			wtype or
			get_wallet_data(
				fmt_code    = fmt_code,
				ext         = ext,
				die_on_fail = die_on_fail ).type
		)),
		'wallet' )

def get_wallet_extensions(key):
	return {
		'enc':   [v.ext for v in wallet_data.values() if v.enc],
		'unenc': [v.ext for v in wallet_data.values() if not v.enc]
	}[key]

def format_fmt_codes():
	d = [(
			v.name,
			('.' + v.ext if v.ext else 'None'),
			','.join(v.fmt_codes)
		) for v in wallet_data.values()]
	w = max(len(i[0]) for i in d)
	ret = [f'{a:<{w}}  {b:<9} {c}' for a,b,c in [
		('Format','FileExt','Valid codes'),
		('------','-------','-----------')
		] + sorted(d) ]
	return '\n'.join(ret) + '\n'

def _get_me(modname):
	return MMGenObject.__new__( getattr( importlib.import_module(f'mmgen.wallet.{modname}'), 'wallet' ) )

def Wallet(
	cfg,
	fn            = None,
	ss            = None,
	seed_bin      = None,
	seed          = None,
	passchg       = False,
	in_data       = None,
	ignore_in_fmt = False,
	in_fmt        = None,
	passwd_file   = None ):

	in_fmt = in_fmt or cfg.in_fmt

	ss_out = (
		get_wallet_data(
			fmt_code    = cfg.out_fmt,
			die_on_fail = True ).type
		if cfg.out_fmt else None )

	if seed or seed_bin:
		me = _get_me( ss_out or 'mmgen' ) # default to native wallet format
		me.seed = seed or Seed( cfg, seed_bin=seed_bin )
		me.op = 'new'
	elif ss:
		me = _get_me( ss.type if passchg else (ss_out or 'mmgen') )
		me.seed = ss.seed
		me.ss_in = ss
		me.op = 'pwchg_new' if passchg else 'conv'
	elif fn or cfg.hidden_incog_input_params:
		if fn:
			wd = get_wallet_data(ext=get_extension(fn),die_on_fail=True)
			if in_fmt and (not ignore_in_fmt) and in_fmt not in wd.fmt_codes:
				die(1,f'{in_fmt}: --in-fmt parameter does not match extension of input file')
			me = _get_me( wd.type )
		else:
			fn = ','.join(cfg.hidden_incog_input_params.split(',')[:-1]) # permit comma in filename
			me = _get_me( 'incog_hidden' )
		from ..filename import MMGenFile
		me.infile = MMGenFile( fn, subclass=type(me) )
		me.op = 'pwchg_old' if passchg else 'old'
	elif in_fmt:
		me = _get_me( get_wallet_data(fmt_code=in_fmt).type )
		me.op = 'pwchg_old' if passchg else 'old'
	else: # called with no arguments: initialize with random seed
		me = _get_me( ss_out or 'mmgen' ) # default to native wallet format
		me.seed = Seed(cfg)
		me.op = 'new'

	me.cfg = cfg

	me.__init__(
		in_data     = in_data,
		passwd_file = passwd_file )

	return me

def check_wallet_extension(fn):
	get_wallet_data( ext=get_extension(fn), die_on_fail=True ) # raises exception on failure
