1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
use regex::Regex;

use crate::codex::modifier::constants::{CHAPTER_STYLE_PATTERN, CHAPTER_TAGS_PATTERN};

use super::base_modifier::BaseModifier;
use super::constants::MAX_HEADING_LEVEL;
use super::modifiers_bucket::ModifiersBucket;
use super::ModifierIdentifier;

#[derive(Debug, PartialEq, Clone)]
pub enum StandardChapterModifier {

    HeadingGeneralCompactVersion(u32),
    HeadingGeneralExtendedVersion(u32),
    MinorHeading,
    MajorHeading,
    SameHeading,

}

impl StandardChapterModifier {
    pub fn ordered() -> Vec<Self> {
        let mut heading_modifiers: Vec<Self> = vec![Self::MinorHeading, Self::MajorHeading, Self::SameHeading];

        for i in (1..=MAX_HEADING_LEVEL).rev() {
            heading_modifiers.push(Self::HeadingGeneralExtendedVersion(i));
            heading_modifiers.push(Self::HeadingGeneralCompactVersion(i));
        }

        heading_modifiers
    }

    pub fn heading_level(content: &str) -> Option<u32> {
        let heading_modifiers = Self::ordered();

        for heading_modifier in heading_modifiers {
            let regex = Regex::new(&heading_modifier.modifier_pattern()).unwrap();

            if regex.is_match(content) {
                match heading_modifier {
                    Self::HeadingGeneralExtendedVersion(level) => return Option::Some(level),
                    Self::HeadingGeneralCompactVersion(level) => return Option::Some(level),
                    _ => panic!("unexpected modifier: {:?}", heading_modifier)
                }
            }
        }

        Option::None
    }

    pub fn str_is_heading(content: &str) -> bool {
        Self::heading_level(content).is_some()
    }

    pub fn identifier(&self) -> ModifierIdentifier {
        match *self {
            Self::HeadingGeneralExtendedVersion(level) => {

                if level == 0 || level > MAX_HEADING_LEVEL {
                    panic!("{level} is an invalid heading level.")
                }

                format!(r"heading-{}-extended-version", level)
            },
            Self::HeadingGeneralCompactVersion(level) => {

                if level == 0 || level > MAX_HEADING_LEVEL {
                    panic!("{level} is an invalid heading level.")
                }

                format!(r"heading-{}-compact-version", level)
            },
            StandardChapterModifier::MinorHeading => String::from("minor-heading"),
            StandardChapterModifier::MajorHeading => String::from("major-heading"),
            StandardChapterModifier::SameHeading => String::from("same-heading"),
        }
    }
    
    pub fn modifier_pattern(&self) -> String {
        let specific_pattern = match *self {
            Self::HeadingGeneralExtendedVersion(level) => {

                if level == 0 || level > MAX_HEADING_LEVEL {
                    panic!("{level} is an invalid heading level.")
                }

                format!(r"(?m:^#{{{}}}\s+(.*))", level)
            },
            Self::HeadingGeneralCompactVersion(level) => {

                if level == 0 || level > MAX_HEADING_LEVEL {
                    panic!("{level} is an invalid heading level.")
                }

                format!(r"(?m:^#({})\s+(.*))", level)
            },
            StandardChapterModifier::MinorHeading => String::from(r"(?m:^#-\s+(.*))"),
            StandardChapterModifier::MajorHeading => String::from(r"(?m:^#\+\s+(.*))"),
            StandardChapterModifier::SameHeading => String::from(r"(?m:^#=\s+(.*))"),
        };

        format!("{}{}{}", specific_pattern, CHAPTER_TAGS_PATTERN, CHAPTER_STYLE_PATTERN)
    }

    pub fn incompatible_modifiers(&self) -> ModifiersBucket {
        ModifiersBucket::None
    }
}

impl Into<BaseModifier> for StandardChapterModifier {
    fn into(self) -> BaseModifier {
        BaseModifier::new(self.identifier(), self.modifier_pattern(), self.incompatible_modifiers())
    }
}