1+ use crate :: Result ;
2+ use crate :: game:: { SessionSummary , ascii_digits:: get_digit_patterns} ;
3+ use crossterm:: {
4+ cursor:: MoveTo ,
5+ event:: { self , Event } ,
6+ execute,
7+ style:: { Attribute , Color , Print , ResetColor , SetAttribute , SetForegroundColor } ,
8+ terminal:: { self , ClearType } ,
9+ } ;
10+ use std:: io:: { stdout, Write } ;
11+
12+ #[ derive( Debug ) ]
13+ pub enum ExitAction {
14+ Exit ,
15+ }
16+
17+ pub struct ExitSummaryScreen ;
18+
19+ impl ExitSummaryScreen {
20+ fn create_ascii_numbers ( score : & str ) -> Vec < String > {
21+ let digit_patterns = get_digit_patterns ( ) ;
22+ let max_height = 4 ;
23+ let mut result = vec ! [ String :: new( ) ; max_height] ;
24+
25+ for ch in score. chars ( ) {
26+ if let Some ( digit) = ch. to_digit ( 10 ) {
27+ let pattern = & digit_patterns[ digit as usize ] ;
28+ for ( i, line) in pattern. iter ( ) . enumerate ( ) {
29+ result[ i] . push_str ( line) ;
30+ result[ i] . push ( ' ' ) ;
31+ }
32+ }
33+ }
34+
35+ result
36+ }
37+
38+ pub fn show ( session_summary : & SessionSummary ) -> Result < ExitAction > {
39+ let mut stdout = stdout ( ) ;
40+ execute ! ( stdout, terminal:: Clear ( ClearType :: All ) ) ?;
41+
42+ let ( terminal_width, terminal_height) = terminal:: size ( ) ?;
43+ let center_row = terminal_height / 2 ;
44+ let center_col = terminal_width / 2 ;
45+
46+ let title = "🎮 SESSION SUMMARY 🎮" ;
47+ let title_col = center_col. saturating_sub ( title. len ( ) as u16 / 2 ) ;
48+ execute ! ( stdout, MoveTo ( title_col, center_row. saturating_sub( 12 ) ) ) ?;
49+ execute ! ( stdout, SetAttribute ( Attribute :: Bold ) , SetForegroundColor ( Color :: Yellow ) ) ?;
50+ execute ! ( stdout, Print ( & title) ) ?;
51+ execute ! ( stdout, ResetColor ) ?;
52+
53+ // Show session duration
54+ let duration_text = format ! ( "Session Duration: {:.1} minutes" , session_summary. total_session_time. as_secs_f64( ) / 60.0 ) ;
55+ let duration_col = center_col. saturating_sub ( duration_text. len ( ) as u16 / 2 ) ;
56+ execute ! ( stdout, MoveTo ( duration_col, center_row. saturating_sub( 10 ) ) ) ?;
57+ execute ! ( stdout, SetForegroundColor ( Color :: Cyan ) ) ?;
58+ execute ! ( stdout, Print ( & duration_text) ) ?;
59+ execute ! ( stdout, ResetColor ) ?;
60+
61+ // Show completion status
62+ let completion_status = session_summary. get_session_completion_status ( ) ;
63+ let status_col = center_col. saturating_sub ( completion_status. len ( ) as u16 / 2 ) ;
64+ execute ! ( stdout, MoveTo ( status_col, center_row. saturating_sub( 9 ) ) ) ?;
65+ execute ! ( stdout, SetForegroundColor ( Color :: Green ) ) ?;
66+ execute ! ( stdout, Print ( & completion_status) ) ?;
67+ execute ! ( stdout, ResetColor ) ?;
68+
69+ // Show total score as large ASCII
70+ let score_label = "TOTAL SESSION SCORE" ;
71+ let score_label_col = center_col. saturating_sub ( score_label. len ( ) as u16 / 2 ) ;
72+ execute ! ( stdout, MoveTo ( score_label_col, center_row. saturating_sub( 7 ) ) ) ?;
73+ execute ! ( stdout, SetAttribute ( Attribute :: Bold ) , SetForegroundColor ( Color :: Cyan ) ) ?;
74+ execute ! ( stdout, Print ( score_label) ) ?;
75+ execute ! ( stdout, ResetColor ) ?;
76+
77+ let score_value = format ! ( "{:.0}" , session_summary. session_score) ;
78+ let ascii_numbers = Self :: create_ascii_numbers ( & score_value) ;
79+ let score_start_row = center_row. saturating_sub ( 6 ) ;
80+
81+ for ( row_index, line) in ascii_numbers. iter ( ) . enumerate ( ) {
82+ let line_col = center_col. saturating_sub ( line. len ( ) as u16 / 2 ) ;
83+ execute ! ( stdout, MoveTo ( line_col, score_start_row + row_index as u16 ) ) ?;
84+ execute ! ( stdout, SetAttribute ( Attribute :: Bold ) , SetForegroundColor ( Color :: Green ) ) ?;
85+ execute ! ( stdout, Print ( line) ) ?;
86+ execute ! ( stdout, ResetColor ) ?;
87+ }
88+
89+ // Show session statistics
90+ let stats_start_row = center_row. saturating_sub ( 1 ) ;
91+
92+ let mut stats_lines = vec ! [
93+ format!( "Overall CPM: {:.1} | WPM: {:.1} | Accuracy: {:.1}%" ,
94+ session_summary. overall_cpm, session_summary. overall_wpm, session_summary. overall_accuracy) ,
95+ format!( "Total Keystrokes: {} | Mistakes: {} | Challenges: {}/{}" ,
96+ session_summary. total_keystrokes, session_summary. total_mistakes,
97+ session_summary. total_challenges_completed, session_summary. total_challenges_attempted) ,
98+ ] ;
99+
100+ if session_summary. total_skips_used > 0 {
101+ stats_lines. push ( format ! ( "Skips Used: {}" , session_summary. total_skips_used) ) ;
102+ }
103+
104+ for ( i, line) in stats_lines. iter ( ) . enumerate ( ) {
105+ let line_col = center_col. saturating_sub ( line. len ( ) as u16 / 2 ) ;
106+ execute ! ( stdout, MoveTo ( line_col, stats_start_row + i as u16 ) ) ?;
107+ execute ! ( stdout, SetForegroundColor ( Color :: White ) ) ?;
108+ execute ! ( stdout, Print ( line) ) ?;
109+ execute ! ( stdout, ResetColor ) ?;
110+ }
111+
112+ // Show best/worst performance if we have completed challenges
113+ if session_summary. total_challenges_completed > 0 {
114+ let performance_start_row = stats_start_row + stats_lines. len ( ) as u16 + 1 ;
115+
116+ let performance_lines = vec ! [
117+ format!( "Best Stage: {:.1} WPM, {:.1}% accuracy" ,
118+ session_summary. best_stage_wpm, session_summary. best_stage_accuracy) ,
119+ format!( "Worst Stage: {:.1} WPM, {:.1}% accuracy" ,
120+ session_summary. worst_stage_wpm, session_summary. worst_stage_accuracy) ,
121+ ] ;
122+
123+ for ( i, line) in performance_lines. iter ( ) . enumerate ( ) {
124+ let line_col = center_col. saturating_sub ( line. len ( ) as u16 / 2 ) ;
125+ execute ! ( stdout, MoveTo ( line_col, performance_start_row + i as u16 ) ) ?;
126+ execute ! ( stdout, SetForegroundColor ( Color :: Grey ) ) ?;
127+ execute ! ( stdout, Print ( line) ) ?;
128+ execute ! ( stdout, ResetColor ) ?;
129+ }
130+ }
131+
132+
133+ // Show exit options
134+ let options_start = if session_summary. total_challenges_completed > 0 {
135+ stats_start_row + stats_lines. len ( ) as u16 + 4
136+ } else {
137+ stats_start_row + stats_lines. len ( ) as u16 + 2
138+ } ;
139+ // Show thanks message
140+ let thanks_message = "Thanks for playing GitType!" ;
141+ let thanks_col = center_col. saturating_sub ( thanks_message. len ( ) as u16 / 2 ) ;
142+ execute ! ( stdout, MoveTo ( thanks_col, options_start) ) ?;
143+ execute ! ( stdout, SetAttribute ( Attribute :: Bold ) , SetForegroundColor ( Color :: Green ) ) ?;
144+ execute ! ( stdout, Print ( thanks_message) ) ?;
145+ execute ! ( stdout, ResetColor ) ?;
146+
147+ let options = vec ! [
148+ "Press any key to exit" ,
149+ ] ;
150+
151+ for ( i, option) in options. iter ( ) . enumerate ( ) {
152+ let option_col = center_col. saturating_sub ( option. len ( ) as u16 / 2 ) ;
153+ execute ! ( stdout, MoveTo ( option_col, options_start + 2 + i as u16 ) ) ?;
154+ execute ! ( stdout, SetForegroundColor ( Color :: Yellow ) ) ?;
155+ execute ! ( stdout, Print ( option) ) ?;
156+ execute ! ( stdout, ResetColor ) ?;
157+ }
158+
159+ stdout. flush ( ) ?;
160+
161+ // Wait for any key press
162+ loop {
163+ if event:: poll ( std:: time:: Duration :: from_millis ( 100 ) ) ? {
164+ if let Event :: Key ( _) = event:: read ( ) ? {
165+ return Ok ( ExitAction :: Exit ) ;
166+ }
167+ }
168+ }
169+ }
170+ }
0 commit comments