#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import os
import sys
import tkinter as tk
from tkinter import ttk, filedialog, messagebox
from collections import Counter, defaultdict
from itertools import groupby
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import csv
import re
from datetime import datetime
import numpy as np


class AdvancedBinaryAnalyzer:
    def __init__(self, root):
        self.root = root
        self.root.title("Analizor Avansat Date Binare")
        self.root.geometry("1000x800")
        
        # Variabile
        self.input_file = tk.StringVar(value="datebinare.txt")
        self.binary_data = ""
        self.analysis_results = {}
        self.output_dir = "rezultate"
        
        self.setup_ui()
        
    def setup_ui(self):
        # Notebook pentru tab-uri
        notebook = ttk.Notebook(self.root)
        notebook.pack(fill='both', expand=True, padx=10, pady=10)
        
        # Tab principal
        main_tab = ttk.Frame(notebook)
        notebook.add(main_tab, text="Analiză Principală")
        
        # Tab pentru pattern-uri avansate
        pattern_tab = ttk.Frame(notebook)
        notebook.add(pattern_tab, text="Pattern-uri Avansate")
        
        # Tab pentru statistici
        stats_tab = ttk.Frame(notebook)
        notebook.add(stats_tab, text="Statistici Detaliate")
        
        self.setup_main_tab(main_tab)
        self.setup_pattern_tab(pattern_tab)
        self.setup_stats_tab(stats_tab)
        
        # Bară de stare
        self.status_bar = ttk.Label(self.root, text="Gata", relief=tk.SUNKEN)
        self.status_bar.pack(side=tk.BOTTOM, fill=tk.X, padx=10, pady=5)
        
    def setup_main_tab(self, tab):
        # Frame pentru fișier
        file_frame = ttk.LabelFrame(tab, text="Fișier de Intrare", padding="10")
        file_frame.pack(fill='x', padx=10, pady=5)
        
        ttk.Label(file_frame, text="Fișier:").grid(row=0, column=0, sticky=tk.W)
        ttk.Entry(file_frame, textvariable=self.input_file, width=60).grid(row=0, column=1, padx=5)
        ttk.Button(file_frame, text="Browse", command=self.browse_file).grid(row=0, column=2, padx=5)
        ttk.Button(file_frame, text="Încarcă Date", command=self.load_data).grid(row=1, column=0, columnspan=3, pady=10)
        
        # Frame pentru analiză rapidă
        quick_frame = ttk.LabelFrame(tab, text="Analiză Rapidă", padding="10")
        quick_frame.pack(fill='x', padx=10, pady=5)
        
        button_frame = ttk.Frame(quick_frame)
        button_frame.pack(pady=10)
        
        ttk.Button(button_frame, text="Analiză Standard", command=self.run_standard_analysis).grid(row=0, column=0, padx=5)
        ttk.Button(button_frame, text="Analiză Completă", command=self.run_complete_analysis).grid(row=0, column=1, padx=5)
        ttk.Button(button_frame, text="Generează Toate Graficele", command=self.generate_all_plots).grid(row=0, column=2, padx=5)
        
        # Zona de rezultate
        results_frame = ttk.LabelFrame(tab, text="Rezultate", padding="10")
        results_frame.pack(fill='both', expand=True, padx=10, pady=5)
        
        scrollbar = ttk.Scrollbar(results_frame)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        
        self.results_text = tk.Text(results_frame, height=20, width=80, yscrollcommand=scrollbar.set)
        self.results_text.pack(side=tk.LEFT, fill='both', expand=True)
        scrollbar.config(command=self.results_text.yview)
        
    def setup_pattern_tab(self, tab):
        # Frame pentru pattern-uri simple
        simple_frame = ttk.LabelFrame(tab, text="Pattern-uri Simple", padding="10")
        simple_frame.pack(fill='x', padx=10, pady=5)
        
        ttk.Label(simple_frame, text="Pattern-uri (separate prin spațiu):").grid(row=0, column=0, sticky=tk.W)
        self.simple_patterns = tk.Text(simple_frame, height=2, width=50)
        self.simple_patterns.grid(row=0, column=1, padx=5)
        self.simple_patterns.insert(tk.END, "001 11110 010 3")
        
        ttk.Button(simple_frame, text="Analizează Pattern-uri Simple", command=self.analyze_simple_patterns).grid(row=1, column=0, columnspan=2, pady=10)
        
        # Frame pentru pattern-uri complexe
        complex_frame = ttk.LabelFrame(tab, text="Pattern-uri Complexe", padding="10")
        complex_frame.pack(fill='x', padx=10, pady=5)
        
        ttk.Label(complex_frame, text="Pattern regex:").grid(row=0, column=0, sticky=tk.W)
        self.regex_pattern = tk.Entry(complex_frame, width=50)
        self.regex_pattern.grid(row=0, column=1, padx=5)
        self.regex_pattern.insert(0, "(01)+(10)+")
        
        ttk.Button(complex_frame, text="Analizează Pattern Complex", command=self.analyze_regex_pattern).grid(row=1, column=0, columnspan=2, pady=10)
        
        # Frame pentru alternanțe
        alternance_frame = ttk.LabelFrame(tab, text="Analiză Alternanțe", padding="10")
        alternance_frame.pack(fill='x', padx=10, pady=5)
        
        ttk.Label(alternance_frame, text="Minim alternanțe:").grid(row=0, column=0, sticky=tk.W)
        self.min_alternances = tk.Spinbox(alternance_frame, from_=1, to=10, width=10)
        self.min_alternances.grid(row=0, column=1, padx=5)
        self.min_alternances.delete(0, tk.END)
        self.min_alternances.insert(0, "2")
        
        ttk.Button(alternance_frame, text="Analizează Alternanțe", command=self.analyze_alternances).grid(row=1, column=0, columnspan=2, pady=10)
        
    def setup_stats_tab(self, tab):
        # Frame pentru statistici detaliate
        stats_frame = ttk.LabelFrame(tab, text="Statistici Detaliate", padding="10")
        stats_frame.pack(fill='both', expand=True, padx=10, pady=5)
        
        scrollbar = ttk.Scrollbar(stats_frame)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        
        self.stats_text = tk.Text(stats_frame, height=25, width=80, yscrollcommand=scrollbar.set)
        self.stats_text.pack(side=tk.LEFT, fill='both', expand=True)
        scrollbar.config(command=self.stats_text.yview)
        
        button_frame = ttk.Frame(tab)
        button_frame.pack(fill='x', padx=10, pady=5)
        
        ttk.Button(button_frame, text="Calculează Statistici", command=self.calculate_detailed_stats).pack(side=tk.LEFT, padx=5)
        ttk.Button(button_frame, text="Salvează Raport Complet", command=self.save_complete_report).pack(side=tk.LEFT, padx=5)
        
    def update_status(self, message):
        self.status_bar.config(text=message)
        self.root.update()
        
    def log_result(self, message, text_widget=None):
        if text_widget is None:
            text_widget = self.results_text
        text_widget.insert(tk.END, message + "\n")
        text_widget.see(tk.END)
        self.root.update()
        
    def browse_file(self):
        filename = filedialog.askopenfilename(
            title="Selectează fișierul cu date binare",
            filetypes=[("Text files", "*.txt"), ("All files", "*.*")]
        )
        if filename:
            self.input_file.set(filename)
            
    def load_data(self):
        self.update_status("Se încarcă datele...")
        file_path = self.input_file.get()
        if not os.path.exists(file_path):
            messagebox.showerror("Eroare", f"Fișierul {file_path} nu există.")
            return
            
        self.binary_data = self.read_bits(file_path)
        if self.binary_data:
            self.log_result(f"✓ Date încărcate cu succes: {len(self.binary_data):,} biți")
            self.log_result(f"✓ Distribuție: {self.binary_data.count('0')} zerouri, {self.binary_data.count('1')} uniți")
            self.log_result(f"✓ Proporție: {(self.binary_data.count('0')/len(self.binary_data)*100):.3f}% zerouri")
            self.update_status("Date încărcate")
        else:
            self.update_status("Eroare la încărcare")
            
    def read_bits(self, path):
        try:
            with open(path, "r", encoding="utf-8") as f:
                data = f.read().strip()
            for ch in set(data):
                if ch not in {"0", "1"}:
                    raise ValueError(f"Fișierul conține caracter invalid: {repr(ch)}")
            if not data:
                raise ValueError("Fișierul este gol.")
            return data
        except Exception as e:
            messagebox.showerror("Eroare", f"Eroare la citirea fișierului: {str(e)}")
            return None
            
    def run_length_encode(self, bits):
        return [(bit, sum(1 for _ in grp)) for bit, grp in groupby(bits)]
        
    def count_pattern_occurrences(self, data, patterns):
        """Contorizează aparițiile pattern-urilor specificate"""
        results = {}
        
        for pattern in patterns:
            pattern = pattern.strip()
            if not pattern:
                continue
                
            # Caută toate aparițiile pattern-ului
            count = len(re.findall(f'(?={pattern})', data))
            percentage = (count / len(data)) * 100 if data else 0
            
            results[pattern] = {
                'count': count,
                'percentage': percentage,
                'pattern_length': len(pattern)
            }
            
        return results
        
    def analyze_simple_patterns(self):
        if not self.binary_data:
            messagebox.showwarning("Atenție", "Nu există date încărcate.")
            return
            
        self.update_status("Se analizează pattern-urile simple...")
        
        patterns_text = self.simple_patterns.get("1.0", tk.END).strip()
        patterns = patterns_text.split()
        
        if not patterns:
            messagebox.showwarning("Atenție", "Nu ați specificat niciun pattern.")
            return
            
        self.log_result("\n" + "="*60)
        self.log_result("ANALIZĂ PATTERN-URI SIMPLE")
        self.log_result("="*60)
        
        results = self.count_pattern_occurrences(self.binary_data, patterns)
        
        for pattern, data in results.items():
            self.log_result(f"Pattern '{pattern}' (lungime {data['pattern_length']}):")
            self.log_result(f"  • Apariții: {data['count']:,}")
            self.log_result(f"  • Procentaj: {data['percentage']:.3f}%")
            self.log_result(f"  • Densitate: {data['count']/len(self.binary_data)*100:.6f}% din total biți")
            self.log_result("")
            
        self.analysis_results['simple_patterns'] = results
        self.update_status("Analiză pattern-uri simple completă")
        
    def analyze_regex_pattern(self):
        if not self.binary_data:
            messagebox.showwarning("Atenție", "Nu există date încărcate.")
            return
            
        self.update_status("Se analizează pattern-ul complex...")
        
        pattern = self.regex_pattern.get().strip()
        if not pattern:
            messagebox.showwarning("Atenție", "Nu ați specificat niciun pattern regex.")
            return
            
        try:
            matches = list(re.finditer(pattern, self.binary_data))
            count = len(matches)
            percentage = (count / len(self.binary_data)) * 100 if self.binary_data else 0
            
            self.log_result("\n" + "="*60)
            self.log_result("ANALIZĂ PATTERN COMPLEX (REGEX)")
            self.log_result("="*60)
            self.log_result(f"Pattern regex: {pattern}")
            self.log_result(f"Apariții: {count:,}")
            self.log_result(f"Procentaj: {percentage:.3f}%")
            
            if matches:
                self.log_result("Primele 10 poziții găsite:")
                for i, match in enumerate(matches[:10]):
                    start, end = match.span()
                    self.log_result(f"  Poziția {start}-{end}: '{match.group()}'")
                    
                if len(matches) > 10:
                    self.log_result(f"... și încă {len(matches) - 10} apariții")
                    
            self.analysis_results['regex_pattern'] = {
                'pattern': pattern,
                'count': count,
                'percentage': percentage,
                'matches': matches
            }
            
        except re.error as e:
            messagebox.showerror("Eroare Regex", f"Eroare în pattern-ul regex: {str(e)}")
            
        self.update_status("Analiză pattern complex completă")
        
    def analyze_alternances(self):
        if not self.binary_data:
            messagebox.showwarning("Atenție", "Nu există date încărcate.")
            return
            
        self.update_status("Se analizează alternanțele...")
        
        min_alternances = int(self.min_alternances.get())
        
        # Găsește toate secvențele cu alternanțe
        alternance_pattern = r'(01)+(10)+|01+|10+'
        matches = list(re.finditer(alternance_pattern, self.binary_data))
        
        # Filtrează după numărul minim de alternanțe
        valid_alternances = []
        for match in matches:
            sequence = match.group()
            # Numără câte ori se schimbă bitul
            changes = sum(1 for i in range(1, len(sequence)) if sequence[i] != sequence[i-1])
            if changes >= min_alternances - 1:  # -1 pentru că schimbările sunt cu 1 mai puține decât alternanțele
                valid_alternances.append({
                    'sequence': sequence,
                    'start': match.start(),
                    'end': match.end(),
                    'alternances': changes + 1,
                    'length': len(sequence)
                })
                
        self.log_result("\n" + "="*60)
        self.log_result("ANALIZĂ ALTERNANȚE")
        self.log_result("="*60)
        self.log_result(f"Alternanțe minime cerute: {min_alternances}")
        self.log_result(f"Secvențe valide găsite: {len(valid_alternances):,}")
        
        if valid_alternances:
            # Grupează după numărul de alternanțe
            by_alternances = defaultdict(list)
            for alt in valid_alternances:
                by_alternances[alt['alternances']].append(alt)
                
            self.log_result("\nDistribuția alternanțelor:")
            total_sequences = len(valid_alternances)
            
            for num_alt in sorted(by_alternances.keys()):
                count = len(by_alternances[num_alt])
                percentage = (count / total_sequences) * 100
                self.log_result(f"  {num_alt} alternanțe: {count:,} secvențe ({percentage:.3f}%)")
                
            # Afișează primele 5 cele mai lungi secvențe
            longest = sorted(valid_alternances, key=lambda x: x['length'], reverse=True)[:5]
            self.log_result("\nCele mai lungi 5 secvențe cu alternanțe:")
            for i, alt in enumerate(longest, 1):
                self.log_result(f"  {i}. '{alt['sequence']}' - {alt['length']} biți, {alt['alternances']} alternanțe")
                
        self.analysis_results['alternances'] = {
            'min_alternances': min_alternances,
            'total_sequences': len(valid_alternances),
            'sequences': valid_alternances
        }
        
        self.update_status("Analiză alternanțe completă")
        
    def run_standard_analysis(self):
        if not self.binary_data:
            messagebox.showwarning("Atenție", "Nu există date încărcate.")
            return
            
        self.update_status("Se rulează analiza standard...")
        
        runs = self.run_length_encode(self.binary_data)
        
        # Contorizare pe lungimi
        c0, c1 = Counter(), Counter()
        for bit, length in runs:
            if bit == "0":
                c0[length] += 1
            else:
                c1[length] += 1
                
        max0 = max((L for b, L in runs if b == "0"), default=0)
        max1 = max((L for b, L in runs if b == "1"), default=0)
        
        self.log_result("\n" + "="*60)
        self.log_result("ANALIZĂ STANDARD")
        self.log_result("="*60)
        self.log_result(f"Total biți: {len(self.binary_data):,}")
        self.log_result(f"Număr total run-uri: {len(runs):,}")
        self.log_result(f"Cea mai lungă serie de 0: {max0}")
        self.log_result(f"Cea mai lungă serie de 1: {max1}")
        self.log_result("")
        
        # Distribuție procentuală
        self.log_result("Distribuția lungimilor pentru 0:")
        total_0 = sum(c0.values())
        for length in sorted(c0.keys()):
            pct = (c0[length] / total_0 * 100) if total_0 > 0 else 0
            self.log_result(f"  Lungime {length}: {c0[length]:,} apariții ({pct:.3f}%)")
            
        self.log_result("")
        self.log_result("Distribuția lungimilor pentru 1:")
        total_1 = sum(c1.values())
        for length in sorted(c1.keys()):
            pct = (c1[length] / total_1 * 100) if total_1 > 0 else 0
            self.log_result(f"  Lungime {length}: {c1[length]:,} apariții ({pct:.3f}%)")
            
        self.analysis_results['standard'] = {
            'total_bits': len(self.binary_data),
            'total_runs': len(runs),
            'max_0_run': max0,
            'max_1_run': max1,
            'distribution_0': c0,
            'distribution_1': c1
        }
        
        self.update_status("Analiză standard completă")
        
    def calculate_detailed_stats(self):
        if not self.binary_data:
            messagebox.showwarning("Atenție", "Nu există date încărcate.")
            return
            
        self.update_status("Se calculează statisticile detaliate...")
        
        data = self.binary_data
        n = len(data)
        
        # Statistici de bază
        count_0 = data.count('0')
        count_1 = data.count('1')
        
        # Entropie Shannon
        p0 = count_0 / n
        p1 = count_1 / n
        entropy = - (p0 * np.log2(p0) + p1 * np.log2(p1)) if p0 > 0 and p1 > 0 else 0
        
        # Autocorelație
        def autocorrelation(data, lag):
            if lag >= len(data):
                return 0
            matches = sum(1 for i in range(len(data) - lag) if data[i] == data[i + lag])
            return matches / (len(data) - lag)
            
        # Calculează autocorelația pentru lag 1-10
        autocorr_values = [autocorrelation(data, lag) for lag in range(1, 11)]
        
        # Găsește cele mai frecvente bi-grame și tri-grame
        bigrams = [data[i:i+2] for i in range(len(data)-1)]
        trigrams = [data[i:i+3] for i in range(len(data)-2)]
        
        bigram_counts = Counter(bigrams)
        trigram_counts = Counter(trigrams)
        
        # Afișează rezultatele
        self.stats_text.delete(1.0, tk.END)
        self.stats_text.insert(tk.END, "STATISTICI DETALIATE\n")
        self.stats_text.insert(tk.END, "="*50 + "\n\n")
        
        self.stats_text.insert(tk.END, f"Dimensiune date: {n:,} biți\n")
        self.stats_text.insert(tk.END, f"Distribuție: {count_0:,} zerouri ({p0*100:.3f}%), {count_1:,} uniți ({p1*100:.3f}%)\n")
        self.stats_text.insert(tk.END, f"Entropie Shannon: {entropy:.6f} biți\n")
        self.stats_text.insert(tk.END, f"Entropie maximă: 1.000000 biți\n")
        self.stats_text.insert(tk.END, f"Randomizare: {entropy/1.0*100:.3f}%\n\n")
        
        self.stats_text.insert(tk.END, "Autocorelație (lag 1-10):\n")
        for i, ac in enumerate(autocorr_values, 1):
            self.stats_text.insert(tk.END, f"  Lag {i}: {ac:.6f}\n")
            
        self.stats_text.insert(tk.END, "\nTop 10 bi-grame:\n")
        for bigram, count in bigram_counts.most_common(10):
            percentage = count / len(bigrams) * 100
            self.stats_text.insert(tk.END, f"  '{bigram}': {count:,} ({percentage:.3f}%)\n")
            
        self.stats_text.insert(tk.END, "\nTop 10 tri-grame:\n")
        for trigram, count in trigram_counts.most_common(10):
            percentage = count / len(trigrams) * 100
            self.stats_text.insert(tk.END, f"  '{trigram}': {count:,} ({percentage:.3f}%)\n")
            
        # Calculează și alte statistici
        runs = self.run_length_encode(data)
        run_lengths = [length for _, length in runs]
        
        if run_lengths:
            avg_run = np.mean(run_lengths)
            std_run = np.std(run_lengths)
            median_run = np.median(run_lengths)
            
            self.stats_text.insert(tk.END, f"\nStatistici run-length:\n")
            self.stats_text.insert(tk.END, f"  Lungime medie: {avg_run:.3f}\n")
            self.stats_text.insert(tk.END, f"  Mediană: {median_run:.1f}\n")
            self.stats_text.insert(tk.END, f"  Deviație standard: {std_run:.3f}\n")
            
        self.analysis_results['detailed_stats'] = {
            'entropy': entropy,
            'autocorrelation': autocorr_values,
            'bigram_counts': bigram_counts,
            'trigram_counts': trigram_counts
        }
        
        self.update_status("Statistici detaliate calculate")
        
    def generate_all_plots(self):
        if not self.binary_data:
            messagebox.showwarning("Atenție", "Nu există date încărcate.")
            return
            
        self.update_status("Se generează toate graficele...")
        
        os.makedirs(self.output_dir, exist_ok=True)
        
        # Generează distribuția run-length
        self.generate_run_length_plots()
        
        # Generează graficul entropiei
        self.generate_entropy_plot()
        
        # Generează graficul autocorelației
        self.generate_autocorrelation_plot()
        
        # Generează graficul distribuției pattern-urilor
        self.generate_pattern_distribution_plot()
        
        self.log_result(f"\n✓ Toate graficele au fost salvate în directorul '{self.output_dir}'")
        self.update_status("Grafice generate")
        
    def generate_run_length_plots(self):
        if 'standard' not in self.analysis_results:
            self.run_standard_analysis()
            
        c0 = self.analysis_results['standard']['distribution_0']
        c1 = self.analysis_results['standard']['distribution_1']
        
        # Plot pentru 0
        plt.figure(figsize=(10, 6))
        if c0:
            lengths = sorted(c0.keys())
            counts = [c0[l] for l in lengths]
            total = sum(counts)
            percentages = [c/total*100 for c in counts]
            
            plt.bar([str(l) for l in lengths], percentages, color='blue', alpha=0.7)
            plt.title('Distribuția lungimilor pentru biții 0')
            plt.xlabel('Lungime run')
            plt.ylabel('Procentaj (%)')
            plt.xticks(rotation=45)
            
        plt.tight_layout()
        plt.savefig(os.path.join(self.output_dir, 'run_distribution_0.png'), dpi=150, bbox_inches='tight')
        plt.close()
        
        # Plot pentru 1
        plt.figure(figsize=(10, 6))
        if c1:
            lengths = sorted(c1.keys())
            counts = [c1[l] for l in lengths]
            total = sum(counts)
            percentages = [c/total*100 for c in counts]
            
            plt.bar([str(l) for l in lengths], percentages, color='red', alpha=0.7)
            plt.title('Distribuția lungimilor pentru biții 1')
            plt.xlabel('Lungime run')
            plt.ylabel('Procentaj (%)')
            plt.xticks(rotation=45)
            
        plt.tight_layout()
        plt.savefig(os.path.join(self.output_dir, 'run_distribution_1.png'), dpi=150, bbox_inches='tight')
        plt.close()
        
    def generate_entropy_plot(self):
        if 'detailed_stats' not in self.analysis_results:
            self.calculate_detailed_stats()
            
        entropy = self.analysis_results['detailed_stats']['entropy']
        
        plt.figure(figsize=(8, 6))
        categories = ['Entropie\ncurentă', 'Entropie\nmaximă']
        values = [entropy, 1.0]
        colors = ['skyblue', 'lightcoral']
        
        bars = plt.bar(categories, values, color=colors, alpha=0.7)
        plt.title('Analiza Entropiei')
        plt.ylabel('Entropie (biți)')
        
        # Adaugă valori pe bare
        for bar, value in zip(bars, values):
            plt.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 0.01, 
                    f'{value:.4f}', ha='center', va='bottom')
                    
        plt.ylim(0, 1.2)
        plt.tight_layout()
        plt.savefig(os.path.join(self.output_dir, 'entropy_analysis.png'), dpi=150, bbox_inches='tight')
        plt.close()
        
    def generate_autocorrelation_plot(self):
        if 'detailed_stats' not in self.analysis_results:
            self.calculate_detailed_stats()
            
        autocorr = self.analysis_results['detailed_stats']['autocorrelation']
        
        plt.figure(figsize=(10, 6))
        lags = range(1, len(autocorr) + 1)
        
        plt.plot(lags, autocorr, marker='o', linewidth=2, markersize=6)
        plt.title('Autocorelația secvenței binare')
        plt.xlabel('Lag')
        plt.ylabel('Autocorelație')
        plt.grid(True, alpha=0.3)
        plt.axhline(y=0.5, color='red', linestyle='--', alpha=0.7, label='Nivel 0.5')
        plt.legend()
        
        plt.tight_layout()
        plt.savefig(os.path.join(self.output_dir, 'autocorrelation_plot.png'), dpi=150, bbox_inches='tight')
        plt.close()
        
    def generate_pattern_distribution_plot(self):
        if 'simple_patterns' not in self.analysis_results:
            self.analyze_simple_patterns()
            
        patterns_data = self.analysis_results['simple_patterns']
        
        if not patterns_data:
            return
            
        plt.figure(figsize=(12, 6))
        
        patterns = list(patterns_data.keys())
        percentages = [patterns_data[p]['percentage'] for p in patterns]
        
        bars = plt.bar(patterns, percentages, color='green', alpha=0.7)
        plt.title('Distribuția pattern-urilor simple')
        plt.xlabel('Pattern')
        plt.ylabel('Procentaj (%)')
        plt.xticks(rotation=45)
        
        # Adaugă valori pe bare
        for bar, percentage in zip(bars, percentages):
            plt.text(bar.get_x() + bar.get_width()/2, bar.get_height() + max(percentages)*0.01, 
                    f'{percentage:.3f}%', ha='center', va='bottom')
                    
        plt.tight_layout()
        plt.savefig(os.path.join(self.output_dir, 'pattern_distribution.png'), dpi=150, bbox_inches='tight')
        plt.close()
        
    def save_complete_report(self):
        if not self.analysis_results:
            messagebox.showwarning("Atenție", "Nu există rezultate de analiză.")
            return
            
        self.update_status("Se salvează raportul complet...")
        
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        report_file = os.path.join(self.output_dir, f"raport_complet_{timestamp}.txt")
        
        with open(report_file, 'w', encoding='utf-8') as f:
            f.write("RAPORT COMPLET ANALIZĂ DATE BINARE\n")
            f.write("="*60 + "\n")
            f.write(f"Data și ora: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
            f.write(f"Fișier de intrare: {self.input_file.get()}\n")
            f.write(f"Dimensiune date: {len(self.binary_data):,} biți\n\n")
            
            # Statistici detaliate
            if 'detailed_stats' in self.analysis_results:
                stats = self.analysis_results['detailed_stats']
                f.write("STATISTICI DETALIATE\n")
                f.write("-"*40 + "\n")
                f.write(f"Entropie Shannon: {stats['entropy']:.6f} biți\n")
                f.write(f"Randomizare: {stats['entropy']/1.0*100:.3f}%\n\n")
                
                f.write("Autocorelație (lag 1-10):\n")
                for i, ac in enumerate(stats['autocorrelation'], 1):
                    f.write(f"  Lag {i}: {ac:.6f}\n")
                    
            # Analiză standard
            if 'standard' in self.analysis_results:
                std = self.analysis_results['standard']
                f.write("\nANALIZĂ STANDARD\n")
                f.write("-"*30 + "\n")
                f.write(f"Total biți: {std['total_bits']:,}\n")
                f.write(f"Număr total run-uri: {std['total_runs']:,}\n")
                f.write(f"Cea mai lungă serie de 0: {std['max_0_run']}\n")
                f.write(f"Cea mai lungă serie de 1: {std['max_1_run']}\n\n")
                
            # Pattern-uri simple
            if 'simple_patterns' in self.analysis_results:
                f.write("ANALIZĂ PATTERN-URI SIMPLE\n")
                f.write("-"*40 + "\n")
                for pattern, data in self.analysis_results['simple_patterns'].items():
                    f.write(f"Pattern '{pattern}' (lungime {data['pattern_length']}):\n")
                    f.write(f"  Apariții: {data['count']:,}\n")
                    f.write(f"  Procentaj: {data['percentage']:.3f}%\n\n")
                    
            # Alternanțe
            if 'alternances' in self.analysis_results:
                alt = self.analysis_results['alternances']
                f.write("ANALIZĂ ALTERNANȚE\n")
                f.write("-"*25 + "\n")
                f.write(f"Alternanțe minime: {alt['min_alternances']}\n")
                f.write(f"Total secvențe: {alt['total_sequences']:,}\n\n")
                
        self.log_result(f"\n✓ Raport complet salvat: {report_file}")
        self.update_status("Raport complet salvat")
        
        # Deschide fișierul
        try:
            os.startfile(report_file)
        except:
            pass
            
    def run_complete_analysis(self):
        self.run_standard_analysis()
        self.calculate_detailed_stats()
        self.analyze_simple_patterns()
        self.analyze_alternances()


def main():
    root = tk.Tk()
    app = AdvancedBinaryAnalyzer(root)
    root.mainloop()


if __name__ == "__main__":
    main()