use itertools::structs::Product;
use itertools::Itertools;
use num_traits::FromPrimitive;
use ux::u5;
use crate::ppu::pixel_index::{ColumnInTile, PixelColumn, PixelRow, RowInTile};
#[derive(Clone, Copy, Debug)]
pub struct BackgroundTileIndex {
column: TileColumn,
row: TileRow,
}
impl BackgroundTileIndex {
pub fn iter() -> BackgroundTileIndexIterator {
BackgroundTileIndexIterator(TileRow::iter().cartesian_product(TileColumn::iter()))
}
pub fn from_tile_column_row(column: TileColumn, row: TileRow) -> BackgroundTileIndex {
BackgroundTileIndex { column, row }
}
pub fn to_usize(self) -> usize {
TileColumn::COLUMN_COUNT * self.row.to_usize() + self.column.to_usize()
}
#[inline]
pub fn tile_column(self) -> TileColumn {
self.column
}
#[inline]
pub fn tile_row(self) -> TileRow {
self.row
}
}
pub struct BackgroundTileIndexIterator(Product<TileRowIterator, TileColumnIterator>);
impl Iterator for BackgroundTileIndexIterator {
type Item = BackgroundTileIndex;
fn next(&mut self) -> Option<BackgroundTileIndex> {
self.0
.next()
.map(|(row, column)| BackgroundTileIndex { column, row })
}
}
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Debug)]
pub struct TileColumn(u8);
impl TileColumn {
pub const ZERO: TileColumn = TileColumn(0);
pub const COLUMN_COUNT: usize = 32;
const MAX: TileColumn = TileColumn(31);
pub fn iter() -> TileColumnIterator {
TileColumnIterator(0)
}
pub fn increment(&mut self) -> bool {
let will_wrap = *self == TileColumn::MAX;
if will_wrap {
self.0 = 0;
} else {
self.0 += 1;
}
will_wrap
}
pub fn to_pixel_column(self, column_in_tile: ColumnInTile) -> PixelColumn {
self.pixel_column()
.add_column_in_tile(column_in_tile)
.unwrap()
}
pub fn to_u8(self) -> u8 {
self.0
}
pub fn to_u16(self) -> u16 {
u16::from(self.0)
}
pub fn to_usize(self) -> usize {
usize::from(self.0)
}
pub fn is_max(self) -> bool {
self == TileColumn::MAX
}
pub fn try_from_u8(tile_column: u8) -> Option<TileColumn> {
if usize::from(tile_column) < TileColumn::COLUMN_COUNT {
Some(TileColumn(tile_column))
} else {
None
}
}
fn pixel_column(self) -> PixelColumn {
PixelColumn::new(8 * self.0)
}
}
impl From<u5> for TileColumn {
fn from(value: u5) -> Self {
Self(value.into())
}
}
impl From<TileColumn> for u8 {
fn from(value: TileColumn) -> Self {
value.0
}
}
impl From<TileColumn> for u16 {
fn from(value: TileColumn) -> Self {
value.0.into()
}
}
#[derive(Clone)]
pub struct TileColumnIterator(u8);
impl Iterator for TileColumnIterator {
type Item = TileColumn;
fn next(&mut self) -> Option<TileColumn> {
let result = TileColumn::try_from_u8(self.0);
self.0 += 1;
result
}
}
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Debug)]
pub struct TileRow(u8);
impl TileRow {
pub const ZERO: TileRow = TileRow(0);
pub const ROW_COUNT: u8 = 32;
const MAX: TileRow = TileRow(31);
const MAX_VISIBLE: TileRow = TileRow(29);
pub fn iter() -> TileRowIterator {
TileRowIterator(0)
}
pub fn increment(&mut self) -> bool {
let should_wrap = *self == TileRow::MAX;
if should_wrap {
self.0 = 0;
} else {
self.0 += 1;
}
should_wrap
}
pub fn increment_visible(&mut self) -> bool {
let should_wrap = *self == TileRow::MAX_VISIBLE;
if *self == TileRow::MAX_VISIBLE || *self == TileRow::MAX {
self.0 = 0;
} else {
self.0 += 1;
}
should_wrap
}
pub fn from_pixel_row(pixel_row: PixelRow) -> (TileRow, RowInTile) {
let tile_row = TileRow(pixel_row.to_u8() / 8);
let row_in_tile = FromPrimitive::from_u8(pixel_row.to_u8() % 8).unwrap();
(tile_row, row_in_tile)
}
pub fn to_u8(self) -> u8 {
self.0
}
pub fn to_u16(self) -> u16 {
u16::from(self.0)
}
pub fn to_usize(self) -> usize {
usize::from(self.0)
}
pub const fn try_from_u8(tile_row: u8) -> Option<TileRow> {
if tile_row < TileRow::ROW_COUNT {
Some(TileRow(tile_row))
} else {
None
}
}
}
impl From<u5> for TileRow {
fn from(value: u5) -> TileRow {
TileRow(value.into())
}
}
impl From<TileRow> for u8 {
fn from(value: TileRow) -> Self {
value.0
}
}
impl From<TileRow> for u16 {
fn from(value: TileRow) -> Self {
value.0.into()
}
}
#[derive(Clone)]
pub struct TileRowIterator(u8);
impl Iterator for TileRowIterator {
type Item = TileRow;
fn next(&mut self) -> Option<TileRow> {
let result = TileRow::try_from_u8(self.0);
self.0 += 1;
result
}
}