use bevy::prelude::*;
use serde::{Deserialize, Serialize};
use super::anim::Anim;
use super::board_cell::BoardCell;
use super::colors::Colors;
use super::cursor::Cursor;
use super::direction::Direction;
use super::inner_board::InnerBoard;
use super::level::Level;
use super::shapes::Shapes;
#[derive(Debug, Clone, Default, Component, Serialize, Deserialize)]
pub struct Board {
inner: Vec<InnerBoard>,
#[serde(default, skip)]
anims: Vec<Anim>,
}
impl Board {
pub fn done(&self) -> Result<bool, std::io::Error> {
Ok(self.current()?.done())
}
pub fn reset(&mut self) -> bool {
if self.inner.len() <= 1 {
return false;
}
self.inner.truncate(1);
true
}
pub fn rotate(&mut self, direction: Direction) -> Result<(), std::io::Error> {
self.current_mut()?.rotate(direction);
Ok(())
}
pub fn update(
&self,
transform: &mut Single<&mut Transform, With<Cursor>>,
) -> bevy::ecs::error::Result<()> {
self.current()?.update(transform);
Ok(())
}
pub fn highlight(&self) -> Result<(i32, i32), std::io::Error> {
let (ix, iy) = self.current()?.highlight();
Ok((ix as i32, iy as i32))
}
pub fn set_highlight(&mut self, xi: i32, yi: i32) {
let xi = ((9 + xi) % 9) as usize;
let yi = ((9 + yi) % 9) as usize;
for board in &mut self.inner {
board.set_highlight(xi, yi);
}
}
pub fn size(&self) -> Result<Vec2, std::io::Error> {
Ok(self.current()?.size())
}
pub fn set_value(&mut self, value: u8) -> Result<bool, std::io::Error> {
let mut anims: Vec<Anim> = Vec::new();
let (x, y) = self.highlight()?;
let (x, y) = (x as usize, y as usize);
let current = self.current()?;
if let Some(board) = current.set_value(x, y, value, &mut anims) {
if value == 0 {
let value = current.cell(x, y).value();
if value != 0 {
anims.push(Anim::Unset { x, y, value });
}
} else {
anims.push(Anim::Set { x, y, value });
}
self.inner.push(board);
self.anims.append(&mut anims);
Ok(true)
} else {
Ok(false)
}
}
pub fn toggle_candidate(&mut self, candidate: u8) -> Result<bool, std::io::Error> {
let mut anims: Vec<Anim> = Vec::new();
let (x, y) = self.highlight()?;
let (x, y) = (x as usize, y as usize);
if let Some(board) = self.current()?.toggle_candidate(x, y, candidate, &mut anims) {
self.inner.push(board);
self.anims.append(&mut anims);
Ok(true)
} else {
Ok(false)
}
}
pub fn undo(&mut self) -> bool {
if self.inner.len() < 2 {
false
} else {
self.inner.pop();
true
}
}
pub fn render(
&mut self,
x: f32,
y: f32,
commands: &mut Commands,
cell_query: &Query<Entity, With<BoardCell>>,
cursor_query: &Query<Entity, With<Cursor>>,
shapes: &Res<Shapes>,
colors: &Res<Colors>,
) -> Result<(), std::io::Error> {
self.current()?.render(
x, y,
commands,
cell_query,
cursor_query,
shapes,
colors,
&self.anims,
);
self.anims.clear();
Ok(())
}
fn current(&self) -> Result<&InnerBoard, std::io::Error> {
let size = self.inner.len();
if size == 0 {
Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
"No board available",
))
} else {
Ok(&self.inner[size - 1])
}
}
fn current_mut(&mut self) -> Result<&mut InnerBoard, std::io::Error> {
let size = self.inner.len();
if size == 0 {
Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
"No board available",
))
} else {
Ok(&mut self.inner[size - 1])
}
}
fn add(&mut self, board: InnerBoard) {
self.inner.push(board);
}
}
impl TryFrom<Level> for Board {
type Error = std::io::Error;
fn try_from(level: Level) -> Result<Self, Self::Error> {
let mut board = Board::default();
board.add(InnerBoard::try_from(level)?);
Ok(board)
}
}