use quick_xml::{events::attributes::Attribute, Writer};

use super::WriteElement;

/// [feComponentTransfer](https://www.w3.org/TR/SVG11/filters.html#feComponentTransferElement)
#[derive(Debug)]
pub struct ComponentTransfer {
    pub func_r: TransferFn,
    pub func_g: TransferFn,
    pub func_b: TransferFn,
    pub func_a: TransferFn,
}

impl WriteElement for ComponentTransfer {
    fn attrs(&self) -> Vec<quick_xml::events::attributes::Attribute> {
        Vec::new()
    }

    fn tag_name(&self) -> &'static str {
        "feComponentTransfer"
    }

    fn element_writer<'writer, 'result>(
        &self,
        writer: &'writer mut quick_xml::Writer<&'result mut Vec<u8>>,
        common: crate::types::nodes::CommonAttrs,
        inputs: Vec<String>,
        output: Option<String>,
    ) -> quick_xml::Result<&'writer mut quick_xml::Writer<&'result mut Vec<u8>>> {
        let inputs: Vec<_> = inputs
            .into_iter()
            .enumerate()
            .map(|(i, edge)| {
                (
                    match i {
                        0 => "in".to_owned(),
                        n => format!("in{}", n + 1),
                    }
                    .into_bytes(),
                    edge.into_bytes(),
                )
            })
            .collect();
        let mut el_writer = writer
            .create_element(self.tag_name())
            .with_attributes(inputs.iter().map(|(k, v)| (&k[..], &v[..])))
            .with_attributes(Into::<Vec<Attribute<'_>>>::into(common));
        if let Some(output) = output {
            el_writer = el_writer.with_attribute(("result", output.as_str()));
        }

        el_writer.write_inner_content(|writer| {
            self.func_r.write_self(writer, "feFuncR")?;
            self.func_g.write_self(writer, "feFuncG")?;
            self.func_b.write_self(writer, "feFuncB")?;
            self.func_a.write_self(writer, "feFuncA")?;
            Ok(())
        })
    }
}

/// [transfer functions](https://www.w3.org/TR/SVG11/filters.html#transferFuncElements)
#[derive(Debug, Clone)]
pub enum TransferFn {
    Identity,
    Table {
        table_values: Vec<f32>,
    },
    Discrete {
        table_values: Vec<f32>,
    },
    Linear {
        slope: f32,
        intercept: f32,
    },
    Gamma {
        amplitude: f32,
        exponent: f32,
        offset: f32,
    },
}

impl TransferFn {
    #[allow(clippy::str_to_string, reason = "inside macro call")]
    fn write_self<'writer, 'result>(
        &self,
        target: &'writer mut Writer<&'result mut Vec<u8>>,
        name: &'static str,
    ) -> quick_xml::Result<&'writer mut Writer<&'result mut Vec<u8>>> {
        target
            .create_element(name)
            .with_attributes(match self {
                TransferFn::Identity => gen_attrs![b"type": "identity"],
                TransferFn::Table { table_values } => gen_attrs![
                b"type": "table",
                b"tableValues":  table_values
                    .iter()
                    .map(std::string::ToString::to_string)
                    .reduce(|mut acc, e| {
                        acc.push(' ');
                        acc.push_str(&e);
                        acc
                    }).expect("empty tables disallowed")
                ],
                TransferFn::Discrete { table_values } => gen_attrs![
                    b"type": "discrete",
                    b"tableValues":  table_values
                    .iter()
                    .map(std::string::ToString::to_string)
                    .reduce(|mut acc, e| {
                        acc.push(' ');
                        acc.push_str(&e);
                        acc
                    }).expect("empty tables disallowed")
                ],
                TransferFn::Linear { slope, intercept } => gen_attrs![
                    b"type": "linear",
                    b"slope": slope,
                    b"intercept": intercept
                ],
                TransferFn::Gamma {
                    amplitude,
                    exponent,
                    offset,
                } => gen_attrs![
                    b"type": "gamma",
                    b"amplitude": amplitude,
                    b"exponent": exponent,
                    b"offset": offset
                ],
            })
            .write_empty()
    }
}
