# Kyler Olsen
# CS 2450 Final Project
# Apr 2025

from __future__ import annotations
from typing import TYPE_CHECKING
import struct

if TYPE_CHECKING:
    from socket import socket

def pack_varint(data: int) -> bytes:
    ordinal = b''
    while True:
        byte = data & 0x7F
        data >>= 7
        ordinal += struct.pack('B', byte | (0x80 if data > 0 else 0))
        if data == 0:
            break
    return ordinal

def unpack_varint(conn: socket) -> int:
    data = 0
    for i in range(5):
        ordinal = conn.recv(1)
        if len(ordinal) == 0:
            break
        byte = ord(ordinal)
        data |= (byte & 0x7F) << 7*i
        if not byte & 0x80:
            break
    return data

def pack_string(text: str) -> bytes:
    data = pack_varint(len(text))
    data += text.encode('utf-8')
    return data

def unpack_string(conn: socket) -> str:
    length = unpack_varint(conn)
    data = conn.recv(length)
    text = data.decode('utf-8')
    return text

def pack_varint_array(array: list[int]) -> bytes:
    data = pack_varint(len(array))
    for i in array:
        data += pack_varint(i)
    return data

def unpack_varint_array(conn: socket) -> list[int]:
    length = unpack_varint(conn)
    array: list[int] = []
    for _ in range(length):
        array.append(unpack_varint(conn))
    return array

def pack_string_array(array: list[str]) -> bytes:
    data = pack_varint(len(array))
    for i in array:
        data += pack_string(i)
    return data

def unpack_string_array(conn: socket) -> list[str]:
    length = unpack_varint(conn)
    array: list[str] = []
    for _ in range(length):
        array.append(unpack_string(conn))
    return array
