brlapi 0.4.1

Safe Rust bindings for the BrlAPI library
// SPDX-License-Identifier: LGPL-2.1

//! Braille Contraction Demonstration
//!
//! This example demonstrates the difference between uncontracted (computer braille)
//! and contracted (literary braille) text output using liblouis integration.
//!
//! It shows how to use the client-side contraction capabilities that BrlAPI
//! requires, implementing the same approach used by all major screen readers.

use brlapi::{Connection, TtyMode, text::CursorPosition};
use std::thread::sleep;
use std::time::Duration;

fn main() -> Result<(), brlapi::BrlApiError> {
    println!("Braille Contraction Demonstration");
    println!("=================================");
    println!();

    // Open connection to BRLTTY
    let connection = Connection::open()?;
    println!("Connected to BRLTTY daemon");

    // Get display information
    let display = brlapi::Display::from_connection(&connection)?;
    println!("Display: {}", display);

    // Show comprehensive debug information about BRLTTY preferences
    println!();
    connection.debug_preferences();
    println!();

    // Enter TTY mode for text operations
    let (tty_mode, tty_number) = TtyMode::enter_auto(&connection, None)?;
    println!("Using TTY: {}", tty_number);

    let writer = tty_mode.writer();

    // Test phrases that demonstrate contractions well
    let test_phrases = [
        "Hello world and welcome to the program",
        "The quick brown fox jumps over the lazy dog",
        "I can see you and the other people",
        "This is a test of the emergency broadcast system",
        "For more information please visit our website",
    ];

    println!("Demonstrating contracted vs uncontracted braille:");
    println!("Press Ctrl+C to exit at any time");
    println!();

    for (i, phrase) in test_phrases.iter().enumerate() {
        println!("Demo {}: {}", i + 1, phrase);

        // Show uncontracted (computer braille) first
        println!("  Writing uncontracted...");
        writer.write_computer_braille(phrase, CursorPosition::Off)?;
        sleep(Duration::from_secs(3));

        // Show contracted (literary braille) using user's preferred table
        println!("  Writing contracted (user's preferred table)...");
        match writer.write_contracted_user_preference(phrase, CursorPosition::Off) {
            Ok(()) => {
                println!("  [SUCCESS] Contraction successful using user preference");
            }
            Err(e) => {
                println!(
                    "  [FALLBACK] Contraction failed ({}), showing uncontracted",
                    e
                );
                writer.write_computer_braille(phrase, CursorPosition::Off)?;
            }
        }
        sleep(Duration::from_secs(3));

        // Show side-by-side comparison in terminal using user's preferred table
        let user_table = connection
            .user_contraction_table()
            .unwrap_or_else(|_| "en-us-g2.ctb".to_string());
        match liblouis::util::translate_text(&user_table, phrase) {
            Ok(contracted) => {
                println!("  Original:   '{}'", phrase);
                println!("  Contracted: '{}'", contracted);
                println!(
                    "  Saved {} characters ({:.1}% compression)",
                    phrase.len() - contracted.len(),
                    100.0 * (1.0 - (contracted.len() as f32 / phrase.len() as f32))
                );
            }
            Err(e) => {
                println!("  Translation comparison failed: {}", e);
            }
        }

        println!();
        sleep(Duration::from_secs(1));
    }

    // Demonstrate liblouis table discovery functionality
    println!("Demonstrating liblouis table discovery:");
    use liblouis::TableResolver;

    // Initialize table discovery
    match TableResolver::index_tables() {
        Ok(()) => {
            println!("  [SUCCESS] Table discovery initialized");

            // Try various query patterns
            let queries = [
                ("English Grade 2", "language:en grade:2"),
                ("Any Grade 2", "grade:2"),
                ("English Language", "language:en"),
                ("US English Grade 2", "language:en region:US grade:2"),
            ];

            for (description, query) in &queries {
                match TableResolver::find_table(query) {
                    Ok(Some(table)) => println!("  {} query '{}' found: {}", description, query, table),
                    Ok(None) => println!("  {} query '{}' found no matches", description, query),
                    Err(e) => println!("  {} query '{}' failed: {}", description, query, e),
                }
            }

            // Test locale resolution
            println!("  Testing locale resolution:");
            let locales = ["en_US", "en_GB", "fr_FR", "de_DE", "unknown"];
            for locale in &locales {
                match TableResolver::resolve_locale(locale) {
                    Ok(table) => println!("    {} -> {}", locale, table),
                    Err(e) => println!("    {} failed: {}", locale, e),
                }
            }
        }
        Err(e) => {
            println!("  [WARNING] Table discovery failed: {}", e);
            println!("  Continuing with hardcoded table names...");
        }
    }
    println!();

    // Demonstrate different contraction tables
    println!("Demonstrating different contraction tables:");
    let demo_text = "Hello and welcome to our program";

    let tables = [
        ("US Grade 2", "en-us-g2.ctb"),
        ("UK Grade 2", "en-gb-g2.ctb"),
        ("US Grade 1", "en-us-g1.ctb"),
    ];

    for (name, table) in &tables {
        println!("  Testing {}: {}", name, table);
        match writer.write_contracted(demo_text, table, CursorPosition::Off) {
            Ok(()) => {
                println!("    [SUCCESS] {} table working", name);
                if let Ok(contracted) = liblouis::util::translate_text(table, demo_text) {
                    println!("    Result: '{}'", contracted);
                }
            }
            Err(e) => {
                println!("    [ERROR] {} table failed: {}", name, e);
            }
        }
        sleep(Duration::from_secs(2));
    }

    // Demonstrate cursor tracking with contractions
    println!();
    println!("Demonstrating cursor position tracking with contractions:");
    let cursor_demo_text = "The quick brown fox";
    let original_cursor_pos = 10; // Position of 'b' in "brown"

    let cursor_table = connection
        .user_contraction_table()
        .unwrap_or_else(|_| "en-us-g2.ctb".to_string());
    match writer.write_contracted_with_cursor_tracking(
        cursor_demo_text,
        &cursor_table,
        original_cursor_pos,
    ) {
        Ok(new_cursor_pos) => {
            println!("  Original text: '{}'", cursor_demo_text);
            println!(
                "  Original cursor at position {} (character '{}')",
                original_cursor_pos,
                cursor_demo_text
                    .chars()
                    .nth(original_cursor_pos)
                    .unwrap_or('?')
            );
            println!("  New cursor position: {}", new_cursor_pos);

            if let Ok(contracted) = liblouis::util::translate_text(&cursor_table, cursor_demo_text) {
                println!("  Contracted text: '{}'", contracted);
                if let Some(cursor_char) = contracted.chars().nth(new_cursor_pos) {
                    println!("  Cursor now at character: '{}'", cursor_char);
                }
            }
        }
        Err(e) => {
            println!("  Cursor tracking failed: {}", e);
        }
    }

    sleep(Duration::from_secs(3));

    // Final summary message
    println!();
    println!("Demonstration complete!");
    writer
        .write_contracted_en_us_g2(
            "Contraction demo complete - thank you for watching!",
            CursorPosition::Off,
        )
        .unwrap_or_else(|_| {
            let _ = writer.write_text("Contraction demo complete - thank you for watching!");
        });

    println!();
    println!("Key points demonstrated:");
    println!("- BrlAPI requires CLIENT-SIDE contraction processing");
    println!("- liblouis provides the contraction engine (same as Orca uses)");
    println!("- Contractions significantly reduce braille text length");
    println!("- Cursor positions are properly mapped through contractions");
    println!("- Multiple contraction tables are supported");
    println!("- Graceful fallback to uncontracted text when needed");

    Ok(())
}