use std::str::FromStr;

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum HtmlTagKind {
    Void,
    Template,
    RawText,
    EscapableRawText,
    Foreign,
    Normal,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum HtmlTag {
    A,
    Abbr,
    Acronym,
    Address,
    Area,
    Article,
    Aside,
    Audio,
    B,
    Base,
    Bdi,
    Bdo,
    Big,
    BlockQuote,
    Body,
    BR,
    Button,
    Canvas,
    Caption,
    Center,
    Cite,
    Code,
    Col,
    ColGroup,
    Data,
    DataList,
    DD,
    Del,
    Details,
    Dfn,
    Dialog,
    Div,
    DL,
    DT,
    Em,
    Embed,
    FieldSet,
    FigCaption,
    Figure,
    Footer,
    Font,
    Form,
    H1,
    H2,
    H3,
    H4,
    H5,
    H6,
    Head,
    Header,
    HR,
    Html,
    I,
    IFrame,
    Img,
    Input,
    Ins,
    Kbd,
    Label,
    Legend,
    LI,
    Link,
    Main,
    Map,
    Mark,
    Meta,
    Meter,
    Nav,
    NoBR,
    NoScript,
    Object,
    OL,
    OptGroup,
    Option,
    Output,
    P,
    Param,
    Picture,
    Pre,
    Progress,
    Q,
    RB,
    RP,
    RT,
    RTC,
    Ruby,
    S,
    Samp,
    Script,
    Select,
    Section,
    Small,
    Source,
    Span,
    Strike,
    Strong,
    Style,
    Sub,
    Summary,
    Sup,
    Table,
    TBody,
    TD,
    Template,
    TFoot,
    TextArea,
    TH,
    THead,
    Time,
    Title,
    TR,
    Track,
    TT,
    U,
    UL,
    Var,
    Video,
    WBR,
    XMP,
}

impl HtmlTag {
    pub fn kind(self) -> HtmlTagKind {
        match self {
            HtmlTag::Area
            | HtmlTag::Base
            | HtmlTag::BR
            | HtmlTag::Col
            | HtmlTag::Embed
            | HtmlTag::HR
            | HtmlTag::Img
            | HtmlTag::Input
            | HtmlTag::Link
            | HtmlTag::Meta
            | HtmlTag::Param
            | HtmlTag::Source
            | HtmlTag::Track
            | HtmlTag::WBR => HtmlTagKind::Void,
            HtmlTag::Template => HtmlTagKind::Template,
            HtmlTag::Script | HtmlTag::Style | HtmlTag::XMP => HtmlTagKind::RawText,
            HtmlTag::TextArea | HtmlTag::Title => HtmlTagKind::EscapableRawText,
            HtmlTag::Canvas => HtmlTagKind::Foreign,
            _ => HtmlTagKind::Normal,
        }
    }

    pub fn name(self) -> &'static str {
        match self {
            HtmlTag::A => "a",
            HtmlTag::Abbr => "abbr",
            HtmlTag::Acronym => "acronym",
            HtmlTag::Address => "address",
            HtmlTag::Area => "area",
            HtmlTag::Article => "article",
            HtmlTag::Aside => "aside",
            HtmlTag::Audio => "audio",
            HtmlTag::B => "b",
            HtmlTag::Base => "base",
            HtmlTag::Bdi => "bdi",
            HtmlTag::Bdo => "bdo",
            HtmlTag::Big => "big",
            HtmlTag::BlockQuote => "blockquote",
            HtmlTag::Body => "body",
            HtmlTag::BR => "br",
            HtmlTag::Button => "button",
            HtmlTag::Canvas => "canvas",
            HtmlTag::Caption => "caption",
            HtmlTag::Center => "center",
            HtmlTag::Cite => "cite",
            HtmlTag::Code => "code",
            HtmlTag::Col => "col",
            HtmlTag::ColGroup => "colgroup",
            HtmlTag::Data => "data",
            HtmlTag::DataList => "datalist",
            HtmlTag::DD => "dd",
            HtmlTag::Del => "del",
            HtmlTag::Details => "details",
            HtmlTag::Dfn => "dfn",
            HtmlTag::Dialog => "dialog",
            HtmlTag::Div => "div",
            HtmlTag::DL => "dl",
            HtmlTag::DT => "dt",
            HtmlTag::Em => "em",
            HtmlTag::Embed => "embed",
            HtmlTag::FieldSet => "fieldset",
            HtmlTag::FigCaption => "figcaption",
            HtmlTag::Figure => "figure",
            HtmlTag::Footer => "footer",
            HtmlTag::Font => "font",
            HtmlTag::Form => "form",
            HtmlTag::H1 => "h1",
            HtmlTag::H2 => "h2",
            HtmlTag::H3 => "h3",
            HtmlTag::H4 => "h4",
            HtmlTag::H5 => "h5",
            HtmlTag::H6 => "h6",
            HtmlTag::Head => "head",
            HtmlTag::Header => "header",
            HtmlTag::HR => "hr",
            HtmlTag::Html => "html",
            HtmlTag::I => "i",
            HtmlTag::IFrame => "iframe",
            HtmlTag::Img => "img",
            HtmlTag::Input => "input",
            HtmlTag::Ins => "ins",
            HtmlTag::Kbd => "kbd",
            HtmlTag::Label => "label",
            HtmlTag::Legend => "legend",
            HtmlTag::LI => "li",
            HtmlTag::Link => "link",
            HtmlTag::Main => "main",
            HtmlTag::Map => "map",
            HtmlTag::Mark => "mark",
            HtmlTag::Meta => "meta",
            HtmlTag::Meter => "meter",
            HtmlTag::Nav => "nav",
            HtmlTag::NoBR => "nobr",
            HtmlTag::NoScript => "noscript",
            HtmlTag::Object => "object",
            HtmlTag::OL => "ol",
            HtmlTag::OptGroup => "optgroup",
            HtmlTag::Option => "option",
            HtmlTag::Output => "output",
            HtmlTag::P => "p",
            HtmlTag::Param => "param",
            HtmlTag::Picture => "picture",
            HtmlTag::Pre => "pre",
            HtmlTag::Progress => "progress",
            HtmlTag::Q => "q",
            HtmlTag::RB => "rb",
            HtmlTag::RP => "rp",
            HtmlTag::RT => "rt",
            HtmlTag::RTC => "rtc",
            HtmlTag::Ruby => "ruby",
            HtmlTag::S => "s",
            HtmlTag::Samp => "samp",
            HtmlTag::Script => "script",
            HtmlTag::Select => "select",
            HtmlTag::Section => "section",
            HtmlTag::Small => "small",
            HtmlTag::Source => "source",
            HtmlTag::Span => "span",
            HtmlTag::Strike => "strike",
            HtmlTag::Strong => "strong",
            HtmlTag::Style => "style",
            HtmlTag::Sub => "sub",
            HtmlTag::Summary => "summary",
            HtmlTag::Sup => "sup",
            HtmlTag::Table => "table",
            HtmlTag::TBody => "tbody",
            HtmlTag::TD => "td",
            HtmlTag::Template => "template",
            HtmlTag::TFoot => "tfoot",
            HtmlTag::TextArea => "textarea",
            HtmlTag::TH => "th",
            HtmlTag::THead => "thead",
            HtmlTag::Time => "time",
            HtmlTag::Title => "title",
            HtmlTag::TR => "tr",
            HtmlTag::Track => "track",
            HtmlTag::TT => "tt",
            HtmlTag::U => "u",
            HtmlTag::UL => "ul",
            HtmlTag::Var => "var",
            HtmlTag::Video => "video",
            HtmlTag::WBR => "wbr",
            HtmlTag::XMP => "xmp",
        }
    }

    pub fn heading_level(self) -> Option<u8> {
        match self {
            HtmlTag::H1 => Some(1),
            HtmlTag::H2 => Some(2),
            HtmlTag::H3 => Some(3),
            HtmlTag::H4 => Some(4),
            HtmlTag::H5 => Some(5),
            HtmlTag::H6 => Some(6),
            _ => None,
        }
    }

    pub fn heading_tag(level: u8) -> Option<Self> {
        match level {
            1 => Some(HtmlTag::H1),
            2 => Some(HtmlTag::H2),
            3 => Some(HtmlTag::H3),
            4 => Some(HtmlTag::H4),
            5 => Some(HtmlTag::H5),
            6 => Some(HtmlTag::H6),
            _ => None,
        }
    }

    pub fn heading_tag_clamped(level: i32) -> Self {
        if level <= 1 {
            HtmlTag::H1
        } else if level >= 6 {
            HtmlTag::H6
        } else {
            Self::heading_tag(level as u8).unwrap_or(HtmlTag::H1)
        }
    }
}

impl FromStr for HtmlTag {
    type Err = ();

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s.to_ascii_lowercase().as_str() {
            "a" => Ok(HtmlTag::A),
            "abbr" => Ok(HtmlTag::Abbr),
            "acronym" => Ok(HtmlTag::Acronym),
            "address" => Ok(HtmlTag::Address),
            "area" => Ok(HtmlTag::Area),
            "article" => Ok(HtmlTag::Article),
            "aside" => Ok(HtmlTag::Aside),
            "audio" => Ok(HtmlTag::Audio),
            "b" => Ok(HtmlTag::B),
            "base" => Ok(HtmlTag::Base),
            "bdi" => Ok(HtmlTag::Bdi),
            "bdo" => Ok(HtmlTag::Bdo),
            "big" => Ok(HtmlTag::Big),
            "blockquote" => Ok(HtmlTag::BlockQuote),
            "body" => Ok(HtmlTag::Body),
            "br" => Ok(HtmlTag::BR),
            "button" => Ok(HtmlTag::Button),
            "canvas" => Ok(HtmlTag::Canvas),
            "caption" => Ok(HtmlTag::Caption),
            "center" => Ok(HtmlTag::Center),
            "cite" => Ok(HtmlTag::Cite),
            "code" => Ok(HtmlTag::Code),
            "col" => Ok(HtmlTag::Col),
            "colgroup" => Ok(HtmlTag::ColGroup),
            "data" => Ok(HtmlTag::Data),
            "datalist" => Ok(HtmlTag::DataList),
            "dd" => Ok(HtmlTag::DD),
            "del" => Ok(HtmlTag::Del),
            "details" => Ok(HtmlTag::Details),
            "dfn" => Ok(HtmlTag::Dfn),
            "dialog" => Ok(HtmlTag::Dialog),
            "div" => Ok(HtmlTag::Div),
            "dl" => Ok(HtmlTag::DL),
            "dt" => Ok(HtmlTag::DT),
            "em" => Ok(HtmlTag::Em),
            "embed" => Ok(HtmlTag::Embed),
            "fieldset" => Ok(HtmlTag::FieldSet),
            "figcaption" => Ok(HtmlTag::FigCaption),
            "figure" => Ok(HtmlTag::Figure),
            "footer" => Ok(HtmlTag::Footer),
            "font" => Ok(HtmlTag::Font),
            "form" => Ok(HtmlTag::Form),
            "h1" => Ok(HtmlTag::H1),
            "h2" => Ok(HtmlTag::H2),
            "h3" => Ok(HtmlTag::H3),
            "h4" => Ok(HtmlTag::H4),
            "h5" => Ok(HtmlTag::H5),
            "h6" => Ok(HtmlTag::H6),
            "head" => Ok(HtmlTag::Head),
            "header" => Ok(HtmlTag::Header),
            "hr" => Ok(HtmlTag::HR),
            "html" => Ok(HtmlTag::Html),
            "i" => Ok(HtmlTag::I),
            "iframe" => Ok(HtmlTag::IFrame),
            "img" => Ok(HtmlTag::Img),
            "input" => Ok(HtmlTag::Input),
            "ins" => Ok(HtmlTag::Ins),
            "kbd" => Ok(HtmlTag::Kbd),
            "label" => Ok(HtmlTag::Label),
            "legend" => Ok(HtmlTag::Legend),
            "li" => Ok(HtmlTag::LI),
            "link" => Ok(HtmlTag::Link),
            "main" => Ok(HtmlTag::Main),
            "map" => Ok(HtmlTag::Map),
            "mark" => Ok(HtmlTag::Mark),
            "meta" => Ok(HtmlTag::Meta),
            "meter" => Ok(HtmlTag::Meter),
            "nav" => Ok(HtmlTag::Nav),
            "nobr" => Ok(HtmlTag::NoBR),
            "noscript" => Ok(HtmlTag::NoScript),
            "object" => Ok(HtmlTag::Object),
            "ol" => Ok(HtmlTag::OL),
            "optgroup" => Ok(HtmlTag::OptGroup),
            "option" => Ok(HtmlTag::Option),
            "output" => Ok(HtmlTag::Output),
            "p" => Ok(HtmlTag::P),
            "param" => Ok(HtmlTag::Param),
            "picture" => Ok(HtmlTag::Picture),
            "pre" => Ok(HtmlTag::Pre),
            "progress" => Ok(HtmlTag::Progress),
            "q" => Ok(HtmlTag::Q),
            "rb" => Ok(HtmlTag::RB),
            "rp" => Ok(HtmlTag::RP),
            "rt" => Ok(HtmlTag::RT),
            "rtc" => Ok(HtmlTag::RTC),
            "ruby" => Ok(HtmlTag::Ruby),
            "s" => Ok(HtmlTag::S),
            "samp" => Ok(HtmlTag::Samp),
            "script" => Ok(HtmlTag::Script),
            "select" => Ok(HtmlTag::Select),
            "section" => Ok(HtmlTag::Section),
            "small" => Ok(HtmlTag::Small),
            "source" => Ok(HtmlTag::Source),
            "span" => Ok(HtmlTag::Span),
            "strike" => Ok(HtmlTag::Strike),
            "strong" => Ok(HtmlTag::Strong),
            "style" => Ok(HtmlTag::Style),
            "sub" => Ok(HtmlTag::Sub),
            "summary" => Ok(HtmlTag::Summary),
            "sup" => Ok(HtmlTag::Sup),
            "table" => Ok(HtmlTag::Table),
            "tbody" => Ok(HtmlTag::TBody),
            "td" => Ok(HtmlTag::TD),
            "template" => Ok(HtmlTag::Template),
            "tfoot" => Ok(HtmlTag::TFoot),
            "textarea" => Ok(HtmlTag::TextArea),
            "th" => Ok(HtmlTag::TH),
            "thead" => Ok(HtmlTag::THead),
            "time" => Ok(HtmlTag::Time),
            "title" => Ok(HtmlTag::Title),
            "tr" => Ok(HtmlTag::TR),
            "track" => Ok(HtmlTag::Track),
            "tt" => Ok(HtmlTag::TT),
            "u" => Ok(HtmlTag::U),
            "ul" => Ok(HtmlTag::UL),
            "var" => Ok(HtmlTag::Var),
            "video" => Ok(HtmlTag::Video),
            "wbr" => Ok(HtmlTag::WBR),
            "xmp" => Ok(HtmlTag::XMP),
            _ => Err(()),
        }
    }
}

pub fn html_tag_kind(tag: HtmlTag) -> HtmlTagKind {
    tag.kind()
}

pub fn html_tag_name(tag: HtmlTag) -> &'static str {
    tag.name()
}

pub fn heading_level(tag: HtmlTag) -> Option<u8> {
    tag.heading_level()
}

pub fn heading_tag(level: u8) -> Option<HtmlTag> {
    HtmlTag::heading_tag(level)
}

pub fn heading_tag_clamped(level: i32) -> HtmlTag {
    HtmlTag::heading_tag_clamped(level)
}
