use quick_xml::{events::attributes::Attribute, ElementWriter, Writer};
use std::convert::Into;

use super::CommonAttrs;

pub mod blend;
pub mod color_matrix;
pub mod component_transfer;
pub mod composite;
pub mod convolve_matrix;
pub mod diffuse_lighting;
pub mod displacement_map;
pub mod flood;
pub mod gaussian_blur;
pub mod image;
pub mod merge;
pub mod morphology;
pub mod offset;
pub mod specular_lighting;
pub mod tile;
pub mod turbulence;

pub trait WriteElement {
    fn attrs(&self) -> Vec<Attribute>;
    fn tag_name(&self) -> &'static str;
    fn element_writer<'writer, 'result>(
        &self,
        writer: &'writer mut Writer<&'result mut Vec<u8>>,
        common: CommonAttrs,
        inputs: Vec<String>,
        output: Option<String>,
    ) -> quick_xml::Result<&'writer mut quick_xml::Writer<&'result mut Vec<u8>>> {
        let attrs: 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(Into::<Vec<Attribute<'_>>>::into(common))
            .with_attributes(self.attrs())
            .with_attributes(attrs.iter().map(|(k, v)| (&k[..], &v[..])));
        if let Some(output) = output {
            el_writer = el_writer.with_attribute(("result", output.as_str()));
        }

        el_writer.write_empty()
    }
}

/// svg filter effects primitives
#[derive(Debug)]
pub enum FePrimitive {
    Blend(blend::Blend),
    ColorMatrix(color_matrix::ColorMatrix),
    ComponentTransfer(component_transfer::ComponentTransfer),
    Composite(composite::Composite),
    ConvolveMatrix(convolve_matrix::ConvolveMatrix),
    DiffuseLighting(diffuse_lighting::DiffuseLighting),
    DisplacementMap(displacement_map::DisplacementMap),
    Flood(flood::Flood),
    GaussianBlur(gaussian_blur::GaussianBlur),
    Image(image::Image),
    Merge(merge::Merge),
    Morphology(morphology::Morphology),
    Offset(offset::Offset),
    SpecularLighting(specular_lighting::SpecularLighting),
    Tile(tile::Tile),
    Turbulence(turbulence::Turbulence),
}

impl WriteElement for FePrimitive {
    fn attrs(&self) -> std::vec::Vec<quick_xml::events::attributes::Attribute<'_>> {
        match self {
            FePrimitive::Blend(el) => el.attrs(),
            FePrimitive::ColorMatrix(el) => el.attrs(),
            FePrimitive::ComponentTransfer(el) => el.attrs(),
            FePrimitive::Composite(el) => el.attrs(),
            FePrimitive::GaussianBlur(el) => el.attrs(),
            FePrimitive::Offset(el) => el.attrs(),
            FePrimitive::Turbulence(el) => el.attrs(),
            FePrimitive::DisplacementMap(el) => el.attrs(),
            FePrimitive::Flood(el) => el.attrs(),
            FePrimitive::Morphology(el) => el.attrs(),
            FePrimitive::ConvolveMatrix(_) => todo!(),
            FePrimitive::DiffuseLighting(_) => todo!(),
            FePrimitive::Image(_) => todo!(),
            FePrimitive::Merge(_) => todo!(),
            FePrimitive::SpecularLighting(_) => todo!(),
            FePrimitive::Tile(_) => todo!(),
        }
    }

    fn tag_name(&self) -> &'static str {
        match self {
            FePrimitive::Blend(el) => el.tag_name(),
            FePrimitive::ColorMatrix(el) => el.tag_name(),
            FePrimitive::ComponentTransfer(el) => el.tag_name(),
            FePrimitive::Composite(el) => el.tag_name(),
            FePrimitive::GaussianBlur(el) => el.tag_name(),
            FePrimitive::Offset(el) => el.tag_name(),
            FePrimitive::Turbulence(el) => el.tag_name(),
            FePrimitive::DisplacementMap(el) => el.tag_name(),
            FePrimitive::Flood(el) => el.tag_name(),
            FePrimitive::Morphology(el) => el.tag_name(),
            FePrimitive::ConvolveMatrix(_) => todo!(),
            FePrimitive::DiffuseLighting(_) => todo!(),
            FePrimitive::Image(_) => todo!(),
            FePrimitive::Merge(_) => todo!(),
            FePrimitive::SpecularLighting(_) => todo!(),
            FePrimitive::Tile(_) => todo!(),
        }
    }

    fn element_writer<'writer, 'result>(
        &self,
        writer: &'writer mut Writer<&'result mut Vec<u8>>,
        common: CommonAttrs,
        inputs: Vec<String>,
        output: Option<String>,
    ) -> quick_xml::Result<&'writer mut quick_xml::Writer<&'result mut Vec<u8>>> {
        match self {
            FePrimitive::Blend(el) => el.element_writer(writer, common, inputs, output),
            FePrimitive::ColorMatrix(el) => el.element_writer(writer, common, inputs, output),
            FePrimitive::ComponentTransfer(el) => el.element_writer(writer, common, inputs, output),
            FePrimitive::Composite(el) => el.element_writer(writer, common, inputs, output),
            FePrimitive::Turbulence(el) => el.element_writer(writer, common, inputs, output),
            FePrimitive::GaussianBlur(el) => el.element_writer(writer, common, inputs, output),
            FePrimitive::Offset(el) => el.element_writer(writer, common, inputs, output),
            FePrimitive::DisplacementMap(el) => el.element_writer(writer, common, inputs, output),
            FePrimitive::Flood(el) => el.element_writer(writer, common, inputs, output),
            FePrimitive::Morphology(el) => el.element_writer(writer, common, inputs, output),
            FePrimitive::ConvolveMatrix(_) => todo!(),
            FePrimitive::DiffuseLighting(_) => todo!(),
            FePrimitive::Image(_) => todo!(),
            FePrimitive::Merge(_) => todo!(),
            FePrimitive::SpecularLighting(_) => todo!(),
            FePrimitive::Tile(_) => todo!(),
        }
    }
}
