use self::attribute::Attribute;
use crate::{
    hydration::Cursor,
    no_attrs,
    prelude::{AddAnyAttr, Mountable},
    renderer::{
        dom::{Element, Node},
        CastFrom, Rndr,
    },
    view::{Position, PositionState, Render, RenderHtml},
};
use std::borrow::Cow;
pub mod attribute;
pub mod class;
pub mod directive;
pub mod element;
pub mod event;
pub mod islands;
pub mod node_ref;
pub mod property;
pub mod style;
pub struct Doctype {
    value: &'static str,
}
pub fn doctype(value: &'static str) -> Doctype {
    Doctype { value }
}
impl Render for Doctype {
    type State = ();
    fn build(self) -> Self::State {}
    fn rebuild(self, _state: &mut Self::State) {}
}
no_attrs!(Doctype);
impl RenderHtml for Doctype {
    type AsyncOutput = Self;
    const MIN_LENGTH: usize = "<!DOCTYPE html>".len();
    fn dry_resolve(&mut self) {}
    async fn resolve(self) -> Self::AsyncOutput {
        self
    }
    fn to_html_with_buf(
        self,
        buf: &mut String,
        _position: &mut Position,
        _escape: bool,
        _mark_branches: bool,
    ) {
        buf.push_str("<!DOCTYPE ");
        buf.push_str(self.value);
        buf.push('>');
    }
    fn hydrate<const FROM_SERVER: bool>(
        self,
        _cursor: &Cursor,
        _position: &PositionState,
    ) -> Self::State {
    }
}
pub struct InertElement {
    html: Cow<'static, str>,
}
impl InertElement {
    pub fn new(html: impl Into<Cow<'static, str>>) -> Self {
        Self { html: html.into() }
    }
}
pub struct InertElementState(Cow<'static, str>, Element);
impl Mountable for InertElementState {
    fn unmount(&mut self) {
        self.1.unmount();
    }
    fn mount(&mut self, parent: &Element, marker: Option<&Node>) {
        self.1.mount(parent, marker)
    }
    fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {
        self.1.insert_before_this(child)
    }
}
impl Render for InertElement {
    type State = InertElementState;
    fn build(self) -> Self::State {
        let el = Rndr::create_element_from_html(&self.html);
        InertElementState(self.html, el)
    }
    fn rebuild(self, state: &mut Self::State) {
        let InertElementState(prev, el) = state;
        if &self.html != prev {
            let mut new_el = Rndr::create_element_from_html(&self.html);
            el.insert_before_this(&mut new_el);
            el.unmount();
            *el = new_el;
            *prev = self.html;
        }
    }
}
impl AddAnyAttr for InertElement {
    type Output<SomeNewAttr: Attribute> = Self;
    fn add_any_attr<NewAttr: Attribute>(
        self,
        _attr: NewAttr,
    ) -> Self::Output<NewAttr>
    where
        Self::Output<NewAttr>: RenderHtml,
    {
        panic!(
            "InertElement does not support adding attributes. It should only \
             be used as a child, and not returned at the top level."
        )
    }
}
impl RenderHtml for InertElement {
    type AsyncOutput = Self;
    const MIN_LENGTH: usize = 0;
    fn html_len(&self) -> usize {
        self.html.len()
    }
    fn dry_resolve(&mut self) {}
    async fn resolve(self) -> Self {
        self
    }
    fn to_html_with_buf(
        self,
        buf: &mut String,
        position: &mut Position,
        _escape: bool,
        _mark_branches: bool,
    ) {
        buf.push_str(&self.html);
        *position = Position::NextChild;
    }
    fn hydrate<const FROM_SERVER: bool>(
        self,
        cursor: &Cursor,
        position: &PositionState,
    ) -> Self::State {
        let curr_position = position.get();
        if curr_position == Position::FirstChild {
            cursor.child();
        } else if curr_position != Position::Current {
            cursor.sibling();
        }
        let el = crate::renderer::types::Element::cast_from(cursor.current())
            .unwrap();
        position.set(Position::NextChild);
        InertElementState(self.html, el)
    }
}