from .cdefs import MIN_TX_SIZE, MAX_TXOUT_PUBKEY_SCRIPT
from .mininode import CTransaction, FromHex, ToHex, CTxOut
from .script import OP_RETURN, CScript

import random
from binascii import hexlify, unhexlify

# Pad outputs until it reaches at least min_size


def pad_tx(tx, min_size=None):
    if min_size is None:
        min_size = MIN_TX_SIZE

    curr_size = len(tx.serialize())

    while curr_size < min_size:
        # txout.value + txout.pk_script bytes + op_return
        extra_bytes = 8 + 1 + 1
        padding_len = max(0, min_size - curr_size - extra_bytes)
        padding_len = min(padding_len, MAX_TXOUT_PUBKEY_SCRIPT)
        if padding_len == 0:
            tx.vout.append(CTxOut(0, CScript([OP_RETURN])))
        else:
            padding = random.randrange(
                1 << 8 * padding_len - 2, 1 << 8 * padding_len - 1)
            tx.vout.append(
                CTxOut(0, CScript([padding, OP_RETURN])))
        curr_size = len(tx.serialize())

    tx.rehash()

# Pad outputs until it reaches at least min_size


def pad_raw_tx(rawtx_hex, min_size=None):
    tx = CTransaction()
    FromHex(tx, rawtx_hex)
    pad_tx(tx, min_size)
    return ToHex(tx)

from .cdefs import MIN_TX_SIZE, MAX_TXOUT_PUBKEY_SCRIPT
from .mininode import CTransaction, FromHex, ToHex, CTxOut
from .script import OP_RETURN, CScript

import random
from binascii import hexlify, unhexlify


def pad_tx(tx, pad_to_size=MIN_TX_SIZE):
    """
    Pad a transaction with op_return junk data until it is at least pad_to_size, or 
    leave it alone if it's already bigger than that.
    """
    curr_size = len(tx.serialize())
    if curr_size >= pad_to_size:
        # Bail early txn is already big enough
        return

    # This code attempts to pad a transaction with opreturn vouts such that
    # it will be exactly pad_to_size.  In order to do this we have to create
    # vouts of size x (maximum OP_RETURN size - vout overhead), plus the final
    # one subsumes any runoff which would be less than vout overhead.
    #
    # There are two cases where this is not possible:
    # 1. The transaction size is between pad_to_size and pad_to_size - extrabytes
    # 2. The transaction is already greater than pad_to_size
    #
    # Visually:
    # | .. x  .. | .. x .. | .. x .. | .. x + desired_size % x |
    #    VOUT_1     VOUT_2    VOUT_3    VOUT_4
    # txout.value + txout.pk_script bytes + op_return
    extra_bytes = 8 + 1 + 1
    required_padding = pad_to_size - curr_size
    while required_padding > 0:
        # We need at least extra_bytes left over each time, or we can't
        # subsume the final (and possibly undersized) iteration of the loop
        padding_len = min(required_padding,
                          MAX_TXOUT_PUBKEY_SCRIPT - extra_bytes)
        assert padding_len >= 0, "Can't pad less than 0 bytes, trying {}".format(
            padding_len)
        # We will end up with less than 1 UTXO of bytes after this, add
        # them to this txn
        next_iteration_padding = required_padding - padding_len - extra_bytes
        if next_iteration_padding > 0 and next_iteration_padding < extra_bytes:
            padding_len += next_iteration_padding

        # If we're at exactly, or below, extra_bytes we don't want a 1 extra byte padding
        if padding_len <= extra_bytes:
            tx.vout.append(CTxOut(0, CScript([OP_RETURN])))
        else:
            # Subtract the overhead for the TxOut
            padding_len -= extra_bytes
            padding = random.randrange(
                1 << 8 * padding_len - 2, 1 << 8 * padding_len - 1)
            tx.vout.append(
                CTxOut(0, CScript([padding, OP_RETURN])))

        curr_size = len(tx.serialize())
        required_padding = pad_to_size - curr_size
    assert curr_size >= pad_to_size, "{} !>= {}".format(curr_size, pad_to_size)
    tx.rehash()


def pad_raw_tx(rawtx_hex, min_size=MIN_TX_SIZE):
    """
    Pad a raw transaction with OP_RETURN data until it reaches at least min_size
    """
    tx = CTransaction()
    FromHex(tx, rawtx_hex)
    pad_tx(tx, min_size)
    return ToHex(tx)

fix pad_tx to better match the desired size