import struct
import hashlib
import base58
from .Bech32 import encode as segwit_encode

# Read a var int at the index of `start_index` in the block data `blk_data`.
# Return the integer and the number of bytes that we read.
# Reference: https://en.bitcoin.it/wiki/Protocol_documentation#Variable_length_integer
def read_var_int(blk_data: bytes, start_index: int) -> tuple[int, int]:
  head = blk_data[start_index]
  if head == 0xfd:   # uint16_t
    (n,) = struct.unpack('<H', blk_data[start_index+1:start_index+3])
    return (n, 3)
  elif head == 0xfe: # uint32_t
    (n,) = struct.unpack('<L', blk_data[start_index+1:start_index+5])
    return (n, 5)
  elif head == 0xff: # uint64_t
    (n,) = struct.unpack('<Q', blk_data[start_index+1:start_index+9])
    return (n, 9)
  else:              # uint8_t
    n = head
    return (n, 1)

def int_to_var_int(num: int) -> bytes:
  assert(type(num) == int)
  assert(num >= 0)
  if num <= 0xfc:
    return num.to_bytes(1, 'big', signed=False)
  elif num <= 0xffff:
    return b'\xfd' + num.to_bytes(2, 'big', signed=False)
  elif num <= 0xffffffff:
    return b'\xfe' + num.to_bytes(4, 'big', signed=False)
  else:
    return b'\xff' + num.to_bytes(8, 'big', signed=False)


'''
Copied from: https://en.bitcoin.it/wiki/Script#Stack

This document is for information purposes only. De facto, 
Bitcoin script is defined by the code run by the network to check the validity of blocks.

The stacks hold byte vectors. When used as numbers, byte vectors are interpreted as little-endian variable-length integers with the most significant bit determining the sign of the integer. 
Thus 0x81 represents -1. 0x80 is another representation of zero (so called negative 0). 
Positive 0 is represented by a null-length vector. 
Byte vectors are interpreted as Booleans where False is represented by any representation of zero and True is represented by any representation of non-zero.

Leading zeros in an integer and negative zero are allowed in blocks but get rejected by the stricter requirements which standard full nodes put on transactions before retransmitting them. 
Byte vectors on the stack are not allowed to be more than 520 bytes long. 
Opcodes which take integers and bools off the stack require that they be no more than 4 bytes long, but addition and subtraction can overflow and result in a 5 byte integer being put on the stack.
'''


# The following functions are used to interpret the byte vector on stack,
# and convert the value to byte vector.
def int_to_byte_vector(n):
  assert(type(n) == int)
  if n == 0:
    return b''
  elif n >= 1 and n <= 127:
    return bytes([n])
  elif n <= -1 and n >= -127:
    bv = bytes([abs(n)])
    return bytes([
      bv[0] | b'\x80'[0] # Toggle the most significant bit.
    ])
  raise NotImplementedError

def bool_to_byte_vector(b):
  assert(type(b) == bool)
  if b:
    return b'\x01'
  else:
    return b''

def byte_vector_to_int(bv):
  bv_int = 0
  is_neg = False
  for i in range(len(bv)-1, -1, -1):
    bv_byte = bv[i]
    if i == len(bv) - 1:
      if bv_byte & 0x80 == 0x80: # If the most significant bit is 1, meaning the number is negative.
        bv_byte ^= 0x80 # Toggle the most significant bit.
        is_neg = True
    bv_int = (bv_int << 8) | bv_byte
  if is_neg:
    bv_int = -bv_int
  return bv_int

def byte_vector_to_bool(bv):
  if byte_vector_to_int(bv) == 0:
    return False
  else:
    return True

# Check if the byte vector is a valid representation of a 4-byte signed integer.
def is_byte_vector_valid_arithmetic_int(bv):
  return len(bv) == 4


# Hash helper functions.
def hash_ripemd160(bytes_data):
  return hashlib.new('ripemd160', bytes_data).digest()

def hash_sha1(bytes_data):
  return hashlib.sha1(bytes_data).digest()

def hash_sha256(bytes_data):
  return hashlib.sha256(bytes_data).digest()

def hash_hash160(bytes_data):
  first_hash = hashlib.sha256(bytes_data).digest()
  return hashlib.new('ripemd160', first_hash).digest()

def hash_hash256(bytes_data):
  first_hash = hashlib.sha256(bytes_data).digest()
  return hashlib.sha256(first_hash).digest()

# Helper functions for extracting the address.
def extract_addr_from_p2pkh(bytes_data: bytes) -> str:
  data = b'\x00' + bytes_data
  checksum = hash_hash256(data)[0:4]
  return base58.b58encode(data + checksum).decode('utf-8')

def extract_addr_from_p2sh(bytes_data: bytes) -> str:
  data = b'\x05' + bytes_data
  checksum = hash_hash256(data)[0:4]
  return base58.b58encode(data + checksum).decode('utf-8')

def extract_addr_from_segwit(version: int, bytes_data: bytes) -> str:
  return segwit_encode('bc', version, bytes_data)

import struct
import hashlib
import base58
from .Bech32 import encode as segwit_encode

# Read a var int at the index of `start_index` in the block data `blk_data`.
# Return the integer and the number of bytes that we read.
# Reference: https://en.bitcoin.it/wiki/Protocol_documentation#Variable_length_integer
def read_var_int(blk_data: bytes, start_index: int) -> tuple[int, int]:
  head = blk_data[start_index]
  if head == 0xfd:   # uint16_t
    (n,) = struct.unpack('<H', blk_data[start_index+1:start_index+3])
    return (n, 3)
  elif head == 0xfe: # uint32_t
    (n,) = struct.unpack('<L', blk_data[start_index+1:start_index+5])
    return (n, 5)
  elif head == 0xff: # uint64_t
    (n,) = struct.unpack('<Q', blk_data[start_index+1:start_index+9])
    return (n, 9)
  else:              # uint8_t
    n = head
    return (n, 1)

def int_to_var_int(num: int) -> bytes:
  assert(type(num) == int)
  assert(num >= 0)
  if num <= 0xfc:
    return num.to_bytes(1, 'big', signed=False)
  elif num <= 0xffff:
    return b'\xfd' + num.to_bytes(2, 'big', signed=False)
  elif num <= 0xffffffff:
    return b'\xfe' + num.to_bytes(4, 'big', signed=False)
  else:
    return b'\xff' + num.to_bytes(8, 'big', signed=False)


'''
Copied from: https://en.bitcoin.it/wiki/Script#Stack

This document is for information purposes only. De facto, 
Bitcoin script is defined by the code run by the network to check the validity of blocks.

The stacks hold byte vectors. When used as numbers, byte vectors are interpreted as little-endian variable-length integers with the most significant bit determining the sign of the integer. 
Thus 0x81 represents -1. 0x80 is another representation of zero (so called negative 0). 
Positive 0 is represented by a null-length vector. 
Byte vectors are interpreted as Booleans where False is represented by any representation of zero and True is represented by any representation of non-zero.

Leading zeros in an integer and negative zero are allowed in blocks but get rejected by the stricter requirements which standard full nodes put on transactions before retransmitting them. 
Byte vectors on the stack are not allowed to be more than 520 bytes long. 
Opcodes which take integers and bools off the stack require that they be no more than 4 bytes long, but addition and subtraction can overflow and result in a 5 byte integer being put on the stack.
'''


# The following functions are used to interpret the byte vector on stack,
# and convert the value to byte vector.
def int_to_byte_vector(n):
  assert(type(n) == int)
  if n == 0:
    return b''
  elif n >= 1 and n <= 127:
    return bytes([n])
  elif n <= -1 and n >= -127:
    bv = bytes([abs(n)])
    return bytes([
      bv[0] | b'\x80'[0] # Toggle the most significant bit.
    ])
  raise NotImplementedError

def bool_to_byte_vector(b):
  assert(type(b) == bool)
  if b:
    return b'\x01'
  else:
    return b''

def byte_vector_to_int(bv):
  bv_int = 0
  is_neg = False
  for i in range(len(bv)-1, -1, -1):
    bv_byte = bv[i]
    if i == len(bv) - 1:
      if bv_byte & 0x80 == 0x80: # If the most significant bit is 1, meaning the number is negative.
        bv_byte ^= 0x80 # Toggle the most significant bit.
        is_neg = True
    bv_int = (bv_int << 8) | bv_byte
  if is_neg:
    bv_int = -bv_int
  return bv_int

def byte_vector_to_bool(bv):
  if byte_vector_to_int(bv) == 0:
    return False
  else:
    return True

# Check if the byte vector is a valid representation of a 4-byte signed integer.
def is_byte_vector_valid_arithmetic_int(bv):
  return len(bv) == 4


# Hash helper functions.
def hash_ripemd160(bytes_data):
  return hashlib.new('ripemd160', bytes_data).digest()

def hash_sha1(bytes_data):
  return hashlib.sha1(bytes_data).digest()

def hash_sha256(bytes_data):
  return hashlib.sha256(bytes_data).digest()

def hash_hash160(bytes_data):
  first_hash = hashlib.sha256(bytes_data).digest()
  return hashlib.new('ripemd160', first_hash).digest()

def hash_hash256(bytes_data):
  first_hash = hashlib.sha256(bytes_data).digest()
  return hashlib.sha256(first_hash).digest()

# Helper functions for extracting the address.
def extract_addr_from_p2pkh(bytes_data: bytes) -> str:
  data = b'\x00' + bytes_data
  checksum = hash_hash256(data)[0:4]
  return base58.b58encode(data + checksum).decode('utf-8')

def extract_addr_from_p2sh(bytes_data: bytes) -> str:
  data = b'\x05' + bytes_data
  checksum = hash_hash256(data)[0:4]
  return base58.b58encode(data + checksum).decode('utf-8')

def extract_addr_from_segwit(version: int, bytes_data: bytes) -> str:
  return segwit_encode('bc', version, bytes_data)

# Read a var int at the index of `start_index` in the block data `blk_data`. # Return the integer and the number of bytes that we read. # Reference: https://en.bitcoin.it/wiki/Protocol_documentation#Variable_length_integer # uint16_t # uint32_t # uint64_t # uint8_t Copied from: https://en.bitcoin.it/wiki/Script#Stack  This document is for information purposes only. De facto,  Bitcoin script is defined by the code run by the network to check the validity of blocks.  The stacks hold byte vectors. When used as numbers, byte vectors are interpreted as little-endian variable-length integers with the most significant bit determining the sign of the integer.  Thus 0x81 represents -1. 0x80 is another representation of zero (so called negative 0).  Positive 0 is represented by a null-length vector.  Byte vectors are interpreted as Booleans where False is represented by any representation of zero and True is represented by any representation of non-zero.  Leading zeros in an integer and negative zero are allowed in blocks but get rejected by the stricter requirements which standard full nodes put on transactions before retransmitting them.  Byte vectors on the stack are not allowed to be more than 520 bytes long.  Opcodes which take integers and bools off the stack require that they be no more than 4 bytes long, but addition and subtraction can overflow and result in a 5 byte integer being put on the stack. # The following functions are used to interpret the byte vector on stack, # and convert the value to byte vector. # Toggle the most significant bit. # If the most significant bit is 1, meaning the number is negative. # Toggle the most significant bit. # Check if the byte vector is a valid representation of a 4-byte signed integer. # Hash helper functions. # Helper functions for extracting the address.