import tkinter as tk
from tkinter import filedialog
import struct

class Application(tk.Frame):
    def __init__(self, master):
        super().__init__(master)
        self.master = master
        self.filename = ''
        self.initUI()

    def initUI(self):
        menubar = tk.Menu(self.master)
        filemenu = tk.Menu(menubar, tearoff=False)
        filemenu.add_command(label='Open', command=self.opendialog)
        menubar.add_cascade(menu=filemenu, label='File')
        self.master.config(menu=menubar)

        self.table = tk.Frame(self.master)
        self.table.pack(fill=tk.BOTH, expand=True)

        tk.Label(self.table, text='APID').grid(row=0, column=0, padx=10, pady=5)
        tk.Label(self.table, text='Sequence Count').grid(row=0, column=1, padx=10, pady=5)
        tk.Label(self.table, text='Packet Length').grid(row=0, column=2, padx=10, pady=5)
        tk.Label(self.table, text='Data').grid(row=0, column=3, padx=10, pady=5)

    def opendialog(self):
        self.filename = filedialog.askopenfilename(defaultextension='.bin', filetypes=[('Binary Files', '.bin'), ('All Files', '.*')])
        if not self.filename:
            return
        self.process_file()

    def process_file(self):
        with open(self.filename, 'rb') as f:
            # Read the entire file contents as bytes
            file_contents = f.read()

            # Split the file contents into individual packets
            packet_delimiter = b'\x03' # CCSDS packet delimiter
            packets = file_contents.split(packet_delimiter)

            # Process each packet separately
            for index, packet in enumerate(packets):
                # Verify that the packet is a valid packet
                if not self.is_valid_packet(packet):
                    continue

                # Decode the packet header fields
                header_size, apid, sequence_count, packet_length = self.decode_packet_header(packet)

                # Extract the packet data
                packet_data = packet[header_size:]

                # Create a table row for the packet
                row = tk.Frame(self.table)
                row.pack(fill=tk.X, padx=5, pady=5)

                tk.Label(row, text=hex(apid)).grid(row=0, column=0, padx=10, pady=5)
                tk.Label(row, text=sequence_count).grid(row=0, column=1, padx=10, pady=5)
                tk.Label(row, text=packet_length).grid(row=0, column=2, padx=10, pady=5)
                tk.Label(row, text=self.format_packet_data(packet_data)).grid(row=0, column=3, padx=10, pady=5)

    def is_valid_packet(self, packet):
        # Verify that the packet contains the correct header and footer fields
        if len(packet) < 8 or packet[0] != 0x1A or packet[1] != 0xCF or packet[-2] != 0x0D or packet[-1] != 0x0A:
            print('invalid Packet', packet)
            return False
        # Verify that the PID is 50 and the category is 12
        print('GOOD Packet', packet)
        pid, category = struct.unpack('>HB', packet[4:7])
        return pid == 50 and category == 12

    def decode_packet_header(self, packet):
        # Extract the fields from the packet header
        apid = struct.unpack('>H', packet[7:9])[0]
        sequence_flags, sequence_count = struct.unpack('>BH', packet[9:12])
        packet_length = struct.unpack('>H', packet[2:4])[0] - 1

        # Calculate the header size based on the packet length field
        if packet_length < 128:
            header_size = 6
        elif packet_length < 16384:
            header_size = 7
        else:
            header_size = 8

        return header_size, apid, sequence_count & 0x3FFF, packet_length

    def format_packet_data(self, packet_data):
        # Format the packet data as a string of hex values
        hex_values = [hex(b) for b in packet_data]
        # Split the string into groups of 8 hex values for better readability
        hex_groups = [hex_values[i:i+8] for i in range(0, len(hex_values), 8)]
        return '\n'.join([' '.join(group) for group in hex_groups])

root = tk.Tk()
root.geometry('800x600')
app = Application(root)
root.mainloop()