use crate::error::Result;
use crate::font::{FontRegistry, GlyphMapping};
use crate::layout::{LayoutDocument, LayoutItem};
use super::ImageResource;
use std::collections::{BTreeMap, BTreeSet};

pub(crate) fn image_resources_for_page(page: &crate::layout::LayoutPage, image_resources: &[ImageResource]) -> String {
    let mut entries = Vec::new();
    if let Some(ref bg_items) = page.background {
        collect_image_entries(bg_items, image_resources, &mut entries);
    }
    if let Some(header) = &page.header {
        collect_image_entries(&header.items, image_resources, &mut entries);
    }
    collect_image_entries(&page.items, image_resources, &mut entries);
    if let Some(footer) = &page.footer {
        collect_image_entries(&footer.items, image_resources, &mut entries);
    }
    if entries.is_empty() { String::new() } else { format!("/XObject << {} >>", entries.join(" ")) }
}

pub(crate) fn collect_page_image_resources(items: &[LayoutItem], resources: &mut Vec<ImageResource>, image_index: &mut usize, font_obj_index: &mut usize) {
    for item in items {
        match item {
            LayoutItem::Image(image) => {
                resources.push(ImageResource {
                    name: format!("Im{}", *image_index),
                    object_id: *font_obj_index + resources.len(),
                    image: image.clone(),
                });
                *image_index += 1;
            }
            LayoutItem::Table(table) => {
                for row in &table.rows {
                    for cell in &row.cells {
                        collect_page_image_resources(&cell.items, resources, image_index, font_obj_index);
                    }
                }
            }
            LayoutItem::Columns(cols) => {
                for col in &cols.columns {
                    collect_page_image_resources(col, resources, image_index, font_obj_index);
                }
            }
            LayoutItem::List(list) => {
                for list_item in &list.items {
                    collect_page_image_resources(&list_item.content, resources, image_index, font_obj_index);
                }
            }
            _ => {}
        }
    }
}

pub(crate) fn collect_opacity_values_from_items(items: &[LayoutItem], opacities: &mut BTreeSet<u8>) {
    for item in items {
        match item {
            LayoutItem::Image(image) => {
                if let Some(op) = image.opacity {
                    if op >= 0.0 && op < 1.0 {
                        opacities.insert((op * 100.0).round() as u8);
                    }
                }
            }
            LayoutItem::Table(table) => {
                for row in &table.rows {
                    for cell in &row.cells {
                        if let Some(op) = cell.fill_opacity {
                            if op >= 0.0 && op < 1.0 {
                                opacities.insert((op * 100.0).round() as u8);
                            }
                        }
                        // Also collect overlay opacity
                        if let Some(op) = cell.overlay_opacity {
                            if op >= 0.0 && op < 1.0 {
                                opacities.insert((op * 100.0).round() as u8);
                            }
                        }
                        collect_opacity_values_from_items(&cell.items, opacities);
                    }
                }
            }
            LayoutItem::Columns(cols) => {
                for col in &cols.columns {
                    collect_opacity_values_from_items(col, opacities);
                }
            }
            LayoutItem::List(list) => {
                for list_item in &list.items {
                    collect_opacity_values_from_items(&list_item.content, opacities);
                }
            }
            _ => {}
        }
    }
}

pub(crate) fn collect_opacity_values(document: &LayoutDocument) -> BTreeSet<u8> {
    let mut opacities = BTreeSet::new();
    for page in &document.pages {
        // Include watermark opacity
        if let Some(ref wm) = page.watermark {
            let pct = (wm.opacity * 100.0).round().clamp(0.0, 100.0) as u8;
            if pct < 100 {
                opacities.insert(pct);
            }
        }
        if let Some(ref bg_items) = page.background {
            collect_opacity_values_from_items(bg_items, &mut opacities);
        }
        if let Some(header) = &page.header {
            collect_opacity_values_from_items(&header.items, &mut opacities);
        }
        collect_opacity_values_from_items(&page.items, &mut opacities);
        if let Some(footer) = &page.footer {
            collect_opacity_values_from_items(&footer.items, &mut opacities);
        }
    }
    opacities
}

pub(crate) fn make_ext_g_state(opacity_pct: u8) -> Vec<u8> {
    let v = opacity_pct as f32 / 100.0;
    format!("<< /Type /ExtGState /ca {:.2} /CA {:.2} >>", v, v).into_bytes()
}

pub(crate) fn collect_image_entries(items: &[LayoutItem], image_resources: &[ImageResource], entries: &mut Vec<String>) {
    for item in items {
        match item {
            LayoutItem::Image(image) => {
                if let Some(resource) = image_resources.iter().find(|r| r.image.x == image.x && r.image.y == image.y && r.image.width == image.width && r.image.height == image.height) {
                    entries.push(format!("/{} {} 0 R", resource.name, resource.object_id));
                }
            }
            LayoutItem::Table(table) => {
                for row in &table.rows {
                    for cell in &row.cells {
                        collect_image_entries(&cell.items, image_resources, entries);
                    }
                }
            }
            LayoutItem::Columns(cols) => {
                for col in &cols.columns {
                    collect_image_entries(col, image_resources, entries);
                }
            }
            LayoutItem::List(list) => {
                for list_item in &list.items {
                    collect_image_entries(&list_item.content, image_resources, entries);
                }
            }
            _ => {}
        }
    }
}

pub(crate) fn collect_used_fonts(
    document: &LayoutDocument,
    fonts: &FontRegistry,
) -> Result<BTreeMap<String, BTreeSet<GlyphMapping>>> {
    let mut usage = BTreeMap::new();
    for page in &document.pages {
        // Collect watermark font usage
        if let Some(ref wm) = page.watermark {
            if let Some(ref family) = wm.font_family {
                if let Ok(glyphs) = fonts.glyphs_for_text(family, &wm.text) {
                    usage
                        .entry(family.clone())
                        .or_insert_with(BTreeSet::new)
                        .extend(glyphs);
                }
            }
        }
        if let Some(ref bg_items) = page.background {
            for item in bg_items {
                collect_fonts_from_item(item, fonts, &mut usage)?;
            }
        }
        if let Some(header) = &page.header {
            for item in &header.items {
                collect_fonts_from_item(item, fonts, &mut usage)?;
            }
        }
        for item in &page.items {
            collect_fonts_from_item(item, fonts, &mut usage)?;
        }
        if let Some(footer) = &page.footer {
            for item in &footer.items {
                collect_fonts_from_item(item, fonts, &mut usage)?;
            }
        }
    }
    Ok(usage)
}

static RENDER_CJK_WARNING_ONCE: std::sync::Once = std::sync::Once::new();

pub(crate) fn warn_render_cjk_missing_font() {
    RENDER_CJK_WARNING_ONCE.call_once(|| {
        eprintln!(
            "[pdfmake_rust] Warning: Rendering CJK characters using Helvetica (no font_family set). \
            Characters will likely not display correctly. \
            Register a CJK font: Document::builder().font(\"family\", \"path.ttf\")"
        );
    });
}

pub(crate) fn collect_fonts_from_item(
    item: &LayoutItem,
    fonts: &FontRegistry,
    usage: &mut BTreeMap<String, BTreeSet<GlyphMapping>>,
) -> Result<()> {
    match item {
        LayoutItem::Text(text) => {
            for run in &text.runs {
                if let Some(family) = &run.style.font_family {
                    let glyphs = fonts.glyphs_for_text(family, &run.text)?;
                    usage
                        .entry(family.clone())
                        .or_insert_with(BTreeSet::new)
                        .extend(glyphs);
                } else if crate::font::detect_special_chars(&run.text).len() > 0 {
                    warn_render_cjk_missing_font();
                }
            }
        }
        LayoutItem::Table(table) => {
            for row in &table.rows {
                for cell in &row.cells {
                    for run in &cell.runs {
                        if let Some(family) = &run.style.font_family {
                            let glyphs = fonts.glyphs_for_text(family, &run.text)?;
                            usage
                                .entry(family.clone())
                                .or_insert_with(BTreeSet::new)
                                .extend(glyphs);
                        } else if crate::font::detect_special_chars(&run.text).len() > 0 {
                            warn_render_cjk_missing_font();
                        }
                    }
                    for nested_item in &cell.items {
                        collect_fonts_from_item(nested_item, fonts, usage)?;
                    }
                }
            }
        }
        LayoutItem::Columns(cols) => {
            for col in &cols.columns {
                for nested_item in col {
                    collect_fonts_from_item(nested_item, fonts, usage)?;
                }
            }
        }
        LayoutItem::List(list) => {
            for list_item in &list.items {
                for nested_item in &list_item.content {
                    collect_fonts_from_item(nested_item, fonts, usage)?;
                }
            }
        }
        LayoutItem::Toc(toc) => {
            for item in &toc.title {
                collect_fonts_from_item(item, fonts, usage)?;
            }
            for entry in &toc.entries {
                if let Some(family) = &entry.style.font_family {
                    let glyphs = fonts.glyphs_for_text(family, &entry.text)?;
                    usage
                        .entry(family.clone())
                        .or_insert_with(BTreeSet::new)
                        .extend(glyphs);
                }
            }
        }
        LayoutItem::Image(_) | LayoutItem::Qr(_) | LayoutItem::Svg(_) | LayoutItem::Canvas(_) => {}
    }
    Ok(())
}
