1
// This file is part of hnefatafl-copenhagen.
2
//
3
// hnefatafl-copenhagen is free software: you can redistribute it and/or modify
4
// it under the terms of the GNU Affero General Public License as published by
5
// the Free Software Foundation, either version 3 of the License, or
6
// (at your option) any later version.
7
//
8
// hnefatafl-copenhagen is distributed in the hope that it will be useful,
9
// but WITHOUT ANY WARRANTY; without even the implied warranty of
10
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
// GNU Affero General Public License for more details.
12
//
13
// You should have received a copy of the GNU Affero General Public License
14
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
15

            
16
use std::fmt;
17

            
18
use anyhow::Context;
19
use serde::{Deserialize, Serialize};
20

            
21
pub const DAY: i64 = 24 * 60 * 60 * 1_000;
22
pub const HOUR: i64 = 60 * 60 * 1_000;
23
pub const MINUTE: i64 = 60 * 1_000;
24
pub const SECOND: i64 = 1_000;
25

            
26
#[derive(Clone, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
27
pub struct Time {
28
    pub add_seconds: i64,
29
    pub milliseconds_left: i64,
30
}
31

            
32
impl Time {
33
    #[must_use]
34
    pub fn time_left(&self) -> String {
35
        time_left(self.milliseconds_left)
36
    }
37
}
38

            
39
impl Default for Time {
40
123348
    fn default() -> Self {
41
123348
        Self {
42
123348
            add_seconds: 10,
43
123348
            milliseconds_left: 15 * MINUTE,
44
123348
        }
45
123348
    }
46
}
47

            
48
impl fmt::Display for Time {
49
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50
        let days = self.milliseconds_left / DAY;
51
        let hours = (self.milliseconds_left % DAY) / HOUR;
52
        let minutes = (self.milliseconds_left % HOUR) / MINUTE;
53
        let seconds = (self.milliseconds_left % MINUTE) / SECOND;
54

            
55
        let add_hours = self.add_seconds / (60 * 60);
56
        let add_minutes = (self.add_seconds % (60 * 60)) / 60;
57
        let add_seconds = self.add_seconds % 60;
58

            
59
        if days == 0 {
60
            write!(
61
                f,
62
                "{hours:02}:{minutes:02}:{seconds:02} | {add_hours:02}:{add_minutes:02}:{add_seconds:02}"
63
            )
64
        } else {
65
            write!(
66
                f,
67
                "{days} {hours:02}:{minutes:02}:{seconds:02} | {add_hours:02}:{add_minutes:02}:{add_seconds:02}"
68
            )
69
        }
70
    }
71
}
72

            
73
#[derive(Clone, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
74
pub enum TimeSettings {
75
    Timed(Time),
76
    UnTimed,
77
}
78

            
79
impl TimeSettings {
80
    #[must_use]
81
    pub fn time_left(&self) -> String {
82
        match self {
83
            Self::Timed(time) => time.time_left(),
84
            Self::UnTimed => "-".to_string(),
85
        }
86
    }
87
}
88

            
89
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
90
pub struct TimeLeft {
91
    pub milliseconds_left: i64,
92
}
93

            
94
impl fmt::Display for TimeLeft {
95
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
96
        write!(f, "{}", time_left(self.milliseconds_left))
97
    }
98
}
99

            
100
impl From<Time> for TimeLeft {
101
    fn from(time: Time) -> Self {
102
        TimeLeft {
103
            milliseconds_left: time.milliseconds_left,
104
        }
105
    }
106
}
107

            
108
impl TryFrom<TimeSettings> for TimeLeft {
109
    type Error = anyhow::Error;
110

            
111
61666
    fn try_from(time: TimeSettings) -> Result<TimeLeft, anyhow::Error> {
112
61666
        match time {
113
61666
            TimeSettings::Timed(time) => Ok(TimeLeft {
114
61666
                milliseconds_left: time.milliseconds_left,
115
61666
            }),
116
            TimeSettings::UnTimed => Err(anyhow::Error::msg("the time settings are un-timed")),
117
        }
118
61666
    }
119
}
120

            
121
impl Default for TimeSettings {
122
123348
    fn default() -> Self {
123
123348
        Self::Timed(Time {
124
123348
            ..Default::default()
125
123348
        })
126
123348
    }
127
}
128

            
129
impl fmt::Debug for TimeSettings {
130
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
131
        match self {
132
            Self::Timed(time) => {
133
                write!(f, "fischer {} {}", time.milliseconds_left, time.add_seconds)
134
            }
135
            Self::UnTimed => write!(f, "un-timed _ _"),
136
        }
137
    }
138
}
139

            
140
impl fmt::Display for TimeSettings {
141
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
142
        match self {
143
            Self::Timed(time) => write!(f, "{time}"),
144
            Self::UnTimed => write!(f, "∞"),
145
        }
146
    }
147
}
148

            
149
impl From<TimeSettings> for bool {
150
    fn from(time_settings: TimeSettings) -> Self {
151
        match time_settings {
152
            TimeSettings::Timed(_) => true,
153
            TimeSettings::UnTimed => false,
154
        }
155
    }
156
}
157

            
158
impl TryFrom<Vec<&str>> for TimeSettings {
159
    type Error = anyhow::Error;
160

            
161
    fn try_from(args: Vec<&str>) -> anyhow::Result<Self> {
162
        let err_msg = "expected: 'time_settings un-timed' or 'time_settings fischer MILLISECONDS ADD_SECONDS'";
163

            
164
        if Some("un-timed").as_ref() == args.get(1) {
165
            return Ok(Self::UnTimed);
166
        }
167

            
168
        if args.len() < 4 {
169
            return Err(anyhow::Error::msg(err_msg));
170
        }
171

            
172
        if "fischer" == args[1] {
173
            let arg_2 = args[2]
174
                .parse::<i64>()
175
                .context("time_settings: arg 2 is not an integer")?;
176

            
177
            let arg_3 = args[3]
178
                .parse::<i64>()
179
                .context("time_settings: arg 3 is not an integer")?;
180

            
181
            Ok(Self::Timed(Time {
182
                add_seconds: arg_3,
183
                milliseconds_left: arg_2,
184
            }))
185
        } else {
186
            Err(anyhow::Error::msg(err_msg))
187
        }
188
    }
189
}
190

            
191
fn time_left(milliseconds_left: i64) -> String {
192
    let days = milliseconds_left / DAY;
193
    let hours = (milliseconds_left % DAY) / HOUR;
194
    let minutes = (milliseconds_left % HOUR) / MINUTE;
195
    let seconds = (milliseconds_left % MINUTE) / SECOND;
196

            
197
    if days == 0 {
198
        if hours == 0 {
199
            if minutes == 0 {
200
                format!("{seconds:02}")
201
            } else {
202
                format!("{minutes:02}:{seconds:02}")
203
            }
204
        } else {
205
            format!("{hours:02}:{minutes:02}:{seconds:02}")
206
        }
207
    } else {
208
        format!("{days} {hours:02}:{minutes:02}:{seconds:02}")
209
    }
210
}
211

            
212
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
213
pub enum TimeEnum {
214
    Classical,
215
    Infinity,
216
    Long,
217
    #[default]
218
    Rapid,
219
    VeryLong,
220
}
221

            
222
impl From<TimeEnum> for TimeSettings {
223
    fn from(time_enum: TimeEnum) -> TimeSettings {
224
        match time_enum {
225
            TimeEnum::Classical => TimeSettings::Timed(Time {
226
                add_seconds: 20,
227
                milliseconds_left: 30 * MINUTE,
228
            }),
229
            TimeEnum::Rapid => TimeSettings::Timed(Time {
230
                add_seconds: 10,
231
                milliseconds_left: 15 * MINUTE,
232
            }),
233

            
234
            TimeEnum::Long => TimeSettings::Timed(Time {
235
                add_seconds: 60 * 60 * 6,
236
                milliseconds_left: 3 * DAY,
237
            }),
238
            TimeEnum::VeryLong => TimeSettings::Timed(Time {
239
                add_seconds: 60 * 60 * 15,
240
                milliseconds_left: 12 * 15 * HOUR,
241
            }),
242
            TimeEnum::Infinity => TimeSettings::UnTimed,
243
        }
244
    }
245
}
246

            
247
impl fmt::Display for TimeEnum {
248
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
249
        match self {
250
            Self::Classical => write!(f, "00:30:00 | 00:00:20"),
251
            Self::Infinity => write!(f, "∞"),
252
            Self::Long => write!(f, "3 00:00:00 | 6:00:00"),
253
            Self::Rapid => write!(f, "00:15:00 | 00:00:10 "),
254
            Self::VeryLong => write!(f, "7 12:00:00 | 15:00:00 "),
255
        }
256
    }
257
}