use serde::{Deserialize, Serialize};
use super::*;

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SectionNode {
    pub content: Vec<DocumentNode>,
    pub page_size: Option<PageSize>,
    pub page_margins: Option<Margins>,
    pub header: Option<HeaderFooter>,
    pub footer: Option<HeaderFooter>,
    pub watermark: Option<WatermarkConfig>,
}

impl SectionNode {
    pub fn new(content: Vec<DocumentNode>) -> Self {
        Self {
            content,
            page_size: None,
            page_margins: None,
            header: None,
            footer: None,
            watermark: None,
        }
    }

    pub fn page_size(mut self, page_size: PageSize) -> Self {
        self.page_size = Some(page_size);
        self
    }

    pub fn page_margins(mut self, margins: Margins) -> Self {
        self.page_margins = Some(margins);
        self
    }

    pub fn header(mut self, node: DocumentNode) -> Self {
        self.header = Some(HeaderFooter::from_static(node));
        self
    }

    pub fn header_fn<F>(mut self, f: F) -> Self
    where
        F: Fn(u32, u32, &PageSize) -> DocumentNode + Send + Sync + 'static,
    {
        self.header = Some(HeaderFooter::from_fn(f));
        self
    }

    pub fn footer(mut self, node: DocumentNode) -> Self {
        self.footer = Some(HeaderFooter::from_static(node));
        self
    }

    pub fn footer_fn<F>(mut self, f: F) -> Self
    where
        F: Fn(u32, u32, &PageSize) -> DocumentNode + Send + Sync + 'static,
    {
        self.footer = Some(HeaderFooter::from_fn(f));
        self
    }

    pub fn build(self) -> Self {
        self
    }
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum DocumentNode {
    Text(TextNode),
    Stack(Vec<DocumentNode>),
    Table(TableNode),
    #[cfg(feature = "image")]
    Image(ImageNode),
    #[cfg(feature = "svg")]
    Svg(SvgNode),
    #[cfg(feature = "qr")]
    Qr(QrNode),
    Columns(ColumnsNode),
    Absolute(AbsoluteNode),
    List(ListNode),
    Section(Box<SectionNode>),
    Canvas(CanvasNode),
    Toc(TocNode),
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct CanvasNode {
    pub shapes: Vec<CanvasShape>,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "type")]
pub enum CanvasShape {
    #[serde(rename = "rect")]
    Rect {
        x: f32,
        y: f32,
        w: f32,
        h: f32,
        #[serde(default)]
        color: Option<String>,
        #[serde(default)]
        line_color: Option<String>,
        #[serde(default)]
        line_width: Option<f32>,
        #[serde(default)]
        fill_opacity: Option<f32>,
        #[serde(default)]
        stroke_opacity: Option<f32>,
    },
    #[serde(rename = "ellipse")]
    Ellipse {
        x: f32,
        y: f32,
        r1: f32,
        r2: f32,
        #[serde(default)]
        color: Option<String>,
        #[serde(default)]
        line_color: Option<String>,
        #[serde(default)]
        line_width: Option<f32>,
    },
    #[serde(rename = "line")]
    Line {
        x1: f32,
        y1: f32,
        x2: f32,
        y2: f32,
        #[serde(default)]
        line_color: Option<String>,
        #[serde(default)]
        line_width: Option<f32>,
    },
    #[serde(rename = "polyline")]
    Polyline {
        points: Vec<CanvasPoint>,
        #[serde(default)]
        close_path: bool,
        #[serde(default)]
        color: Option<String>,
        #[serde(default)]
        line_color: Option<String>,
        #[serde(default)]
        line_width: Option<f32>,
    },
}

#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub struct CanvasPoint {
    pub x: f32,
    pub y: f32,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct TocNode {
    pub id: Option<String>,
    pub title: Option<Box<DocumentNode>>,
    pub text_style: Option<Style>,
    pub number_style: Option<Style>,
    pub text_margin: Option<Margins>,
}

impl TocNode {
    pub fn new() -> Self {
        Self {
            id: None,
            title: None,
            text_style: None,
            number_style: None,
            text_margin: None,
        }
    }

    pub fn id(mut self, id: impl Into<String>) -> Self {
        self.id = Some(id.into());
        self
    }

    pub fn title(mut self, title: DocumentNode) -> Self {
        self.title = Some(Box::new(title));
        self
    }

    pub fn text_style(mut self, style: Style) -> Self {
        self.text_style = Some(style);
        self
    }

    pub fn number_style(mut self, style: Style) -> Self {
        self.number_style = Some(style);
        self
    }

    pub fn text_margin(mut self, margin: Margins) -> Self {
        self.text_margin = Some(margin);
        self
    }
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ColumnsNode {
    pub columns: Vec<Vec<DocumentNode>>,
    pub column_gap: f32,
    pub snaking_columns: bool,
}

impl ColumnsNode {
    pub fn new(columns: Vec<Vec<DocumentNode>>) -> Self {
        Self { columns, column_gap: 10.0, snaking_columns: false }
    }

    pub fn column_gap(mut self, gap: f32) -> Self {
        self.column_gap = gap;
        self
    }

    pub fn snaking_columns(mut self, value: bool) -> Self {
        self.snaking_columns = value;
        self
    }
}

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

#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub enum OrderedMarkerType {
    Decimal,
    LowerAlpha,
    UpperAlpha,
    LowerRoman,
    UpperRoman,
    None,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct AbsoluteNode {
    pub content: Box<DocumentNode>,
    pub left: Option<f32>,
    pub top: Option<f32>,
    pub right: Option<f32>,
    pub bottom: Option<f32>,
    pub center_horizontal: bool,
    pub center_vertical: bool,
}

impl AbsoluteNode {
    pub fn new(content: DocumentNode) -> Self {
        Self {
            content: Box::new(content),
            left: None,
            top: None,
            right: None,
            bottom: None,
            center_horizontal: false,
            center_vertical: false,
        }
    }

    pub fn left(mut self, value: f32) -> Self {
        self.left = Some(value);
        self
    }

    pub fn top(mut self, value: f32) -> Self {
        self.top = Some(value);
        self
    }

    pub fn right(mut self, value: f32) -> Self {
        self.right = Some(value);
        self
    }

    pub fn bottom(mut self, value: f32) -> Self {
        self.bottom = Some(value);
        self
    }

    pub fn center_horizontal(mut self) -> Self {
        self.center_horizontal = true;
        self
    }
    
    pub fn center_vertical(mut self) -> Self {
        self.center_vertical = true;
        self
    }
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ListNode {
    pub items: Vec<DocumentNode>,
    pub ordered: bool,
    pub marker_type: Option<String>,
    pub start: Option<usize>,
    pub separator: Option<String>,
    pub reversed: bool,
    pub color: Option<String>,
}

impl ListNode {
    pub fn unordered(items: Vec<DocumentNode>) -> Self {
        Self {
            items, ordered: false, marker_type: None, start: None,
            separator: None, reversed: false, color: None,
        }
    }

    pub fn ordered(items: Vec<DocumentNode>) -> Self {
        Self {
            items, ordered: true, marker_type: None, start: Some(1),
            separator: Some(".".to_string()),
            reversed: false, color: None,
        }
    }

    pub fn marker_type(mut self, marker_type: impl Into<String>) -> Self {
        self.marker_type = Some(marker_type.into());
        self
    }

    pub fn unordered_type(mut self, t: UnorderedMarkerType) -> Self {
        self.ordered = false;
        self.marker_type = Some(match t {
            UnorderedMarkerType::Disc => "disc",
            UnorderedMarkerType::Circle => "circle",
            UnorderedMarkerType::Square => "square",
            UnorderedMarkerType::None => "none",
        }.to_string());
        self
    }

    pub fn ordered_type(mut self, t: OrderedMarkerType) -> Self {
        self.ordered = true;
        self.marker_type = Some(match t {
            OrderedMarkerType::Decimal => "decimal",
            OrderedMarkerType::LowerAlpha => "lower-alpha",
            OrderedMarkerType::UpperAlpha => "upper-alpha",
            OrderedMarkerType::LowerRoman => "lower-roman",
            OrderedMarkerType::UpperRoman => "upper-roman",
            OrderedMarkerType::None => "none",
        }.to_string());
        self
    }

    pub fn start(mut self, start: usize) -> Self {
        self.start = Some(start);
        self
    }

    pub fn separator(mut self, separator: impl Into<String>) -> Self {
        self.separator = Some(separator.into());
        self
    }

    pub fn reversed(mut self) -> Self {
        self.reversed = true;
        self
    }

    pub fn color(mut self, color: impl Into<String>) -> Self {
        self.color = Some(color.into());
        self
    }
}

impl DocumentNode {
    pub fn text(text: impl Into<String>) -> Self {
        Self::Text(TextNode::new(text))
    }

    pub fn stack(nodes: Vec<DocumentNode>) -> Self {
        Self::Stack(nodes)
    }

    pub fn table(table: TableNode) -> Self {
        Self::Table(table)
    }

    #[cfg(feature = "image")]
    pub fn image(image: ImageNode) -> Self {
        Self::Image(image)
    }

    #[cfg(feature = "svg")]
    pub fn svg(svg: SvgNode) -> Self {
        Self::Svg(svg)
    }

    #[cfg(feature = "qr")]
    pub fn qr(qr: QrNode) -> Self {
        Self::Qr(qr)
    }

    pub fn columns(columns: ColumnsNode) -> Self {
        Self::Columns(columns)
    }

    pub fn absolute(abs: AbsoluteNode) -> Self {
        Self::Absolute(abs)
    }

    pub fn list(list: ListNode) -> Self {
        Self::List(list)
    }

    pub fn section(section: SectionNode) -> Self {
        Self::Section(Box::new(section))
    }

    pub fn canvas(shapes: Vec<CanvasShape>) -> Self {
        Self::Canvas(CanvasNode { shapes })
    }

    pub fn toc(toc: TocNode) -> Self {
        Self::Toc(toc)
    }
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct TextNode {
    pub inlines: Vec<InlineFragment>,
    pub style_names: Vec<String>,
    pub style: Style,
    pub page_break: Option<PageBreakMode>,
    pub id: Option<String>,
    pub page_reference: Option<String>,
    pub toc_item: Option<String>,
}

impl TextNode {
    pub fn new(text: impl Into<String>) -> Self {
        Self { inlines: vec![InlineFragment::new(text)], style_names: Vec::new(), style: Style::new(), page_break: None, id: None, page_reference: None, toc_item: None }
    }

    pub fn with_inlines(inlines: Vec<InlineFragment>) -> Self {
        Self { inlines, style_names: Vec::new(), style: Style::new(), page_break: None, id: None, page_reference: None, toc_item: None }
    }

    pub fn style(mut self, name: impl Into<String>) -> Self {
        self.style_names.push(name.into());
        self
    }

    pub fn inline_style(mut self, style: Style) -> Self {
        self.style = style;
        self
    }

    pub fn page_break(mut self, mode: PageBreakMode) -> Self {
        self.page_break = Some(mode);
        self
    }

    pub fn id(mut self, id: impl Into<String>) -> Self {
        self.id = Some(id.into());
        self
    }

    pub fn page_reference(mut self, id: impl Into<String>) -> Self {
        self.page_reference = Some(id.into());
        self
    }

    pub fn toc_item(mut self, toc_id: impl Into<String>) -> Self {
        self.toc_item = Some(toc_id.into());
        self
    }
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct InlineFragment {
    pub text: String,
    pub style: Style,
}

impl InlineFragment {
    pub fn new(text: impl Into<String>) -> Self {
        Self { text: text.into(), style: Style::new() }
    }

    pub fn style(mut self, style: Style) -> Self {
        self.style = style;
        self
    }
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum ImageSource {
    Path(String),
    Bytes(Vec<u8>),
    Base64(String),
}

#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub struct Fit {
    pub width: f32,
    pub height: f32,
}

#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub struct ImageCover {
    pub width: f32,
    pub height: f32,
    pub align: Alignment,
    pub valign: VerticalAlignment,
}

impl ImageCover {
    pub fn new(width: f32, height: f32) -> Self {
        Self { width, height, align: Alignment::Center, valign: VerticalAlignment::Middle }
    }

    pub fn align(mut self, align: Alignment) -> Self {
        self.align = align;
        self
    }

    pub fn valign(mut self, valign: VerticalAlignment) -> Self {
        self.valign = valign;
        self
    }
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ImageNode {
    pub source: ImageSource,
    pub width: Option<f32>,
    pub height: Option<f32>,
    pub fit: Option<Fit>,
    pub alignment: Alignment,
    pub max_width: Option<f32>,
    pub max_height: Option<f32>,
    pub min_width: Option<f32>,
    pub min_height: Option<f32>,
    pub cover: Option<ImageCover>,
    pub opacity: Option<f32>,
    pub link: Option<String>,
    pub link_to_page: Option<usize>,
    pub link_to_destination: Option<String>,
}

impl ImageNode {
    pub fn path(path: impl Into<String>) -> Self {
        Self { source: ImageSource::Path(path.into()), width: None, height: None, fit: None, alignment: Alignment::Left, max_width: None, max_height: None, min_width: None, min_height: None, cover: None, opacity: None, link: None, link_to_page: None, link_to_destination: None }
    }

    pub fn bytes(bytes: Vec<u8>) -> Self {
        Self { source: ImageSource::Bytes(bytes), width: None, height: None, fit: None, alignment: Alignment::Left, max_width: None, max_height: None, min_width: None, min_height: None, cover: None, opacity: None, link: None, link_to_page: None, link_to_destination: None }
    }

    pub fn base64(data: impl Into<String>) -> Self {
        Self { source: ImageSource::Base64(data.into()), width: None, height: None, fit: None, alignment: Alignment::Left, max_width: None, max_height: None, min_width: None, min_height: None, cover: None, opacity: None, link: None, link_to_page: None, link_to_destination: None }
    }

    pub fn width(mut self, value: f32) -> Self {
        self.width = Some(value);
        self
    }

    pub fn height(mut self, value: f32) -> Self {
        self.height = Some(value);
        self
    }

    pub fn fit(mut self, width: f32, height: f32) -> Self {
        self.fit = Some(Fit { width, height });
        self
    }

    pub fn alignment(mut self, alignment: Alignment) -> Self {
        self.alignment = alignment;
        self
    }

    pub fn max_width(mut self, value: f32) -> Self {
        self.max_width = Some(value);
        self
    }

    pub fn max_height(mut self, value: f32) -> Self {
        self.max_height = Some(value);
        self
    }

    pub fn min_width(mut self, value: f32) -> Self {
        self.min_width = Some(value);
        self
    }

    pub fn min_height(mut self, value: f32) -> Self {
        self.min_height = Some(value);
        self
    }

    pub fn cover(mut self, cover: ImageCover) -> Self {
        self.cover = Some(cover);
        self
    }

    pub fn opacity(mut self, value: f32) -> Self {
        self.opacity = Some(value);
        self
    }

    pub fn link(mut self, url: impl Into<String>) -> Self {
        self.link = Some(url.into());
        self
    }

    pub fn link_to_page(mut self, page: usize) -> Self {
        self.link_to_page = Some(page);
        self
    }

    pub fn link_to_destination(mut self, dest: impl Into<String>) -> Self {
        self.link_to_destination = Some(dest.into());
        self
    }
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct QrNode {
    pub text: String,
    pub fit: Option<f32>,
    pub alignment: Alignment,
    pub foreground: Option<String>,
    pub background: Option<String>,
    pub ecc_level: Option<String>,
    pub padding: Option<f32>,
    pub mode: Option<String>,
    pub version: Option<u32>,
    pub mask: Option<u8>,
}

impl QrNode {
    pub fn new(text: impl Into<String>) -> Self {
        Self { text: text.into(), fit: None, alignment: Alignment::Left, foreground: None, background: None, ecc_level: None, padding: None, mode: None, version: None, mask: None }
    }

    pub fn fit(mut self, value: f32) -> Self {
        self.fit = Some(value);
        self
    }

    pub fn alignment(mut self, alignment: Alignment) -> Self {
        self.alignment = alignment;
        self
    }

    pub fn foreground(mut self, value: impl Into<String>) -> Self {
        self.foreground = Some(value.into());
        self
    }

    pub fn background(mut self, value: impl Into<String>) -> Self {
        self.background = Some(value.into());
        self
    }

    pub fn ecc_level(mut self, value: impl Into<String>) -> Self {
        self.ecc_level = Some(value.into());
        self
    }

    pub fn padding(mut self, value: f32) -> Self {
        self.padding = Some(value);
        self
    }

    pub fn mode(mut self, value: impl Into<String>) -> Self {
        self.mode = Some(value.into());
        self
    }

    pub fn version(mut self, value: u32) -> Self {
        self.version = Some(value);
        self
    }

    pub fn mask(mut self, value: u8) -> Self {
        self.mask = Some(value);
        self
    }
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SvgNode {
    pub markup: String,
    pub width: Option<f32>,
    pub height: Option<f32>,
    pub fit: Option<Fit>,
    pub alignment: Alignment,
    pub link: Option<String>,
    pub link_to_page: Option<usize>,
    pub link_to_destination: Option<String>,
}

impl SvgNode {
    pub fn new(markup: impl Into<String>) -> Self {
        Self { markup: markup.into(), width: None, height: None, fit: None, alignment: Alignment::Left, link: None, link_to_page: None, link_to_destination: None }
    }

    pub fn width(mut self, value: f32) -> Self {
        self.width = Some(value);
        self
    }

    pub fn height(mut self, value: f32) -> Self {
        self.height = Some(value);
        self
    }

    pub fn fit(mut self, width: f32, height: f32) -> Self {
        self.fit = Some(Fit { width, height });
        self
    }

    pub fn alignment(mut self, alignment: Alignment) -> Self {
        self.alignment = alignment;
        self
    }

    pub fn link(mut self, url: impl Into<String>) -> Self {
        self.link = Some(url.into());
        self
    }

    pub fn link_to_page(mut self, page: usize) -> Self {
        self.link_to_page = Some(page);
        self
    }

    pub fn link_to_destination(mut self, dest: impl Into<String>) -> Self {
        self.link_to_destination = Some(dest.into());
        self
    }
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "type")]
pub enum TableCellContent {
    Text(Vec<InlineFragment>),
    Node(DocumentNode),
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct TableCell {
    pub content: TableCellContent,
    pub style: Style,
    pub padding: f32,
    pub col_span: usize,
    pub row_span: usize,
    pub vertical_alignment: Option<VerticalAlignment>,
    pub border: Option<Vec<bool>>,
    pub border_color: Option<Vec<String>>,
    pub fill_color: Option<String>,
    pub fill_opacity: Option<f32>,
}

impl TableCell {
    pub fn text(text: impl Into<String>) -> Self {
        Self { content: TableCellContent::Text(vec![InlineFragment::new(text)]), style: Style::new(), padding: 4.0, col_span: 1, row_span: 1, vertical_alignment: None, border: None, border_color: None, fill_color: None, fill_opacity: None }
    }

    pub fn with_inlines(inlines: Vec<InlineFragment>) -> Self {
        Self { content: TableCellContent::Text(inlines), style: Style::new(), padding: 4.0, col_span: 1, row_span: 1, vertical_alignment: None, border: None, border_color: None, fill_color: None, fill_opacity: None }
    }

    pub fn node(node: DocumentNode) -> Self {
        Self { content: TableCellContent::Node(node), style: Style::new(), padding: 4.0, col_span: 1, row_span: 1, vertical_alignment: None, border: None, border_color: None, fill_color: None, fill_opacity: None }
    }

    pub fn style(mut self, style: Style) -> Self {
        self.style = style;
        self
    }

    pub fn padding(mut self, padding: f32) -> Self {
        self.padding = padding;
        self
    }

    pub fn col_span(mut self, col_span: usize) -> Self {
        self.col_span = col_span.max(1);
        self
    }

    pub fn row_span(mut self, row_span: usize) -> Self {
        self.row_span = row_span.max(1);
        self
    }

    pub fn vertical_alignment(mut self, value: VerticalAlignment) -> Self {
        self.vertical_alignment = Some(value);
        self
    }

    pub fn border(mut self, sides: Vec<bool>) -> Self {
        self.border = Some(sides);
        self
    }

    pub fn border_color(mut self, colors: Vec<impl Into<String>>) -> Self {
        self.border_color = Some(colors.into_iter().map(|c| c.into()).collect());
        self
    }

    pub fn fill_color(mut self, color: impl Into<String>) -> Self {
        self.fill_color = Some(color.into());
        self
    }

    pub fn fill_opacity(mut self, opacity: f32) -> Self {
        self.fill_opacity = Some(opacity);
        self
    }
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct TableNode {
    pub body: Vec<Vec<TableCell>>,
    pub widths: Vec<TableWidth>,
    pub heights: Option<Vec<f32>>,
    pub style: Style,
    pub style_names: Vec<String>,
    pub header_rows: Option<usize>,
    pub dont_break_rows: bool,
    pub page_break: Option<PageBreakMode>,
    pub keep_with_header_rows: Option<usize>,
    pub table_style: Option<TableStyle>,
    pub layout_config: Option<TableLayoutConfig>,
    pub h_line_when_broken: Option<bool>,
}

impl TableNode {
    pub fn new(body: Vec<Vec<TableCell>>) -> Self {
        Self {
            body,
            widths: Vec::new(),
            heights: None,
            style: Style::new(),
            style_names: Vec::new(),
            header_rows: None,
            dont_break_rows: false,
            page_break: None,
            keep_with_header_rows: None,
            table_style: None,
            layout_config: None,
            h_line_when_broken: None,
        }
    }

    pub fn widths(mut self, widths: Vec<TableWidth>) -> Self {
        self.widths = widths;
        self
    }

    pub fn heights(mut self, heights: Vec<f32>) -> Self {
        self.heights = Some(heights);
        self
    }

    pub fn style(mut self, name: impl Into<String>) -> Self {
        self.style_names.push(name.into());
        self
    }

    pub fn inline_style(mut self, style: Style) -> Self {
        self.style = style;
        self
    }

    pub fn header_rows(mut self, count: usize) -> Self {
        self.header_rows = Some(count);
        self
    }

    pub fn dont_break_rows(mut self) -> Self {
        self.dont_break_rows = true;
        self
    }

    pub fn page_break(mut self, mode: PageBreakMode) -> Self {
        self.page_break = Some(mode);
        self
    }

    pub fn keep_with_header_rows(mut self, count: usize) -> Self {
        self.keep_with_header_rows = Some(count);
        self
    }

    pub fn table_style(mut self, style: TableStyle) -> Self {
        self.table_style = Some(style);
        self
    }

    pub fn layout_config(mut self, config: TableLayoutConfig) -> Self {
        self.layout_config = Some(config);
        self
    }

    pub fn h_line_when_broken(mut self, value: bool) -> Self {
        self.h_line_when_broken = Some(value);
        self
    }
}
