"""
Lab002, часть 2.
Повреждение одного бита пакета без контроля целостности.

Цель:
1. Сформировать цифровой пакет.
2. Намеренно изменить один бит полезной нагрузки.
3. Разобрать повреждённый пакет.
4. Убедиться, что без CRC повреждение не обнаруживается.
"""

import struct


# ============================================================
# Константы протокола
# ============================================================

SYNC_WORD = 0xAA55
PROTOCOL_VERSION = 1
MESSAGE_TYPE_TEXT = 1

HEADER_FORMAT = ">HBBHH"


# ============================================================
# Формирование исходного пакета
# ============================================================

message = "ПРИВЕТ SDR"
payload = message.encode("utf-8")

sequence_number = 1
message_type = MESSAGE_TYPE_TEXT
payload_length = len(payload)

header = struct.pack(
    HEADER_FORMAT,
    SYNC_WORD,
    PROTOCOL_VERSION,
    message_type,
    sequence_number,
    payload_length,
)

packet = header + payload


print("Исходное сообщение:")
print(message)

print("\nИсходный пакет:")
print(packet.hex(" "))


# ============================================================
# Имитация повреждения пакета
# ============================================================

# bytes нельзя изменять напрямую.
# Поэтому преобразуем пакет в изменяемый массив bytearray.
corrupted_packet = bytearray(packet)

header_size = struct.calcsize(HEADER_FORMAT)

# В строке "ПРИВЕТ SDR":
#
# "ПРИВЕТ" занимает 12 байтов UTF-8,
# пробел занимает 1 байт,
# буква S находится по индексу 13 внутри PAYLOAD.
payload_byte_index = 13

# Полный индекс внутри пакета:
packet_byte_index = header_size + payload_byte_index

original_byte = corrupted_packet[packet_byte_index]

# XOR с 0x01 изменяет младший бит:
#
# 0x53 = 01010011 = S
# 0x52 = 01010010 = R
corrupted_packet[packet_byte_index] ^= 0x01

corrupted_byte = corrupted_packet[packet_byte_index]


print("\nИзменяемый байт внутри пакета:")

print(
    f"До повреждения:    "
    f"{original_byte:3d} "
    f"= 0x{original_byte:02X} "
    f"= {original_byte:08b}"
)

print(
    f"После повреждения: "
    f"{corrupted_byte:3d} "
    f"= 0x{corrupted_byte:02X} "
    f"= {corrupted_byte:08b}"
)

print("\nПовреждённый пакет:")
print(bytes(corrupted_packet).hex(" "))


# ============================================================
# Разбор повреждённого пакета
# ============================================================

received_header = corrupted_packet[:header_size]
received_payload = corrupted_packet[header_size:]

(
    received_sync,
    received_version,
    received_type,
    received_sequence,
    received_length,
) = struct.unpack(HEADER_FORMAT, received_header)


# ============================================================
# Проверки структуры
# ============================================================

assert received_sync == SYNC_WORD
assert received_version == PROTOCOL_VERSION
assert received_type == MESSAGE_TYPE_TEXT
assert received_sequence == sequence_number
assert received_length == len(received_payload)

print("\nВсе проверки заголовка пройдены.")

restored_message = bytes(received_payload).decode("utf-8")


print("\nСообщение после повреждения:")
print(restored_message)

print("\nИсходное сообщение:")
print(message)


# ============================================================
# Проверка результата эксперимента
# ============================================================

assert restored_message != message

print("\nПовреждение данных произошло.")
print("Но заголовок и длина пакета остались правильными.")
print("Без CRC приёмник не смог определить ошибку.")