use crate::{
    image::ImageFormat,
    model::{CanvasShape, FontSource, HeaderFooter, ImageCover, Margins, PageSize, TableLayoutConfig},
    qr::DarkModule,
    style::ResolvedStyle,
    vector::VectorShape,
};
use serde::{Deserialize, Serialize};
use std::collections::{BTreeMap, HashSet};
use std::sync::Arc;

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct LayoutDocument {
    pub page_size: PageSize,
    pub page_margins: Margins,
    pub fonts: BTreeMap<String, FontSource>,
    pub pages: Vec<LayoutPage>,
}

#[derive(Debug, Clone, Default, PartialEq)]
pub struct PageCustomProperties {
    pub header: Option<HeaderFooter>,
    pub footer: Option<HeaderFooter>,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct LayoutPage {
    pub items: Vec<LayoutItem>,
    pub background: Option<Vec<LayoutItem>>,
    pub watermark: Option<LayoutWatermark>,
    pub header: Option<LayoutHeaderFooter>,
    pub footer: Option<LayoutHeaderFooter>,
    #[serde(skip)]
    pub custom_properties: PageCustomProperties,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct LayoutHeaderFooter {
    pub items: Vec<LayoutItem>,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct LayoutWatermark {
    pub text: String,
    pub font_family: Option<String>,
    pub font_size: f32,
    pub color_r: f32,
    pub color_g: f32,
    pub color_b: f32,
    pub opacity: f32,
    pub angle: f32,
    pub text_width: f32,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum LayoutItem {
    Text(LayoutTextItem),
    Table(LayoutTable),
    Image(LayoutImage),
    Qr(LayoutQr),
    Svg(LayoutSvg),
    Columns(LayoutColumns),
    List(LayoutList),
    Canvas(LayoutCanvas),
    Toc(LayoutToc),
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct LayoutToc {
    pub x: f32,
    pub y: f32,
    pub width: f32,
    pub height: f32,
    pub id: Option<String>,
    pub title: Vec<LayoutItem>,
    pub entries: Vec<LayoutTocEntry>,
    pub text_style: ResolvedStyle,
    pub number_style: ResolvedStyle,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct LayoutTocEntry {
    pub text: String,
    pub style: ResolvedStyle,
    pub page_number: Option<usize>,
    pub x: f32,
    pub y: f32,
    pub line_height: f32,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct LayoutColumns {
    pub x: f32,
    pub y: f32,
    pub width: f32,
    pub height: f32,
    pub columns: Vec<Vec<LayoutItem>>,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct LayoutList {
    pub x: f32,
    pub y: f32,
    pub width: f32,
    pub height: f32,
    pub items: Vec<ListLayoutItem>,
    pub marker_color: Option<String>,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ListLayoutItem {
    pub content: Vec<LayoutItem>,
    pub marker: Option<LayoutListMarker>,
}

#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub enum BulletShape {
    Disc,
    Circle,
    Square,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum LayoutListMarker {
    Bullet { x: f32, y: f32, size: f32, shape: BulletShape, color: Option<String> },
    Number { text: String, x: f32, y: f32, font_size: f32, font_family: Option<String>, color: Option<String> },
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct LayoutTextItem {
    pub text: String,
    pub x: f32,
    pub y: f32,
    pub width: f32,
    pub available_width: f32,
    pub line_height: f32,
    pub runs: Vec<LayoutRun>,
    pub is_justify: bool,
    pub id: Option<String>,
    pub page_reference: Option<String>,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct LayoutTable {
    pub x: f32,
    pub y: f32,
    pub width: f32,
    pub height: f32,
    pub rows: Vec<LayoutTableRow>,
    pub borders: Vec<LayoutLine>,
    pub layout_config: Option<TableLayoutConfig>,
    pub h_line_when_broken: Option<bool>,
    /// Edges with custom border colors (from cells with border_color set).
    /// Used to skip default black borders on shared edges that have colored counterparts.
    pub colored_edges: HashSet<(i32, i32, i32, i32)>,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct LayoutTableRow {
    pub y: f32,
    pub height: f32,
    pub cells: Vec<LayoutTableCell>,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct LayoutImage {
    pub x: f32,
    pub y: f32,
    pub width: f32,
    pub height: f32,
    pub data: Arc<Vec<u8>>,
    pub format: ImageFormat,
    pub intrinsic_width: u32,
    pub intrinsic_height: u32,
    pub cover: Option<ImageCover>,
    pub opacity: Option<f32>,
    pub link: Option<String>,
    pub link_to_page: Option<usize>,
    pub link_to_destination: Option<String>,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct LayoutQr {
    pub x: f32,
    pub y: f32,
    pub width: f32,
    pub height: f32,
    pub modules: usize,
    pub dark_modules: Vec<DarkModule>,
    pub foreground: Option<String>,
    pub background: Option<String>,
    pub padding: Option<f32>,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct LayoutSvg {
    pub x: f32,
    pub y: f32,
    pub width: f32,
    pub height: f32,
    pub view_width: f32,
    pub view_height: f32,
    pub shapes: Vec<VectorShape>,
    pub link: Option<String>,
    pub link_to_page: Option<usize>,
    pub link_to_destination: Option<String>,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct LayoutCanvas {
    pub x: f32,
    pub y: f32,
    pub width: f32,
    pub height: f32,
    pub shapes: Vec<CanvasShape>,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct LayoutTableCell {
    pub x: f32,
    pub y: f32,
    pub width: f32,
    pub height: f32,
    pub padding: f32,
    pub runs: Vec<LayoutRun>,
    pub items: Vec<LayoutItem>,
    pub borders: Vec<LayoutLine>,
    pub col_span: usize,
    pub row_span: usize,
    pub fill_color: Option<String>,
    pub fill_opacity: Option<f32>,
    pub border_colors: Option<Vec<String>>,
    pub overlay_pattern: Option<String>,
    pub overlay_opacity: Option<f32>,
    pub border: Option<Vec<bool>>,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct LayoutLine {
    pub x1: f32,
    pub y1: f32,
    pub x2: f32,
    pub y2: f32,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct LayoutRun {
    pub text: String,
    pub x_offset: f32,
    pub y_offset: f32,
    pub width: f32,
    pub font_size: f32,
    pub style: ResolvedStyle,
    pub is_justify: bool,
    pub available_width: f32,
}
