import bitcoin.main as main
import bitcoin.transaction as tx

# Shared secrets and uncovering pay keys


def shared_secret_sender(scan_pubkey, ephem_privkey):
    shared_point = main.multiply(scan_pubkey, ephem_privkey)
    shared_secret = main.sha256(main.encode_pubkey(shared_point, 'bin_compressed'))
    return shared_secret


def shared_secret_receiver(ephem_pubkey, scan_privkey):
    shared_point = main.multiply(ephem_pubkey, scan_privkey)
    shared_secret = main.sha256(main.encode_pubkey(shared_point, 'bin_compressed'))
    return shared_secret


def uncover_pay_pubkey_sender(scan_pubkey, spend_pubkey, ephem_privkey):
    shared_secret = shared_secret_sender(scan_pubkey, ephem_privkey)
    return main.add_pubkeys(spend_pubkey, main.privtopub(shared_secret))


def uncover_pay_pubkey_receiver(scan_privkey, spend_pubkey, ephem_pubkey):
    shared_secret = shared_secret_receiver(ephem_pubkey, scan_privkey)
    return main.add_pubkeys(spend_pubkey, main.privtopub(shared_secret))


def uncover_pay_privkey(scan_privkey, spend_privkey, ephem_pubkey):
    shared_secret = shared_secret_receiver(ephem_pubkey, scan_privkey)
    return main.add_privkeys(spend_privkey, shared_secret)

# Address encoding

# Functions for basic stealth addresses,
# i.e. one scan key, one spend key, no prefix


def pubkeys_to_basic_stealth_address(scan_pubkey, spend_pubkey, magic_byte=42):
    # magic_byte = 42 for mainnet, 43 for testnet.
    hex_scankey = main.encode_pubkey(scan_pubkey, 'hex_compressed')
    hex_spendkey = main.encode_pubkey(spend_pubkey, 'hex_compressed')
    hex_data = '00{0:066x}01{1:066x}0100'.format(int(hex_scankey, 16), int(hex_spendkey, 16))
    addr = main.hex_to_b58check(hex_data, magic_byte)
    return addr


def basic_stealth_address_to_pubkeys(stealth_address):
    hex_data = main.b58check_to_hex(stealth_address)
    if len(hex_data) != 140:
        raise Exception('Stealth address is not of basic type (one scan key, one spend key, no prefix)')

    scan_pubkey = hex_data[2:68]
    spend_pubkey = hex_data[70:136]
    return scan_pubkey, spend_pubkey

# Sending stealth payments


def mk_stealth_metadata_script(ephem_pubkey, nonce):
    op_return = '6a'
    msg_size = '26'
    version = '06'
    return op_return + msg_size + version + '{0:08x}'.format(nonce) + main.encode_pubkey(ephem_pubkey, 'hex_compressed')


def mk_stealth_tx_outputs(stealth_addr, value, ephem_privkey, nonce, network='btc'):

    scan_pubkey, spend_pubkey = basic_stealth_address_to_pubkeys(stealth_addr)

    if network == 'btc':
        btc_magic_byte = 42
        if stealth_addr != pubkeys_to_basic_stealth_address(scan_pubkey, spend_pubkey, btc_magic_byte):
            raise Exception('Invalid btc mainnet stealth address: ' + stealth_addr)
        magic_byte_addr = 0

    elif network == 'testnet':
        testnet_magic_byte = 43
        if stealth_addr != pubkeys_to_basic_stealth_address(scan_pubkey, spend_pubkey, testnet_magic_byte):
            raise Exception('Invalid testnet stealth address: ' + stealth_addr)
        magic_byte_addr = 111

    ephem_pubkey = main.privkey_to_pubkey(ephem_privkey)
    output0 = {'script': mk_stealth_metadata_script(ephem_pubkey, nonce),
               'value': 0}

    pay_pubkey = uncover_pay_pubkey_sender(scan_pubkey, spend_pubkey, ephem_privkey)
    pay_addr = main.pubkey_to_address(pay_pubkey, magic_byte_addr)
    output1 = {'address': pay_addr,
               'value': value}

    return [output0, output1]

# Receiving stealth payments


def ephem_pubkey_from_tx_script(stealth_tx_script):
    if len(stealth_tx_script) != 80:
        raise Exception('Wrong format for stealth tx output')
    return stealth_tx_script[14:]
