import decimal
from binascii import hexlify
import string


class Decimal(decimal.Decimal):
    def __new__(cls, value):
        return super().__new__(cls, str(value))


def chunk_data(data, size):
    return (data[i:i + size] for i in range(0, len(data), size))


def int_to_unknown_bytes(num, byteorder='big'):
    """Converts an int to the least number of bytes as possible."""
    return num.to_bytes((num.bit_length() + 7) // 8 or 1, byteorder)


def bytes_to_hex(bytestr, upper=False):
    hexed = hexlify(bytestr).decode()
    return hexed.upper() if upper else hexed


def hex_to_bytes(hexed):

    if len(hexed) & 1:
        hexed = '0' + hexed

    return bytes.fromhex(hexed)


def int_to_hex(num, upper=False):
    """Ensures that there is an even number of characters in the hex string"""
    hexed = hex(num)[2:]
    if len(hexed) % 2 != 0:
        hexed = '0' + hexed
    return hexed.upper() if upper else hexed


def hex_to_int(hexed):
    return int(hexed, 16)


def flip_hex_byte_order(string):
    return bytes_to_hex(hex_to_bytes(string)[::-1])


def int_to_varint(val):

    if val < 253:
        return val.to_bytes(1, 'little')
    elif val <= 65535:
        return b'\xfd'+val.to_bytes(2, 'little')
    elif val <= 4294967295:
        return b'\xfe'+val.to_bytes(4, 'little')
    else:
        return b'\xff'+val.to_bytes(8, 'little')


def is_valid_hex(s):
    """Can only detect if something definitely is *not* hex (could still return true by
    coincidence).
    But in this case all asm op codes begin with "OP_" as per https://en.bitcoin.it/wiki/Script.
    So all return False. The only exception to this is for OP_PUSHDATA codes 1 - 75."""
    return all(c in string.hexdigits for c in s)


def asm_to_list(asm):
    """Takes in Bitcoin Script Assembly code and returns:
    - op_codes (with 'OP_' prefixes) as string
    - raw hex data as bytes.
    """
    asm_list = []
    for pushdata in asm.split(' '):
        if not is_valid_hex(pushdata):
            asm_list.append(pushdata)
        else:
            asm_list.append(hex_to_bytes(pushdata))
    return asm_list