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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
//! This module helps manage ways things are printed.

use std::fmt::{Display, Formatter};
use crate::control::ControlSequence;
use crate::control::rendition::{CharacterPath, PathEffect};

pub enum JustifyMode {
    /// No justification, end of justification of preceding text.
    None,
    WordFill,
    WordSpace,
    LetterSpace,
    Hyphen,
    /// Flush to line home position margin.
    FlushHome,
    /// Centre between line home position and line limit position margins.
    Center,
    /// Flush to line limit position margin.
    FlushLimit,
    ItalianHyphen,
}

impl Display for JustifyMode {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", match self {
            JustifyMode::None => "0",
            JustifyMode::WordFill => "1",
            JustifyMode::WordSpace => "2",
            JustifyMode::LetterSpace => "3",
            JustifyMode::Hyphen => "4",
            JustifyMode::FlushHome => "5",
            JustifyMode::Center => "6",
            JustifyMode::FlushLimit => "7",
            JustifyMode::ItalianHyphen => "8",
        })
    }
}

/// # JFY - Justify
///
/// JFY is used to indicate the beginning of a string of graphic characters in the presentation component that
/// are to be justified according to the layout specified by the parameter values.
///
/// The end of the string to be justified is indicated by the next occurrence of JFY in the data stream.
pub fn justify(modes: &[JustifyMode]) -> ControlSequence {
    let str_modes: Vec<String> = modes.iter()
        .map(|mode| mode.to_string())
        .collect();

    let str_ref_modes: Vec<&str> = str_modes.iter()
        .map(AsRef::as_ref)
        .collect();

    ControlSequence::new(&str_ref_modes, " F")
}

pub enum ScrollDirection {
    /// # SD - Scroll down
    ///
    /// SD causes the data in the presentation component to be moved by n line positions if the line orientation
    /// is horizontal, or by `n` character positions if the line orientation is vertical, such that the data appear to
    /// move down.
    ///
    /// The active presentation position is not affected by this control function.
    Down,

    /// # SL - Scroll left
    ///
    /// SL causes the data in the presentation component to be moved by n character positions if the line
    /// orientation is horizontal, or by `n` line positions if the line orientation is vertical, such that the data appear
    /// to move to the left.
    ///
    /// The active presentation position is not affected by this control function.
    Left,

    /// # SR - Scroll right
    ///
    /// SR causes the data in the presentation component to be moved by n character positions if the line
    /// orientation is horizontal, or by `n` line positions if the line orientation is vertical, such that the data appear
    /// to move to the right.
    ///
    /// The active presentation position is not affected by this control function.
    Right,

    /// # SU - Scroll up
    /// 
    /// SU causes the data in the presentation component to be moved by n line positions if the line orientation
    /// is horizontal, or by `n` character positions if the line orientation is vertical, such that the data appear to
    /// move up.
    /// 
    /// The active presentation position is not affected by this control function.
    Up,
}

impl Display for ScrollDirection {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", match self {
            ScrollDirection::Down => "T",
            ScrollDirection::Left => " @",
            ScrollDirection::Right => " A",
            ScrollDirection::Up => "S",
        })
    }
}

/// Use this function to call the control functions `SD`, `SL`, `ST` and `SR`.
pub fn scroll(n: usize, scroll_direction: ScrollDirection) -> ControlSequence {
    ControlSequence::new(&[&n.to_string()], &scroll_direction.to_string())
}

pub enum EditingExtent {
    Page,
    Line,
    Field,
    QualifiedArea,
    Relevant,
}

impl Display for EditingExtent {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", match self {
            EditingExtent::Page => "0",
            EditingExtent::Line => "1",
            EditingExtent::Field => "2",
            EditingExtent::QualifiedArea => "3",
            EditingExtent::Relevant => "4",
        })
    }
}

/// # SEE - Select editing extent
///
/// SEE is used to establish the editing extent for subsequent character or line insertion or deletion. The
/// established extent remains in effect until the next occurrence of SEE in the data stream.
pub fn select_extent(editing_extent: EditingExtent) -> ControlSequence {
    ControlSequence::new(&[&editing_extent.to_string()], "Q")
}

/// # SLH - Set line home
///
/// If the DEVICE COMPONENT SELECT MODE is set to PRESENTATION, SLH is used to establish at
/// character position n in the active line (the line that contains the active presentation position) and lines of
/// subsequent text in the presentation component the position to which the active presentation position will
/// be moved by subsequent occurrences of CARRIAGE RETURN (CR), DELETE LINE (DL), INSERT
/// LINE (IL) or NEXT LINE (NEL) in the data stream. In the case of a
/// device without data component, it is also the position ahead of which no implicit movement of the active
/// presentation position shall occur.
///
/// If the DEVICE COMPONENT SELECT MODE is set to DATA, SLH is used to establish at character
/// position n in the active line (the line that contains the active data position) and lines of subsequent text
/// in the data component the position to which the active data position will be moved by subsequent
/// occurrences of CARRIAGE RETURN (CR), DELETE LINE (DL), INSERT LINE (IL) or NEXT LINE
/// (NEL) in the data stream. It is also the position ahead of which no
/// implicit movement of the active data position shall occur.
///
/// The established position is called the line home position and remains in effect until the next occurrence
/// of SLH in the data stream.
pub fn line_home(c: usize) -> ControlSequence {
    ControlSequence::new(&[&c.to_string()], " U")
}

/// # SLL - Set line limit
///
/// If the DEVICE COMPONENT SELECT MODE is set to PRESENTATION, SLL is used to establish at
/// character position n in the active line (the line that contains the active presentation position) and lines of
/// subsequent text in the presentation component the position to which the active presentation position will
/// be moved by subsequent occurrences of CARRIAGE RETURN (CR), or NEXT LINE (NEL) in the data
/// stream if the parameter value of SELECT IMPLICIT MOVEMENT DIRECTION (SIMD) is equal to 1;
/// where n equals the value of Pn. In the case of a device without data component, it is also the position
/// beyond which no implicit movement of the active presentation position shall occur.
///
/// If the DEVICE COMPONENT SELECT MODE is set to DATA, SLL is used to establish at character
/// position n in the active line (the line that contains the active data position) and lines of subsequent text
/// in the data component the position beyond which no implicit movement of the active data position shall
/// occur. It is also the position in the data component to which the active data position will be moved by
/// subsequent occurrences of CR or NEL in the data stream, if the parameter value of SELECT IMPLICIT
/// MOVEMENT DIRECTION (SIMD) is equal to 1.
///
/// The established position is called the line limit position and remains in effect until the next occurrence
/// of SLL in the data stream.
pub fn line_limit(n: usize) -> ControlSequence {
    ControlSequence::new(&[&n.to_string()], " V")
}

/// # SLS - Set line spacing
///
/// SLS is used to establish the line spacing for subsequent text. The established spacing remains in effect
/// until the next occurrence of SLS or of SELECT LINE SPACING (SVS) or of SPACING INCREMENT
/// (SPI) in the data stream.
///
/// The unit in which the parameter value is expressed is that established by the parameter value of SELECT
/// SIZE UNIT (SSU).
pub fn line_spacing(n: usize) -> ControlSequence {
    ControlSequence::new(&[&n.to_string()], " h")
}

pub enum LineOrientation {
    Horizontal,
    Vertical,
}

fn spd_ps1(line_orientation: LineOrientation, line_progression: CharacterPath, character_path: CharacterPath) -> usize {
    match line_orientation {
        LineOrientation::Horizontal => {
            match line_progression {
                CharacterPath::LeftToRight => {
                    match character_path {
                        CharacterPath::LeftToRight => 0,
                        CharacterPath::RightToLeft => 3,
                    }
                }
                CharacterPath::RightToLeft => {
                    match character_path {
                        CharacterPath::LeftToRight => 6,
                        CharacterPath::RightToLeft => 5,
                    }
                }
            }
        }
        LineOrientation::Vertical => {
            match line_progression {
                CharacterPath::LeftToRight => {
                    match character_path {
                        CharacterPath::LeftToRight => 2,
                        CharacterPath::RightToLeft => 4,
                    }
                }
                CharacterPath::RightToLeft => {
                    match character_path {
                        CharacterPath::LeftToRight => 1,
                        CharacterPath::RightToLeft => 7,
                    }
                }
            }
        }
    }
}

/// # SPD - Select presentation directions
///
/// SPD is used to select the line orientation, the line progression, and the character path in the presentation
/// component. It is also used to update the content of the presentation component and the content of the
/// data component. This takes effect immediately.
pub fn select_directions(
    line_orientation: LineOrientation,
    line_progression: CharacterPath,
    character_path: CharacterPath,
    path_effect: PathEffect,
) -> ControlSequence {
    ControlSequence::new(&[&spd_ps1(line_orientation, line_progression, character_path).to_string(), &path_effect.to_string()], " S")
}

/// # SPH - Set page home
///
/// If the DEVICE COMPONENT SELECT MODE is set to PRESENTATION, SPH is used to establish at
/// line position n in the active page (the page that contains the active presentation position) and subsequent
/// pages in the presentation component the position to which the active presentation position will be moved
/// by subsequent occurrences of FORM FEED (FF) in the data stream In
/// the case of a device without data component, it is also the position ahead of which no implicit movement
/// of the active presentation position shall occur.
///
/// If the DEVICE COMPONENT SELECT MODE is set to DATA, SPH is used to establish at line position
/// `n` in the active page (the page that contains the active data position) and subsequent pages in the data
/// component the position to which the active data position will be moved by subsequent occurrences of
/// FORM FEED (FF) in the data stream. It is also the position ahead of
/// which no implicit movement of the active presentation position shall occur.
///
/// The established position is called the page home position and remains in effect until the next occurrence
/// of SPH in the data stream.
pub fn page_home(n: usize) -> ControlSequence {
    ControlSequence::new(&[&n.to_string()], " i")
}

/// # SPL - Set page limit
///
/// If the DEVICE COMPONENT SELECT MODE is set to PRESENTATION, SPL is used to establish at
/// line position n in the active page (the page that contains the active presentation position) and pages of
/// subsequent text in the presentation component the position beyond which the active presentation position
/// can normally not be moved In the case of a device without data
/// component, it is also the position beyond which no implicit movement of the active presentation position
/// shall occur.
///
/// If the DEVICE COMPONENT SELECT MODE is set to DATA, SPL is used to establish at line position
/// n in the active page (the page that contains the active data position) and pages of subsequent text in the
/// data component the position beyond which no implicit movement of the active data position shall occur.
///
/// The established position is called the page limit position and remains in effect until the next occurrence
/// of SPL in the data stream.
pub fn page_limit(n: usize) -> ControlSequence {
    ControlSequence::new(&[&n.to_string()], " j")
}

/// # SSW - Set space width
/// 
/// SSW is used to establish for subsequent text the character escapement associated with the character
/// SPACE. The established escapement remains in effect until the next occurrence of SSW in the data
/// stream or until it is reset to the default value by a subsequent occurrence of CARRIAGE RETURN/LINE
/// FEED (CR/LF), CARRIAGE RETURN/FORM FEED (CR/FF), or of NEXT LINE (NEL) in the data stream.
///
/// `n` specifies the escapement.
///
/// The unit in which the parameter value is expressed is that established by the parameter value of SELECT
/// SIZE UNIT (SSU).
///
/// The default character escapement of SPACE is specified by the most recent occurrence of SET
/// CHARACTER SPACING (SCS) or of SELECT CHARACTER SPACING (SHS) or of SELECT
/// SPACING INCREMENT (SPI) in the data stream if the current font has constant spacing, or is specified
/// by the nominal width of the character SPACE in the current font if that font has proportional spacing.
pub fn space_width(n: usize) -> ControlSequence {
    ControlSequence::new(&[&n.to_string()], " [")
}