# BEGIN: user added these matplotlib lines to ensure any plots do not pop-up in their UI
import matplotlib
matplotlib.use('Agg')  # Set the backend to non-interactive
import matplotlib.pyplot as plt
plt.ioff()
import os
os.environ['TERM'] = 'dumb'
# END: user added these matplotlib lines to ensure any plots do not pop-up in their UI
# filename: extrage_biti_personalizat_final.py
# execution: true

import string

def extrage_biti_personalizat(fisier_binar, secventa_pozitii):
    """
    Extrage biti din fisierul binar conform secventei de pozitii specificate.
    Secventa reprezinta spatiul dintre bitii care trebuie selectati.
    Secventa se repeta automat pana la sfarsitul fisierului.
    Filtreaza si afiseaza doar caractere lizibile (litere, cifre, spatii, punctuatie).
    """
    try:
        # Citeste fisierul binar
        with open(fisier_binar, 'r', encoding='utf-8') as f:
            continut = f.read().strip()
        
        # Pastreaza doar caracterele 0 si 1
        secventa_binara = ''.join(bit for bit in continut if bit in ['0', '1'])
        
        if not secventa_binara:
            print("Fisierul nu contine date binare valide (0 si 1).")
            return
        
        print(f"Lungimea secventei binare: {len(secventa_binara)} biti")
        
        # Extrage bitii conform secventei de pozitii
        biti_extrasi = []
        pozitie_curenta = 0
        
        # IMPORTANT: Extrage PRIMUL bit (pozitia 0)
        if pozitie_curenta < len(secventa_binara):
            biti_extrasi.append(secventa_binara[pozitie_curenta])
            print(f"Pozitia {pozitie_curenta}: bit = {secventa_binara[pozitie_curenta]}")
        
        # Apoi continua cu secventa de spatii, repetand-o pana la sfarsit
        index_secventa = 0
        pozitii_afisate = 0
        while True:
            spatiu = secventa_pozitii[index_secventa]
            pozitie_curenta += spatiu
            
            if pozitie_curenta >= len(secventa_binara):
                print(f"Pozitia {pozitie_curenta} depaseste lungimea secventei binare ({len(secventa_binara)}). Stop.")
                break
            
            biti_extrasi.append(secventa_binara[pozitie_curenta])
            
            # Afiseaza doar primele 20 de pozitii pentru a nu umple ecranul
            if pozitii_afisate < 20:
                print(f"Pozitia {pozitie_curenta}: bit = {secventa_binara[pozitie_curenta]} (spatiu: {spatiu})")
                pozitii_afisate += 1
            elif pozitii_afisate == 20:
                print("... (continuare)")
                pozitii_afisate += 1
            
            # Trece la urmatorul element din secventa (cu wrap-around)
            index_secventa = (index_secventa + 1) % len(secventa_pozitii)
        
        # Converteste bitii extrasi in grupuri de 8 pentru caractere ASCII
        toate_caracterele = []
        for i in range(0, len(biti_extrasi), 8):
            if i + 8 <= len(biti_extrasi):
                grup_biti = ''.join(biti_extrasi[i:i+8])
                valoare_ascii = int(grup_biti, 2)
                caracter = chr(valoare_ascii)
                toate_caracterele.append((grup_biti, valoare_ascii, caracter))
        
        # Defineste caracterele valide (lizibile)
        # Litere mari, litere mici, cifre, spatiu, si punctuatie comuna
        caractere_valide = string.ascii_letters + string.digits + ' .,!?;:\'-"()\n'
        
        # Filtreaza doar caracterele lizibile
        caractere_filtrate = []
        caractere_ignorate = []
        
        for grup_biti, valoare_ascii, caracter in toate_caracterele:
            if caracter in caractere_valide:
                caractere_filtrate.append((grup_biti, valoare_ascii, caracter))
            else:
                caractere_ignorate.append((grup_biti, valoare_ascii, caracter))
        
        # Calculeaza cati biti au ramas (nu formeaza un grup complet de 8)
        biti_ramasi = len(biti_extrasi) % 8
        
        # Afiseaza rezultatele
        print("\n" + "="*60)
        print("REZULTATE:")
        print("="*60)
        print(f"\nBiti extrasi: {len(biti_extrasi)} biti total")
        print(f"Caractere totale: {len(toate_caracterele)} caractere")
        print(f"Caractere lizibile: {len(caractere_filtrate)} caractere")
        print(f"Caractere ignorate: {len(caractere_ignorate)} caractere")
        
        if biti_ramasi > 0:
            print(f"Ultimii {biti_ramasi} biti nu formeaza un grup complet de 8.")
        
        # Afiseaza DOAR caracterele lizibile
        if caractere_filtrate:
            print(f"\n{'='*60}")
            print(f"CARACTERE LIZIBILE ({len(caractere_filtrate)} caractere):")
            print(f"{'='*60}")
            
            for idx, (grup_biti, valoare_ascii, caracter) in enumerate(caractere_filtrate, 1):
                # Afiseaza caracter special pentru newline
                if caracter == '\n':
                    char_display = '\\n'
                else:
                    char_display = caracter
                print(f"{idx}. Biti: {grup_biti}, ASCII: {valoare_ascii:3d}, Char: '{char_display}'")
        
        # Afiseaza caracterele ignorate (optional, pentru debug)
        if caractere_ignorate:
            print(f"\n{'='*60}")
            print(f"CARACTERE IGNORATE ({len(caractere_ignorate)} caractere):")
            print(f"{'='*60}")
            for grup_biti, valoare_ascii, caracter in caractere_ignorate[:10]:  # Afiseaza doar primele 10
                print(f"Biti: {grup_biti}, ASCII: {valoare_ascii:3d} [ignorat]")
            if len(caractere_ignorate) > 10:
                print(f"... si inca {len(caractere_ignorate) - 10} caractere ignorate")
        
        # Construieste textul final doar din caractere lizibile
        if caractere_filtrate:
            text_rezultat = ''.join(caracter for _, _, caracter in caractere_filtrate)
            print(f"\n{'='*60}")
            print(f"TEXT REZULTAT ({len(text_rezultat)} caractere):")
            print(f"{'='*60}")
            print(text_rezultat)
            print(f"{'='*60}")
        else:
            print("\nNu s-au gasit caractere lizibile!")
        
        # Salveaza rezultatele intr-un fisier
        with open('rezultate_personalizate.txt', 'w', encoding='utf-8') as out_file:
            out_file.write(f"Secventa de pozitii: {secventa_pozitii}\n")
            out_file.write(f"Secventa repetata automat pana la sfarsitul fisierului\n\n")
            out_file.write(f"Statistici:\n")
            out_file.write(f"- Biti extrasi: {len(biti_extrasi)} biti\n")
            out_file.write(f"- Caractere totale: {len(toate_caracterele)} caractere\n")
            out_file.write(f"- Caractere lizibile: {len(caractere_filtrate)} caractere\n")
            out_file.write(f"- Caractere ignorate: {len(caractere_ignorate)} caractere\n")
            
            if biti_ramasi > 0:
                out_file.write(f"- Ultimii {biti_ramasi} biti nu formeaza un grup complet de 8.\n")
            
            out_file.write("\n" + "="*60 + "\n")
            out_file.write(f"CARACTERE LIZIBILE ({len(caractere_filtrate)} caractere):\n")
            out_file.write("="*60 + "\n")
            
            for idx, (grup_biti, valoare_ascii, caracter) in enumerate(caractere_filtrate, 1):
                if caracter == '\n':
                    char_display = '\\n'
                else:
                    char_display = caracter
                out_file.write(f"{idx}. Biti: {grup_biti}, ASCII: {valoare_ascii:3d}, Char: '{char_display}'\n")
            
            if caractere_filtrate:
                out_file.write(f"\n{'='*60}\n")
                out_file.write(f"TEXT REZULTAT ({len(text_rezultat)} caractere):\n")
                out_file.write(f"{'='*60}\n")
                out_file.write(f"{text_rezultat}\n")
            
            # Sectiune optionala cu caracterele ignorate
            if caractere_ignorate:
                out_file.write(f"\n{'='*60}\n")
                out_file.write(f"CARACTERE IGNORATE ({len(caractere_ignorate)} caractere):\n")
                out_file.write(f"{'='*60}\n")
                for grup_biti, valoare_ascii, caracter in caractere_ignorate:
                    out_file.write(f"Biti: {grup_biti}, ASCII: {valoare_ascii:3d} [ignorat]\n")
        
        print(f"\nRezultatele au fost salvate in fisierul 'rezultate_personalizate.txt'")
        
        return biti_extrasi, caractere_filtrate
        
    except FileNotFoundError:
        print(f"Fisierul '{fisier_binar}' nu a fost gasit.")
        print(f"Asigura-te ca fisierul exista in acelasi director cu scriptul!")
    except Exception as e:
        print(f"A aparut o eroare: {e}")
        import traceback
        traceback.print_exc()

# Modul interactiv - utilizatorul introduce secventa
print("="*60)
print("EXTRAGERE BITI - DOAR CARACTERE LIZIBILE")
print("="*60)

# Citeste numele fisierului
fisier = input("\nIntroduceti numele fisierului binar (implicit: datebinare.txt): ").strip()
if not fisier:
    fisier = "datebinare.txt"

# Citeste secventa de la utilizator
print("\nIntroduceti secventa de pozitii (spatii intre biti).")
print("\nFormate acceptate:")
print("  1. Numere separate prin virgula: 4,7,9,4")
print("  2. Numar intreg (cifrele vor fi separate automat): 4794 -> [4,7,9,4]")
print("  3. Un singur numar: 78")
print("\nNOTA: Primul bit (pozitia 0) va fi extras automat.")
print("      Secventa se va REPETA automat pana la sfarsitul fisierului.")
print("      Doar caracterele lizibile vor fi afisate (litere, cifre, spatii, punctuatie).")

secventa_input = input("\nSecventa: ").strip()

try:
    # Incearca sa parseaza input-ul
    if ',' in secventa_input:
        # Format cu virgule: 4,7,9,4
        secventa = [int(x.strip()) for x in secventa_input.split(',')]
    else:
        # Poate fi un singur numar sau un numar intreg cu cifre
        numar = secventa_input.strip()
        if len(numar) == 1:
            # Un singur numar: 7
            secventa = [int(numar)]
        else:
            # Numar cu mai multe cifre: 4794 -> [4,7,9,4]
            if int(numar) > 1000:
                # Intreaba utilizatorul
                print(f"\nAi introdus: {numar}")
                print("Cum vrei sa fie interpretat?")
                print(f"  1. Ca o secventa de cifre: {[int(c) for c in numar]}")
                print(f"  2. Ca un singur numar: [{numar}]")
                alegere = input("Alege (1 sau 2, implicit 1): ").strip()
                if alegere == "2":
                    secventa = [int(numar)]
                else:
                    secventa = [int(c) for c in numar]
            else:
                # Numar mai mic, interpreteaza ca cifre separate
                secventa = [int(c) for c in numar]
    
    print(f"\nSecventa interpretata: {secventa}")
    print("Secventa se va repeta automat pana la sfarsitul fisierului.")
    print("Doar caracterele lizibile vor fi extrase si afisate.")
    print("\nProcesare in curs...\n")
    
    # Apeleaza functia de extragere
    extrage_biti_personalizat(fisier, secventa)
    
except ValueError:
    print("Eroare: Secventa introdusa nu este valida.")
    print("Exemple valide: 4794 sau 4,7,9,4 sau 78")
except Exception as e:
    print(f"Eroare: {e}")
    import traceback
    traceback.print_exc()

print("\n" + "="*60)
print("Procesare finalizata!")
print("="*60)
