use std::mem;
use itertools::Itertools;
use super::super::IoReadError;
use crate::{Collection, GameBoard, Puzzle, SavedGame, SokobanPlus, Tessellation};
pub const AUTHOR: &str = "Author";
pub const TITLE: &str = "Title";
pub const COLLECTION: &str = "Collection";
pub const GOALORDER: &str = "goalorder";
pub const BOXORDER: &str = "boxorder";
pub const SOLVER: &str = "Solver";
pub const VARIANT: &str = "Game";
pub const DATE_CREATED: &str = "Date Created";
pub const DATE_OF_LAST_CHANGE: &str = "Date of Last Change";
pub const RAW_FILE_NOTES: &str = "::";
pub const TAG_DELIMITERS: &str = "=:";
#[derive(Debug, Clone)]
pub struct NumberedLine {
pub idx: usize,
pub ln: String,
}
#[derive(Debug, Clone)]
pub struct SavedGameData {
pub ln_idx: usize,
pub moves: String,
pub title: String,
pub solver: String,
pub notes: Vec<NumberedLine>,
}
impl SavedGameData {
pub fn new(ln_idx: usize) -> Self {
Self {
ln_idx,
moves: String::new(),
title: String::new(),
solver: String::new(),
notes: Vec::new(),
}
}
pub fn create_saved_game(
&mut self, tessellation: Tessellation,
) -> Result<SavedGame, IoReadError> {
match SavedGame::parsed(&self.moves, tessellation) {
Ok(mut saved_game) => {
mem::swap(&mut saved_game.title, &mut self.title);
mem::swap(&mut saved_game.solved_by, &mut self.solver);
saved_game.notes = self.notes.iter().map(|num_ln| &num_ln.ln).join("\n");
Ok(saved_game)
}
Err(e) => Err(IoReadError::new_saved_game_syntax_error(self.ln_idx, e)),
}
}
}
#[derive(Debug, Clone)]
pub struct PuzzleData {
pub board_ln_idx: usize,
pub sokplus_ln_idx: usize,
pub board: String,
pub tessellation: Tessellation,
pub title: String,
pub author: String,
pub boxorder: String,
pub goalorder: String,
pub notes: Vec<NumberedLine>,
pub saved_games: Vec<SavedGameData>,
}
impl PuzzleData {
pub fn new(board_ln_idx: usize) -> Self {
Self {
board_ln_idx,
sokplus_ln_idx: 0,
board: String::new(),
tessellation: Tessellation::Sokoban,
title: String::new(),
author: String::new(),
boxorder: String::new(),
goalorder: String::new(),
notes: Vec::new(),
saved_games: Vec::new(),
}
}
pub fn create_puzzle(
&mut self, legacy_sokoban_plus: bool, tessellation: Tessellation,
) -> Result<Puzzle, IoReadError> {
match GameBoard::parsed(&self.board, tessellation) {
Ok(board) => {
let mut puzzle = Puzzle {
board,
title: String::default(),
author: String::default(),
notes: String::default(),
saved_games: Vec::default(),
sokoban_plus: None,
};
mem::swap(&mut puzzle.title, &mut self.title);
mem::swap(&mut puzzle.author, &mut self.author);
puzzle.notes = self.notes.iter().map(|num_ln| &num_ln.ln).join("\n");
let result = if legacy_sokoban_plus {
SokobanPlus::parsed_legacy(&self.boxorder, &self.goalorder).map_err(|e| {
IoReadError::new_sokoban_plus_syntax_error(self.sokplus_ln_idx, e)
})?
} else {
SokobanPlus::parsed(&self.boxorder, &self.goalorder).map_err(|e| {
IoReadError::new_sokoban_plus_syntax_error(self.sokplus_ln_idx, e)
})?
};
if !result.is_empty() {
puzzle.sokoban_plus = Some(result);
}
Ok(puzzle)
}
Err(e) => Err(IoReadError::new_puzzle_syntax_error(self.board_ln_idx, e)),
}
}
}
#[derive(Debug, Clone)]
pub struct CollectionData {
pub title: String,
pub author: String,
pub created_at: String,
pub updated_at: String,
pub notes: Vec<NumberedLine>,
pub header_tessellation_hint: Option<Tessellation>,
pub was_tessellation_in_header: bool,
pub puzzles: Vec<PuzzleData>,
}
impl CollectionData {
pub fn new(tessellation_hint: Option<Tessellation>) -> Self {
Self {
title: String::new(),
author: String::new(),
created_at: String::new(),
updated_at: String::new(),
notes: Vec::new(),
header_tessellation_hint: tessellation_hint,
was_tessellation_in_header: false,
puzzles: Vec::new(),
}
}
pub fn create_collection(
&mut self, legacy_sokoban_plus: bool,
) -> Result<Collection, IoReadError> {
let mut collection = Collection {
title: mem::take(&mut self.title),
author: mem::take(&mut self.author),
created_at: mem::take(&mut self.created_at),
updated_at: mem::take(&mut self.updated_at),
email: String::new(),
url: String::new(),
notes: self.notes.iter().map(|num_ln| &num_ln.ln).join("\n"),
puzzles: Vec::new(),
};
for puzzle_data in &mut self.puzzles {
let mut puzzle =
puzzle_data.create_puzzle(legacy_sokoban_plus, puzzle_data.tessellation)?;
for saved_game_data in &mut puzzle_data.saved_games {
let saved_game = saved_game_data.create_saved_game(puzzle_data.tessellation)?;
puzzle.saved_games.push(saved_game);
}
collection.puzzles.push(puzzle);
}
Ok(collection)
}
}