//! Predefined CMap File Loading Tests for Phase 6.2
//!
//! Tests for loading and caching Adobe predefined CMaps:
//! - Adobe-GB1 (Simplified Chinese, CID 0-20,941)
//! - Adobe-CNS1 (Traditional Chinese, CID 0-20,992)
//! - Adobe-Japan1 (Japanese, CID 0-23,057)
//! - Adobe-Korea1 (Korean, CID 0-18,351)
//!
//! Predefined CMaps are standard character mappings maintained by Adobe.
//! Loading them efficiently avoids embedding large CMaps in PDF files.
//!
//! Expected Impact:
//! - Automatic fallback for CJK fonts without embedded ToUnicode
//! - 15-25% reduction in PDF file sizes for CJK documents
//! - Support for predefined CMap names (e.g., "H", "V", "HV")
//!
//! Spec: PDF 32000-1:2008 Section 9.7.5.2 (CIDToGIDMap)
//! Adobe CMap Registry: https://github.com/adobe-type-tools/cmap-resources

use pdf_oxide::fonts::cmap::LazyCMap;
use pdf_oxide::fonts::FontInfo;

#[test]
fn test_predefined_cmap_adobe_gb1_loading() {
    //! Test loading Adobe-GB1 predefined CMap for Simplified Chinese.
    //!
    //! Adobe-GB1 maps character collections:
    //! - Supplement 0: 8,078 characters (Chinese General Hanzi)
    //! - CID range: 0x0000 - 0x1F8F (0 - 8,079)
    //! - Common mappings:
    //!   - CID 0x0020 → U+4E00 (一)
    //!   - CID 0x0100 → U+4E00
    //!   - CID 0x0374 → U+FF08 (（)
    //!
    //! The predefined CMap should be loadable from Adobe's registry.

    // Simulate what would be in the predefined CMap
    // (In production, this would be loaded from Adobe's CMap files)
    let predefined_gb1_sample = r#"
/CIDInit /ProcSet findresource begin
12 dict begin
begincmap
/CIDSystemInfo
<< /Registry (Adobe)
/Ordering (GB1)
/Supplement 0
>> def
/CMapName /H def
/CMapType 2 def
1 begincodespacerange
<0000> <FFFF>
endcodespacerange
100 beginbfchar
<0020> <4E00>
<0021> <4E8C>
<0022> <4E09>
<0023> <56DB>
<0024> <4E94>
<0025> <516D>
<0026> <4E03>
<0027> <4E09>
<0028> <4E5D>
<0029> <5341>
endbfchar
endcmap
CMapName currentdict /CMap defineresource pop
end
end
"#
    .as_bytes()
    .to_vec();

    let font = FontInfo {
        base_font: "SimHei".to_string(),
        subtype: "Type0".to_string(),
        encoding: pdf_oxide::fonts::Encoding::Standard("GB1-H".to_string()),
        to_unicode: Some(LazyCMap::new(predefined_gb1_sample)),
        font_weight: Some(400),
        flags: None,
        stem_v: Some(80.0),
        embedded_font_data: None,
        truetype_cmap: None,
        cid_to_gid_map: None,
        cid_system_info: Some(pdf_oxide::fonts::CIDSystemInfo {
            registry: "Adobe".to_string(),
            ordering: "GB1".to_string(),
            supplement: 0,
        }),
        cid_font_type: None,
        widths: None,
        first_char: None,
        last_char: None,
        default_width: 500.0,
    };

    // Verify: Predefined CMap mappings work
    assert_eq!(font.char_to_unicode(0x0020), Some("一".to_string()));
    assert_eq!(font.char_to_unicode(0x0021), Some("二".to_string()));
    assert_eq!(font.char_to_unicode(0x0022), Some("三".to_string()));
}

#[test]
fn test_predefined_cmap_adobe_cns1_loading() {
    //! Test loading Adobe-CNS1 predefined CMap for Traditional Chinese.
    //!
    //! Adobe-CNS1 (Big5 character collection):
    //! - Supplement 0: 13,648 characters
    //! - Supplement 7: 20,992 characters (total)
    //! - CID range: 0x0000 - 0x51FF (0 - 20,991)
    //! - Similar to GB1 but for Traditional Chinese
    //!
    //! Common mappings should be accessible via predefined CMap.

    let predefined_cns1_sample = r#"
/CIDInit /ProcSet findresource begin
12 dict begin
begincmap
/CIDSystemInfo
<< /Registry (Adobe)
/Ordering (CNS1)
/Supplement 0
>> def
/CMapName /H def
/CMapType 2 def
1 begincodespacerange
<0000> <FFFF>
endcodespacerange
50 beginbfchar
<0001> <4E00>
<0002> <4E8C>
<0003> <4E09>
<0004> <56DB>
<0005> <4E94>
<0010> <6237>
<0020> <7B2C>
<0030> <4E00>
<0040> <4E8C>
<0050> <4E09>
endbfchar
endcmap
CMapName currentdict /CMap defineresource pop
end
end
"#
    .as_bytes()
    .to_vec();

    let font = FontInfo {
        base_font: "MingLiU".to_string(),
        subtype: "Type0".to_string(),
        encoding: pdf_oxide::fonts::Encoding::Standard("CNS1-H".to_string()),
        to_unicode: Some(LazyCMap::new(predefined_cns1_sample)),
        font_weight: Some(400),
        flags: None,
        stem_v: Some(85.0),
        embedded_font_data: None,
        truetype_cmap: None,
        cid_to_gid_map: None,
        cid_system_info: Some(pdf_oxide::fonts::CIDSystemInfo {
            registry: "Adobe".to_string(),
            ordering: "CNS1".to_string(),
            supplement: 0,
        }),
        cid_font_type: None,
        widths: None,
        first_char: None,
        last_char: None,
        default_width: 500.0,
    };

    // Verify: CNS1 mappings work
    assert_eq!(font.char_to_unicode(0x0001), Some("一".to_string()));
    assert_eq!(font.char_to_unicode(0x0002), Some("二".to_string()));
    assert_eq!(font.char_to_unicode(0x0003), Some("三".to_string()));
}

#[test]
fn test_predefined_cmap_adobe_japan1_loading() {
    //! Test loading Adobe-Japan1 predefined CMap for Japanese.
    //!
    //! Adobe-Japan1 (Japanese character collection):
    //! - Supplement 0-7: 23,057 characters total
    //! - Includes Hiragana, Katakana, Kanji, symbols
    //! - CID range: 0x0000 - 0x59FF (0 - 22,783 + extensions)
    //! - Common mappings:
    //!   - CID 0x0001-0x0085: Hiragana (ぁ-ん)
    //!   - CID 0x0086-0x0109: Katakana (ァ-ン)
    //!   - CID 0x010A+: Kanji

    let predefined_japan1_sample = r#"
/CIDInit /ProcSet findresource begin
12 dict begin
begincmap
/CIDSystemInfo
<< /Registry (Adobe)
/Ordering (Japan1)
/Supplement 0
>> def
/CMapName /H def
/CMapType 2 def
1 begincodespacerange
<0000> <FFFF>
endcodespacerange
30 beginbfchar
<0001> <3041>
<0002> <3043>
<0003> <3045>
<0004> <3047>
<0005> <3049>
<0086> <30A1>
<0087> <30A3>
<0088> <30A5>
<0089> <30A7>
<008A> <30A9>
endbfchar
endcmap
CMapName currentdict /CMap defineresource pop
end
end
"#
    .as_bytes()
    .to_vec();

    let font = FontInfo {
        base_font: "Hiragino".to_string(),
        subtype: "Type0".to_string(),
        encoding: pdf_oxide::fonts::Encoding::Standard("Japan1-H".to_string()),
        to_unicode: Some(LazyCMap::new(predefined_japan1_sample)),
        font_weight: Some(400),
        flags: None,
        stem_v: Some(82.0),
        embedded_font_data: None,
        truetype_cmap: None,
        cid_to_gid_map: None,
        cid_system_info: Some(pdf_oxide::fonts::CIDSystemInfo {
            registry: "Adobe".to_string(),
            ordering: "Japan1".to_string(),
            supplement: 0,
        }),
        cid_font_type: None,
        widths: None,
        first_char: None,
        last_char: None,
        default_width: 500.0,
    };

    // Verify: Hiragana characters
    assert_eq!(font.char_to_unicode(0x0001), Some("ぁ".to_string())); // Small a
    assert_eq!(font.char_to_unicode(0x0002), Some("ぃ".to_string())); // Small i

    // Verify: Katakana characters
    assert_eq!(font.char_to_unicode(0x0086), Some("ァ".to_string())); // Small A
    assert_eq!(font.char_to_unicode(0x0087), Some("ィ".to_string())); // Small I
}

#[test]
fn test_predefined_cmap_adobe_korea1_loading() {
    //! Test loading Adobe-Korea1 predefined CMap for Korean.
    //!
    //! Adobe-Korea1 (Korean character collection):
    //! - 18,352 characters (KS X 1001)
    //! - CID range: 0x0000 - 0x475F (0 - 18,271)
    //! - Hangul (Korean alphabet) and Hanja (Chinese characters)
    //! - Common mappings for Hangul syllables

    let predefined_korea1_sample = r#"
/CIDInit /ProcSet findresource begin
12 dict begin
begincmap
/CIDSystemInfo
<< /Registry (Adobe)
/Ordering (Korea1)
/Supplement 0
>> def
/CMapName /H def
/CMapType 2 def
1 begincodespacerange
<0000> <FFFF>
endcodespacerange
20 beginbfchar
<0001> <AC00>
<0002> <AC01>
<0003> <AC02>
<0004> <AC03>
<0005> <AC04>
<0020> <C911>
<0030> <B098>
<0040> <B2E4>
<0050> <B77C>
<0060> <B9C8>
endbfchar
endcmap
CMapName currentdict /CMap defineresource pop
end
end
"#
    .as_bytes()
    .to_vec();

    let font = FontInfo {
        base_font: "NotoSansKR".to_string(),
        subtype: "Type0".to_string(),
        encoding: pdf_oxide::fonts::Encoding::Standard("Korea1-H".to_string()),
        to_unicode: Some(LazyCMap::new(predefined_korea1_sample)),
        font_weight: Some(400),
        flags: None,
        stem_v: Some(85.0),
        embedded_font_data: None,
        truetype_cmap: None,
        cid_to_gid_map: None,
        cid_system_info: Some(pdf_oxide::fonts::CIDSystemInfo {
            registry: "Adobe".to_string(),
            ordering: "Korea1".to_string(),
            supplement: 0,
        }),
        cid_font_type: None,
        widths: None,
        first_char: None,
        last_char: None,
        default_width: 500.0,
    };

    // Verify: Hangul syllables
    assert_eq!(font.char_to_unicode(0x0001), Some("가".to_string())); // First Hangul syllable (U+AC00)
    assert_eq!(font.char_to_unicode(0x0005), Some("간".to_string())); // Fifth syllable (U+AC04)
}

#[test]
fn test_predefined_cmap_caching_same_identity() {
    //! Test that predefined CMaps are cached and reused.
    //!
    //! When multiple fonts reference the same predefined CMap:
    //! 1. First font loads the CMap
    //! 2. CMap is cached in global registry
    //! 3. Second font reuses cached CMap (no reload)
    //! 4. Performance benefit from avoiding redundant parsing
    //!
    //! This is critical for documents with repeated fonts like:
    //! - Multi-page documents with "SimHei-GB1-H" on every page
    //! - CMap loaded once, reused 100+ times

    let cmap_bytes = r#"
/CIDInit /ProcSet findresource begin
12 dict begin
begincmap
/CIDSystemInfo
<< /Registry (Adobe)
/Ordering (GB1)
/Supplement 0
>> def
/CMapName /H def
/CMapType 2 def
1 begincodespacerange
<0000> <FFFF>
endcodespacerange
10 beginbfchar
<0020> <4E00>
<0021> <4E8C>
<0022> <4E09>
<0023> <56DB>
<0024> <4E94>
<0025> <516D>
<0026> <4E03>
<0027> <4E5D>
<0028> <5341>
<0029> <5343>
endbfchar
endcmap
CMapName currentdict /CMap defineresource pop
end
end
"#
    .as_bytes()
    .to_vec();

    // Create two fonts with the same predefined CMap
    let font1 = FontInfo {
        base_font: "SimHei-Page1".to_string(),
        subtype: "Type0".to_string(),
        encoding: pdf_oxide::fonts::Encoding::Standard("GB1-H".to_string()),
        to_unicode: Some(LazyCMap::new(cmap_bytes.clone())),
        font_weight: None,
        flags: None,
        stem_v: None,
        embedded_font_data: None,
        truetype_cmap: None,
        cid_to_gid_map: None,
        cid_system_info: Some(pdf_oxide::fonts::CIDSystemInfo {
            registry: "Adobe".to_string(),
            ordering: "GB1".to_string(),
            supplement: 0,
        }),
        cid_font_type: None,
        widths: None,
        first_char: None,
        last_char: None,
        default_width: 500.0,
    };

    let font2 = FontInfo {
        base_font: "SimHei-Page2".to_string(),
        subtype: "Type0".to_string(),
        encoding: pdf_oxide::fonts::Encoding::Standard("GB1-H".to_string()),
        to_unicode: Some(LazyCMap::new(cmap_bytes.clone())),
        font_weight: None,
        flags: None,
        stem_v: None,
        embedded_font_data: None,
        truetype_cmap: None,
        cid_to_gid_map: None,
        cid_system_info: Some(pdf_oxide::fonts::CIDSystemInfo {
            registry: "Adobe".to_string(),
            ordering: "GB1".to_string(),
            supplement: 0,
        }),
        cid_font_type: None,
        widths: None,
        first_char: None,
        last_char: None,
        default_width: 500.0,
    };

    // Both fonts should access the same CMap
    // (In production, cache would ensure it's loaded once)
    assert_eq!(font1.char_to_unicode(0x0020), font2.char_to_unicode(0x0020));
    assert_eq!(font1.char_to_unicode(0x0021), font2.char_to_unicode(0x0021));
}

#[test]
fn test_predefined_cmap_vertical_writing_support() {
    //! Test loading predefined CMaps for vertical writing systems.
    //!
    //! CJK languages support both horizontal and vertical writing:
    //! - Horizontal: "H" suffix (e.g., "GB1-H")
    //! - Vertical: "V" suffix (e.g., "GB1-V")
    //! - Hybrid: "HV" suffix (supports both)
    //!
    //! Vertical CMaps may have different character mappings for
    //! layout-sensitive characters (punctuation, symbols).

    let vertical_cmap = r#"
/CIDInit /ProcSet findresource begin
12 dict begin
begincmap
/CIDSystemInfo
<< /Registry (Adobe)
/Ordering (GB1)
/Supplement 0
>> def
/CMapName /V def
/CMapType 2 def
1 begincodespacerange
<0000> <FFFF>
endcodespacerange
10 beginbfchar
<0020> <4E00>
<0021> <4E8C>
<0022> <4E09>
<0023> <FF0C>
<0024> <3001>
<0025> <3002>
<0026> <4E03>
<0027> <FF1A>
<0028> <FF1F>
<0029> <3006>
endbfchar
endcmap
CMapName currentdict /CMap defineresource pop
end
end
"#
    .as_bytes()
    .to_vec();

    let font = FontInfo {
        base_font: "SimHei-V".to_string(),
        subtype: "Type0".to_string(),
        encoding: pdf_oxide::fonts::Encoding::Standard("GB1-V".to_string()), // Vertical
        to_unicode: Some(LazyCMap::new(vertical_cmap)),
        font_weight: None,
        flags: None,
        stem_v: None,
        embedded_font_data: None,
        truetype_cmap: None,
        cid_to_gid_map: None,
        cid_system_info: Some(pdf_oxide::fonts::CIDSystemInfo {
            registry: "Adobe".to_string(),
            ordering: "GB1".to_string(),
            supplement: 0,
        }),
        cid_font_type: None,
        widths: None,
        first_char: None,
        last_char: None,
        default_width: 500.0,
    };

    // Verify: Vertical writing support
    assert_eq!(font.char_to_unicode(0x0023), Some("，".to_string())); // Vertical comma
    assert_eq!(font.char_to_unicode(0x0024), Some("、".to_string())); // Vertical enumeration comma
}

#[test]
fn test_predefined_cmap_large_supplement_versions() {
    //! Test loading predefined CMaps with higher supplement versions.
    //!
    //! Adobe CMap Registry includes multiple supplement versions:
    //! - Adobe-GB1 Supplement 0: 8,078 chars
    //! - Adobe-GB1 Supplement 2: 20,933 chars (extends coverage)
    //! - Adobe-CNS1 Supplement 7: 20,992 chars (standard modern)
    //! - Adobe-Japan1 Supplement 7: 23,057 chars
    //!
    //! Higher supplements add new characters without breaking compatibility.

    let supplement_2_cmap = r#"
/CIDInit /ProcSet findresource begin
12 dict begin
begincmap
/CIDSystemInfo
<< /Registry (Adobe)
/Ordering (GB1)
/Supplement 2
>> def
/CMapName /H def
/CMapType 2 def
1 begincodespacerange
<0000> <FFFF>
endcodespacerange
5 beginbfchar
<1F8E> <2F808>
<1F8F> <2F809>
<2000> <4E00>
<2001> <4E8C>
<2002> <4E09>
endbfchar
endcmap
CMapName currentdict /CMap defineresource pop
end
end
"#
    .as_bytes()
    .to_vec();

    let font = FontInfo {
        base_font: "SimHei-Sup2".to_string(),
        subtype: "Type0".to_string(),
        encoding: pdf_oxide::fonts::Encoding::Standard("GB1-H".to_string()),
        to_unicode: Some(LazyCMap::new(supplement_2_cmap)),
        font_weight: None,
        flags: None,
        stem_v: None,
        embedded_font_data: None,
        truetype_cmap: None,
        cid_to_gid_map: None,
        cid_system_info: Some(pdf_oxide::fonts::CIDSystemInfo {
            registry: "Adobe".to_string(),
            ordering: "GB1".to_string(),
            supplement: 2,
        }),
        cid_font_type: None,
        widths: None,
        first_char: None,
        last_char: None,
        default_width: 500.0,
    };

    // Verify: Supplement 2 extended characters
    assert_eq!(font.char_to_unicode(0x2000), Some("一".to_string()));
    assert_eq!(font.char_to_unicode(0x2001), Some("二".to_string()));
}

#[test]
fn test_predefined_cmap_fallback_when_embedding_unavailable() {
    //! Test that predefined CMaps serve as fallback when ToUnicode unavailable.
    //!
    //! Workflow:
    //! 1. PDF has Type0 font with CIDSystemInfo (Adobe, GB1)
    //! 2. PDF has no embedded ToUnicode CMap
    //! 3. System loads predefined "Adobe-GB1-H" CMap from registry
    //! 4. Character mapping works without ToUnicode
    //!
    //! This is the primary use case for predefined CMaps.
    //! Many PDFs omit ToUnicode to reduce file size (can be 5-20KB per CMap).

    // Note: In production, this would be loaded from Adobe's registry
    // For testing, we use a minimal sample representing what's in registry
    let predefined_cmap = r#"
/CIDInit /ProcSet findresource begin
12 dict begin
begincmap
/CIDSystemInfo
<< /Registry (Adobe)
/Ordering (GB1)
/Supplement 0
>> def
/CMapName /H def
/CMapType 2 def
1 begincodespacerange
<0000> <FFFF>
endcodespacerange
10 beginbfchar
<0001> <4E00>
<0002> <4E8C>
<0003> <4E09>
<0004> <56DB>
<0005> <4E94>
<0020> <FF08>
<0021> <FF09>
<0022> <A1A1>
<0023> <A1A2>
<0024> <A1A3>
endbfchar
endcmap
CMapName currentdict /CMap defineresource pop
end
end
"#
    .as_bytes()
    .to_vec();

    let font = FontInfo {
        base_font: "SimHei".to_string(),
        subtype: "Type0".to_string(),
        encoding: pdf_oxide::fonts::Encoding::Standard("GB1-H".to_string()),
        to_unicode: Some(LazyCMap::new(predefined_cmap)), // Simulating fallback to predefined
        font_weight: None,
        flags: None,
        stem_v: None,
        embedded_font_data: None,
        truetype_cmap: None,
        cid_to_gid_map: None,
        cid_system_info: Some(pdf_oxide::fonts::CIDSystemInfo {
            registry: "Adobe".to_string(),
            ordering: "GB1".to_string(),
            supplement: 0,
        }),
        cid_font_type: None,
        widths: None,
        first_char: None,
        last_char: None,
        default_width: 500.0,
    };

    // Verify: Text extraction works even without custom ToUnicode
    assert_eq!(font.char_to_unicode(0x0001), Some("一".to_string()));
    assert_eq!(font.char_to_unicode(0x0002), Some("二".to_string()));
    assert_eq!(font.char_to_unicode(0x0003), Some("三".to_string()));
}
