# Support functions for the SWIG python wrapper
from sys import version as _wally_py_version

def _wrap_bin(fn, length, resize=False):
    """ Wrap functions that take an output buffer to create/return it """
    def wrapped(*args):
        n = length(*args) if callable(length) else length
        buf = bytearray(n)
        ret = fn(*list(args)+[buf])
        if resize:
            # Truncate buf to bytes written if needed. Also assert the
            # wrapper allocated enough space for the returned value to fit.
            assert ret <= n
            return buf[0:ret] if ret != n else buf
        return (ret, buf) if ret is not None else buf
    return wrapped

def _wrap_with_defaults(fn, min_args, max_args):
    """ Wrap functions that take argument defaults """
    def wrapped(*args):
        args = list(args)
        n = len(args)
        assert n >= min_args
        r = max_args - n
        return fn(*args+[None]*r)
    return wrapped

_unwrapped_hex_to_bytes = hex_to_bytes
if int(_wally_py_version[0]) >= 3:
    def hex_to_bytes(hex_str):
        buf = bytearray(len(hex_str) // 2)
        _unwrapped_hex_to_bytes(hex_str, buf)
        return buf
else:
    def hex_to_bytes(hex_str):
        hex_str = hex_str.encode('ascii')
        buf = bytearray(len(hex_str) // 2)
        _unwrapped_hex_to_bytes(bytes(hex_str), buf)
        return buf

sha256 = _wrap_bin(sha256, SHA256_LEN)
sha256_midstate = _wrap_bin(sha256_midstate, SHA256_LEN)
sha256d = _wrap_bin(sha256d, SHA256_LEN)
sha512 = _wrap_bin(sha512, SHA512_LEN)
hash160 = _wrap_bin(hash160, HASH160_LEN)
hmac_sha256 = _wrap_bin(hmac_sha256, HMAC_SHA256_LEN)
hmac_sha512 = _wrap_bin(hmac_sha512, HMAC_SHA512_LEN)

bip32_key_serialize = _wrap_bin(bip32_key_serialize, BIP32_SERIALIZED_LEN)
bip32_key_get_chain_code = _wrap_bin(bip32_key_get_chain_code, 32)
bip32_key_get_priv_key = _wrap_bin(bip32_key_get_priv_key, 32)
bip32_key_get_pub_key = _wrap_bin(bip32_key_get_pub_key, 33)
bip32_key_get_parent160 = _wrap_bin(bip32_key_get_parent160, HASH160_LEN)
bip32_key_get_hash160 = _wrap_bin(bip32_key_get_hash160, HASH160_LEN)

bip38_raw_from_private_key = _wrap_bin(bip38_raw_from_private_key, BIP38_SERIALIZED_LEN)
bip38_raw_to_private_key = _wrap_bin(bip38_raw_to_private_key, 32);
bip38_to_private_key = _wrap_bin(bip38_raw_to_private_key, 32);
bip39_mnemonic_to_seed512 = _wrap_bin(bip39_mnemonic_to_seed, BIP39_SEED_LEN_512)

pbkdf2_hmac_sha256 = _wrap_bin(pbkdf2_hmac_sha256, PBKDF2_HMAC_SHA256_LEN)
pbkdf2_hmac_sha512 = _wrap_bin(pbkdf2_hmac_sha512, PBKDF2_HMAC_SHA512_LEN)

ec_public_key_decompress = _wrap_bin(ec_public_key_decompress, EC_PUBLIC_KEY_UNCOMPRESSED_LEN)
ec_public_key_from_private_key = _wrap_bin(ec_public_key_from_private_key, EC_PUBLIC_KEY_LEN)
ec_sig_from_bytes = _wrap_bin(ec_sig_from_bytes, EC_SIGNATURE_LEN)
ec_sig_from_der = _wrap_bin(ec_sig_from_der, EC_SIGNATURE_LEN)
ec_sig_normalize = _wrap_bin(ec_sig_normalize, EC_SIGNATURE_LEN)
ec_sig_to_der = _wrap_bin(ec_sig_to_der, EC_SIGNATURE_DER_MAX_LEN, resize=True)

def base58check_from_bytes(buf):
    return base58_from_bytes(buf, BASE58_FLAG_CHECKSUM)

def _base58_len_fn(base58, flags):
    return len(base58) + (4 if (flags & BASE58_FLAG_CHECKSUM) else 0)
base58_to_bytes = _wrap_bin(base58_to_bytes, _base58_len_fn, resize=True)

def base58check_to_bytes(base58):
    return base58_to_bytes(base58, BASE58_FLAG_CHECKSUM)

def _format_bitcoin_message_len_fn(msg, flags):
    if flags & BITCOIN_MESSAGE_FLAG_HASH:
        return SHA256_LEN
    msg_len = len(msg)
    return 25 + msg_len + (1 if msg_len < 253 else 3)
format_bitcoin_message = _wrap_bin(format_bitcoin_message, _format_bitcoin_message_len_fn, resize=True)

asset_generator_from_bytes = _wrap_bin(asset_generator_from_bytes, ASSET_GENERATOR_LEN)
asset_final_vbf = _wrap_bin(asset_final_vbf, ASSET_TAG_LEN)
asset_value_commitment = _wrap_bin(asset_value_commitment, ASSET_COMMITMENT_LEN)
asset_rangeproof = _wrap_bin(asset_rangeproof, ASSET_RANGEPROOF_MAX_LEN, resize=True)
def _asset_surjectionproof_len_fn(aid, ag, gen, r, in_aid, in_abf, in_ags):
    return asset_surjectionproof_size(len(in_aid) // ASSET_TAG_LEN)
asset_surjectionproof = _wrap_bin(asset_surjectionproof, _asset_surjectionproof_len_fn, resize=True)

def _asset_unblind_fn(fn):
    def wrapped(*args):
        buffers = [bytearray(ASSET_TAG_LEN) for _ in xrange(3)]
        ret = fn(*list(args) + buffers)
        return ret, buffers[0], buffers[1], buffers[2]
    return wrapped
asset_unblind = _asset_unblind_fn(asset_unblind)

def _script_push_from_bytes_len_fn(data, flags):
    if flags & WALLY_SCRIPT_HASH160:
        return HASH160_LEN + 1
    elif flags & WALLY_SCRIPT_SHA256:
        return SHA256_LEN + 1
    push_len, opcode_len = len(data), 5
    for l, op_len in [(76, 1), (256, 2), (65536, 3)]:
        if push_len < l:
            opcode_len = op_len
            break
    return push_len + opcode_len
script_push_from_bytes = _wrap_bin(script_push_from_bytes, _script_push_from_bytes_len_fn, resize=True)
def _spkcsv2of2_1_len_fn(sigs, csv_blocks, flags):
    # TODO: If we expose scriptint functions we can calc the real length here
    # instead of hardcoding 4 (largest push for the max csv_blocks of 0xffff)
    return 9 + 2 * (EC_PUBLIC_KEY_LEN + 1) + 4
scriptpubkey_csv_2of2_then_1_from_bytes = _wrap_bin(scriptpubkey_csv_2of2_then_1_from_bytes, _spkcsv2of2_1_len_fn, resize=True)
def _spkcsv2of3_2_len_fn(sigs, csv_blocks, flags):
    # TODO: See TODO in _spkcsv2of2_1_len_fn
    return 13 + 3 * (EC_PUBLIC_KEY_LEN + 1) + 4
scriptpubkey_csv_2of3_then_2_from_bytes = _wrap_bin(scriptpubkey_csv_2of3_then_2_from_bytes, _spkcsv2of3_2_len_fn, resize=True)
scriptpubkey_op_return_from_bytes = _wrap_bin(scriptpubkey_op_return_from_bytes, WALLY_SCRIPTPUBKEY_OP_RETURN_MAX_LEN, resize=True)
scriptpubkey_p2pkh_from_bytes = _wrap_bin(scriptpubkey_p2pkh_from_bytes, WALLY_SCRIPTPUBKEY_P2PKH_LEN, resize=True)
scriptpubkey_p2sh_from_bytes = _wrap_bin(scriptpubkey_p2sh_from_bytes, WALLY_SCRIPTPUBKEY_P2SH_LEN, resize=True)
def _spkmfb_len_fn(sigs, threshold, flags):
    return 3 + (len(sigs) // EC_PUBLIC_KEY_LEN) * (EC_PUBLIC_KEY_LEN + 1)
scriptpubkey_multisig_from_bytes = _wrap_bin(scriptpubkey_multisig_from_bytes, _spkmfb_len_fn, resize=True)
def _ssmfb_len_fn(script, sigs, sighashes, flags):
    der_len = (len(sigs) // EC_SIGNATURE_LEN) * (EC_SIGNATURE_DER_MAX_LEN + 2)
    script_len = _script_push_from_bytes_len_fn(script, 0)
    return 1 + der_len + script_len
scriptsig_multisig_from_bytes = _wrap_bin(scriptsig_multisig_from_bytes, _ssmfb_len_fn, resize=True)

scriptsig_p2pkh_from_sig = _wrap_bin(scriptsig_p2pkh_from_sig, WALLY_SCRIPTSIG_P2PKH_MAX_LEN, resize=True)
scriptsig_p2pkh_from_der = _wrap_bin(scriptsig_p2pkh_from_der, WALLY_SCRIPTSIG_P2PKH_MAX_LEN, resize=True)

witness_program_from_bytes = _wrap_bin(witness_program_from_bytes, WALLY_WITNESSSCRIPT_MAX_LEN, resize=True)

addr_segwit_to_bytes = _wrap_bin(addr_segwit_to_bytes, 32 + 2, resize=True)

tx_to_bytes = _wrap_bin(tx_to_bytes, tx_get_length, resize=True)
tx_get_btc_signature_hash = _wrap_bin(tx_get_btc_signature_hash, SHA256_LEN)
tx_get_signature_hash = _wrap_bin(tx_get_signature_hash, SHA256_LEN)
tx_input_get_txhash = _wrap_bin(tx_input_get_txhash, WALLY_TXHASH_LEN)
tx_input_get_script = _wrap_bin(tx_input_get_script, tx_input_get_script_len, resize=True)
def _tx_input_get_witness_len_fn(tx_input_in, index):
    return tx_input_get_witness_len(tx_input_in, index)
tx_input_get_witness = _wrap_bin(tx_input_get_witness, _tx_input_get_witness_len_fn, resize=True)
tx_output_get_script = _wrap_bin(tx_output_get_script, tx_output_get_script_len, resize=True)

tx_get_input_txhash = _wrap_bin(tx_get_input_txhash, WALLY_TXHASH_LEN)
tx_get_input_script = _wrap_bin(tx_get_input_script, tx_get_input_script_len, resize=True)
tx_get_input_witness = _wrap_bin(tx_get_input_witness, tx_get_input_witness_len, resize=True)
tx_get_output_script = _wrap_bin(tx_get_output_script, tx_get_output_script_len, resize=True)

def tx_witness_stack_create(witnesses):
    """ Helper to create a witness stack from a list of witness programs """
    witness_stack = tx_witness_stack_init(len(witnesses))
    for witness_script in witnesses:
        tx_witness_stack_add(witness_stack, witness_script)
    return witness_stack

wif_to_bytes = _wrap_bin(wif_to_bytes, EC_PRIVATE_KEY_LEN)
def _wif_to_public_key_len(wif, prefix):
    return EC_PUBLIC_KEY_UNCOMPRESSED_LEN if wif_is_uncompressed(wif) else EC_PUBLIC_KEY_LEN
wif_to_public_key = _wrap_bin(wif_to_public_key, _wif_to_public_key_len, resize=True)

if is_elements_build():
    tx_elements_input_init = _wrap_with_defaults(tx_elements_input_init, 4, 12)
    tx_elements_output_init = _wrap_with_defaults(tx_elements_output_init, 3, 6)
    tx_confidential_value_from_satoshi = _wrap_bin(tx_confidential_value_from_satoshi, WALLY_TX_ASSET_CT_VALUE_UNBLIND_LEN)
    tx_elements_issuance_generate_entropy = _wrap_bin(tx_elements_issuance_generate_entropy, SHA256_LEN)
    tx_elements_issuance_calculate_asset = _wrap_bin(tx_elements_issuance_calculate_asset, SHA256_LEN)
    tx_elements_issuance_calculate_reissuance_token = _wrap_bin(tx_elements_issuance_calculate_reissuance_token, SHA256_LEN)
    tx_get_elements_signature_hash = _wrap_bin(tx_get_elements_signature_hash, SHA256_LEN)

WALLY_SATOSHI_MAX = WALLY_BTC_MAX * WALLY_SATOSHI_PER_BTC
