# analisa_binara_gui.py
# GUI complet pentru extragere + analiză avansată a datelor binare (ASCII)

import tkinter as tk
from tkinter import filedialog, messagebox, scrolledtext, ttk

# Folosim backend-ul potrivit pentru Tkinter
import matplotlib
matplotlib.use('TkAgg')

from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure

import os
import re
from collections import Counter
import threading

# -------------------------------
# Funcții pentru extragere
# -------------------------------

def este_caracter_valid(caracter: str) -> bool:
    cod_ascii = ord(caracter)
    if (48 <= cod_ascii <= 57) or (65 <= cod_ascii <= 90) or (97 <= cod_ascii <= 122) \
       or caracter in [' ', '.', ',', ':', ';', '!','?','-','_','@','/','\\','(' ,')',"'", '"']:
        return True
    return False

def extrage_caractere_valide(secventa_binara: str, offset: int, pas: int):
    """
    Extrage caractere ASCII plauzibile din secvența de biți, luând octeți de 8 biți
    începând de la offset, avansând cu 'pas' biți.
    """
    rezultate = []
    # Ne asigurăm că avansăm cel puțin cu 1 bit
    pas = max(1, int(pas))
    lung = len(secventa_binara)
    for i in range(offset, lung - 7, pas):
        grup_biti = secventa_binara[i:i+8]
        try:
            valoare_ascii = int(grup_biti, 2)
            if 0 <= valoare_ascii <= 255:
                caracter = chr(valoare_ascii)
                if este_caracter_valid(caracter):
                    rezultate.append((i, caracter))
        except Exception:
            # Ignorăm conversiile eșuate
            continue
    return rezultate

def ruleaza_extragere(continut_binara: str, fisier_rezultate: str) -> str:
    # Păstrăm doar 0 și 1
    secventa_binara = ''.join(bit for bit in continut_binara if bit in ('0', '1'))
    if not secventa_binara:
        return "Eroare: Nu s-au găsit biți valizi (doar 0 și 1)."

    with open(fisier_rezultate, 'w', encoding='utf-8') as f:
        f.write("=== EXTRAGERE DATE DIN FIȘIER BINAR ===\n")
        f.write(f"Lungime secvență: {len(secventa_binara)} biți\n\n")

        metode = [
            ("METODA 1: 8 biți consecutivi (pas=1)", 1),
            ("METODA 2: 8 biți din 2 în 2 (pas=2)", 2),
            ("METODA 3: 8 biți din 7 în 7 (pas=7)", 7),
        ]

        for nume_metoda, pas in metode:
            f.write(f"\n=== {nume_metoda} ===\n")
            gasit_ceva = False
            for offset in range(8):
                rezultate = extrage_caractere_valide(secventa_binara, offset, pas)
                if rezultate:
                    gasit_ceva = True
                    f.write(f"\nOffset {offset}, pas {pas}:\n")
                    f.write("Poziție\tCaracter\n")
                    for poz, car in rezultate:
                        # Înlocuim newline/tab dacă apar accidental
                        c = car.replace('\n', ' ').replace('\t', ' ')
                        f.write(f"{poz}\t{c}\n")
                    text = ''.join(car for _, car in rezultate)
                    f.write(f"\nText posibil: {text}\n")
            if not gasit_ceva:
                f.write("Niciun caracter valid găsit cu această metodă.\n")

    return f"Extracție finalizată. Rezultate salvate în '{fisier_rezultate}'."

# -------------------------------
# Funcții de analiză avansată
# -------------------------------

def cauta_cuvinte_cunoscute(fisier_rezultate: str, output_widget: tk.Text):
    try:
        with open(fisier_rezultate, 'r', encoding='utf-8') as f:
            continut = f.read()

        cuvinte_comune = [
            # RO
            'si', 'în', 'in', 'la', 'de', 'pe', 'cu', 'pentru', 'este', 'sunt', 'au', 'care',
            # EN
            'the', 'and', 'to', 'of', 'is', 'you', 'that', 'it', 'he', 'was', 'for', 'on',
            'are', 'with', 'as', 'his', 'they', 'at', 'be'
        ]

        output_widget.insert(tk.END, "\n\n=== CĂUTARE CUVINTE CUNOSCUTE ===\n")
        gasit = False
        for cuvant in cuvinte_comune:
            # căutare ca „cuvânt întreg”, tolerantă la margini
            pattern = r'(?i)(?<![a-zăâîșțA-Z])' + re.escape(cuvant) + r'(?![a-zăâîșțA-Z])'
            matches = re.findall(pattern, continut)
            if matches:
                output_widget.insert(tk.END, f"Cuvântul '{cuvant}' apare de {len(matches)} ori\n")
                gasit = True
        if not gasit:
            output_widget.insert(tk.END, "Nu am găsit niciun cuvânt comun.\n")
    except Exception as e:
        output_widget.insert(tk.END, f"Eroare la căutare cuvinte: {e}\n")

def analizeaza_secvente_repetitive(fisier_rezultate: str, output_widget: tk.Text):
    try:
        with open(fisier_rezultate, 'r', encoding='utf-8') as f:
            continut = f.read()

        # Colectăm toate liniile „Text posibil: ...”
        secvente = re.findall(r'Text posibil: (.*)', continut)
        output_widget.insert(tk.END, "\n\n=== SECVENȚE REPETITIVE ===\n")
        if not secvente:
            output_widget.insert(tk.END, "Nu am găsit secvențe de text.\n")
            return

        repetitive = {}
        for secv in secvente:
            n = len(secv)
            for i in range(n):
                # subsecvențe cu lungime 3..min(10, n-i)
                max_len = min(10, n - i)
                for lungime in range(3, max_len + 1):
                    sub = secv[i:i+lungime]
                    repetitive[sub] = repetitive.get(sub, 0) + 1

        repetitive = {k: v for k, v in repetitive.items() if v > 1}

        output_widget.insert(tk.END, f"S-au găsit {len(repetitive)} subsecvențe repetitive (>1 apariții).\n")
        if repetitive:
            top = sorted(repetitive.items(), key=lambda x: x[1], reverse=True)[:10]
            for secv, cnt in top:
                output_widget.insert(tk.END, f"'{secv}': {cnt} apariții\n")
        else:
            output_widget.insert(tk.END, "Nu am găsit subsecvențe repetitive relevante.\n")
    except Exception as e:
        output_widget.insert(tk.END, f"Eroare analiză repetitivă: {e}\n")

def genereaza_grafic_distributie(fisier_rezultate: str, canvas: FigureCanvasTkAgg):
    try:
        with open(fisier_rezultate, 'r', encoding='utf-8') as f:
            continut = f.read()

        # extragem caracterele din tabel (coloana a doua)
        caractere = re.findall(r'\t(.)\n', continut)
        if not caractere:
            messagebox.showinfo("Grafic", "Nu s-au găsit caractere pentru grafic.")
            return

        frecventa = Counter(caractere)
        litere = {k: v for k, v in frecventa.items() if k.isalpha()}
        cifre = {k: v for k, v in frecventa.items() if k.isdigit()}
        punct = {k: v for k, v in frecventa.items() if not k.isalpha() and not k.isdigit()}

        fig: Figure = canvas.figure
        fig.clf()

        row = 0
        if litere:
            ax1 = fig.add_subplot(311)
            top_litere = sorted(litere.items(), key=lambda x: x[1], reverse=True)[:20]
            x, y = zip(*top_litere)
            ax1.bar(x, y)
            ax1.set_title('Top 20 Litere')
            ax1.set_ylabel('Frecvență')
            row = 1

        if cifre:
            # dacă nu avem litere, primul subplot devine 3x1,1 oricum; păstrăm layoutul 3x1
            ax2 = fig.add_subplot(312)
            x, y = zip(*sorted(cifre.items()))
            ax2.bar(x, y)
            ax2.set_title('Cifre')
            ax2.set_ylabel('Frecvență')
            row = 2

        if punct:
            ax3 = fig.add_subplot(313)
            top_punct = sorted(punct.items(), key=lambda x: x[1], reverse=True)
            x, y = zip(*top_punct)
            ax3.bar(x, y)
            ax3.set_title('Semne de punctuație')
            ax3.set_ylabel('Frecvență')

        fig.tight_layout()
        canvas.draw()
        messagebox.showinfo("Succes", "Graficul a fost generat!")
    except Exception as e:
        messagebox.showerror("Eroare", f"Eroare la generarea graficului: {e}")

# -------------------------------
# GUI cu Tkinter
# -------------------------------

class AnalizaBinaraGUI:
    def __init__(self, root: tk.Tk):
        self.root = root
        self.root.title("Analiză Avansată Date Binare")
        self.root.geometry("1000x700")
        self.root.configure(bg="#f0f0f0")

        self.fisier_binar = None
        self.fisier_rezultate = "rezultate_extrase.txt"

        self.creeaza_interfata()

    def creeeaza_titlu(self):
        title = tk.Label(self.root, text="Analiză Date Binare ASCII",
                         font=("Arial", 16, "bold"), bg="#f0f0f0")
        title.pack(pady=10)

    def creeaza_interfata(self):
        # Titlu
        self.creeeaza_titlu = self.creeeaza_titlu if hasattr(self, 'creeeaza_titlu') else None
        title = tk.Label(self.root, text="Analiză Date Binare ASCII",
                         font=("Arial", 16, "bold"), bg="#f0f0f0")
        title.pack(pady=10)

        # Buton încărcare fișier
        frame_load = tk.Frame(self.root, bg="#f0f0f0")
        frame_load.pack(pady=5)
        tk.Button(frame_load, text="Încarcă datebinare.txt",
                  command=self.incarca_fisier,
                  bg="#4CAF50", fg="white", font=("Arial", 10, "bold")).pack(side=tk.LEFT, padx=5)
        self.label_fisier = tk.Label(frame_load, text="Niciun fișier încărcat", bg="#f0f0f0", fg="gray")
        self.label_fisier.pack(side=tk.LEFT, padx=10)

        # Butoane acțiune
        frame_butoane = tk.Frame(self.root, bg="#f0f0f0")
        frame_butoane.pack(pady=10)
        tk.Button(frame_butoane, text="Rulează Extragere",
                  command=self.start_extragere, bg="#2196F3", fg="white", width=20).pack(side=tk.LEFT, padx=5)
        tk.Button(frame_butoane, text="Rulează Analiză Avansată",
                  command=self.start_analiza, bg="#FF9800", fg="white", width=20).pack(side=tk.LEFT, padx=5)
        tk.Button(frame_butoane, text="Generează Grafic",
                  command=self.start_grafic, bg="#9C27B0", fg="white", width=20).pack(side=tk.LEFT, padx=5)

        # Progress bar
        self.progress = ttk.Progressbar(self.root, mode='indeterminate')
        self.progress.pack(fill=tk.X, padx=20, pady=5)

        # Zona de rezultate
        frame_output = tk.LabelFrame(self.root, text=" Rezultate și Log ", font=("Arial", 10, "bold"))
        frame_output.pack(fill=tk.BOTH, expand=True, padx=20, pady=10)

        self.text_output = scrolledtext.ScrolledText(frame_output, height=12, font=("Courier", 10))
        self.text_output.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)

        # Zona grafic
        frame_grafic = tk.LabelFrame(self.root, text=" Grafic Distribuție Caractere ", font=("Arial", 10, "bold"))
        frame_grafic.pack(fill=tk.BOTH, expand=True, padx=20, pady=10)

        fig = Figure(figsize=(8, 6))
        self.canvas = FigureCanvasTkAgg(fig, master=frame_grafic)
        self.canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True)

    # ---------------------------
    # Acțiuni
    # ---------------------------

    def incarca_fisier(self):
        path = filedialog.askopenfilename(
            title="Selectează datebinare.txt",
            filetypes=[("Text files", "*.txt"), ("All files", "*.*")]
        )
        if path:
            self.fisier_binar = path
            self.label_fisier.config(text=os.path.basename(path), fg="green")
            self.text_output.insert(tk.END, f"Fișier încărcat: {path}\n")

    def start_extragere(self):
        if not self.fisier_binar:
            messagebox.showwarning("Lipsește fișierul", "Te rog încarcă datebinare.txt")
            return
        self.run_in_thread(self.run_extragere)

    def run_extragere(self):
        self.progress.start()
        self.text_output.insert(tk.END, "\n" + "="*50 + "\nÎncep extracția...\n")
        try:
            # Încercăm mai multe encodări uzuale
            encodari = ['utf-8', 'latin-1', 'ascii']
            continut = None
            for enc in encodari:
                try:
                    with open(self.fisier_binar, 'r', encoding=enc, errors='ignore') as f:
                        continut = f.read()
                    break
                except Exception:
                    continue
            if continut is None:
                raise ValueError("Nu am putut citi fișierul cu encodări uzuale.")

            mesaj = ruleaza_extragere(continut, self.fisier_rezultate)
            self.text_output.insert(tk.END, mesaj + "\n")
        except Exception as e:
            self.text_output.insert(tk.END, f"Eroare: {e}\n")
        finally:
            self.progress.stop()

    def start_analiza(self):
        if not os.path.exists(self.fisier_rezultate):
            messagebox.showwarning("Lipsește rezultatul", "Rulează mai întâi extracția!")
            return
        self.run_in_thread(self.run_analiza)

    def run_analiza(self):
        self.progress.start()
        self.text_output.insert(tk.END, "\n" + "="*50 + "\nÎncep analiza avansată...\n")
        try:
            cauta_cuvinte_cunoscute(self.fisier_rezultate, self.text_output)
            analizeaza_secvente_repetitive(self.fisier_rezultate, self.text_output)
            self.text_output.insert(tk.END, "\nAnaliza avansată finalizată.\n")
        except Exception as e:
            self.text_output.insert(tk.END, f"Eroare analiză: {e}\n")
        finally:
            self.progress.stop()

    def start_grafic(self):
        if not os.path.exists(self.fisier_rezultate):
            messagebox.showwarning("Lipsește rezultatul", "Rulează mai întâi extracția!")
            return
        self.run_in_thread(lambda: genereaza_grafic_distributie(self.fisier_rezultate, self.canvas))

    def run_in_thread(self, func):
        # Notă: actualizările Tk în thread secundar pot fi fragile pe unele sisteme.
        # Pentru proiecte mari, folosiți `queue` + `after()`. Aici păstrăm simplu.
        threading.Thread(target=func, daemon=True).start()

# -------------------------------
# Pornire aplicație
# -------------------------------

if __name__ == "__main__":
    root = tk.Tk()
    app = AnalizaBinaraGUI(root)
    root.mainloop()
