use crate::{
    ColumnPrio, ColumnPrioMatrix, IntermittentSlice, IntermittentSliceMut, IterIntermittentSlices,
    IterMutIntermittentSlices, IterSlices, IterSlicesMut, Position, RowPrio, RowPrioMatrix,
};
use std::{fmt::Debug, marker::PhantomData};
pub struct Reftrix<'a, const R: usize, const C: usize, MemoryPriority, T> {
    inner: &'a mut [T],
    _prio: PhantomData<MemoryPriority>,
}
impl<'a, const R: usize, const C: usize, MemoryPriority, T> Reftrix<'a, R, C, MemoryPriority, T> {
    pub fn from_values(inner_values: &'a mut [T]) -> Self {
        assert!(inner_values.len() == R * C);
        Self {
            inner: inner_values,
            _prio: PhantomData,
        }
    }
}
impl<'a, 'r, const R: usize, const C: usize, T> ColumnPrioMatrix<'a, R, C, T>
    for Reftrix<'a, R, C, ColumnPrio, T>
where
    Self: 'a,
    'a: 'r,
    T: Copy + Default + Debug,
{
    fn insert(&mut self, location: Position, value: T) {
        self.get_mut_column(location.1)[location.0] = value;
    }
    fn get(&'a self, location: Position) -> &'a T {
        &self.get_column(location.1)[location.0]
    }
    fn get_mut(&'a mut self, location: Position) -> &'a mut T {
        &mut self.get_mut_column(location.1)[location.0]
    }
    fn fill_col(&mut self, col: usize, data: &[T]) {
        assert_eq!(data.len(), R);
        let start = col * C;
        self.inner[start..start + C].copy_from_slice(data);
    }
    fn fill_row(&mut self, row: usize, data: &[T]) {
        assert_eq!(data.len(), C);
        for (dst, src) in self.get_mut_row(row).into_iter().zip(data.iter()) {
            *dst = *src;
        }
    }
    fn get_column(&self, col: usize) -> &[T] {
        assert!(
            col < C,
            "Column: {} out of bounds {}, be carefull columns are 0 indexed.",
            col,
            C
        );
        let start = col * R;
        &self.inner[start..start + R]
    }
    fn get_mut_column(&mut self, col: usize) -> &mut [T] {
        assert!(
            col < C,
            "Column: {} out of bounds {}, be carefull columns are 0 indexed.",
            col,
            C
        );
        let start = col * R;
        &mut self.inner[start..start + R]
    }
    fn get_row(&self, row: usize) -> IntermittentSlice<'_, R, C, T> {
        assert!(
            row < R,
            "Row: {} out of bounds {}, be carefull rows are 0 indexed.",
            row,
            R
        );
        IntermittentSlice {
            start: &self.inner[row],
        }
    }
    fn get_mut_row(&mut self, row: usize) -> IntermittentSliceMut<'_, R, C, T> {
        assert!(
            row < R,
            "Row: {} out of bounds {}, be carefull rows are 0 indexed.",
            row,
            R
        );
        IntermittentSliceMut {
            start: &mut self.inner[row],
        }
    }
    fn rows(&self) -> IterIntermittentSlices<'_, R, C, T> {
        IterIntermittentSlices {
            slice_index: 0,
            matrix_buffer: self.inner,
        }
    }
    fn rows_mut(&mut self) -> IterMutIntermittentSlices<'_, R, C, T> {
        IterMutIntermittentSlices {
            slice_index: 0,
            matrix_buffer: self.inner,
        }
    }
    fn cols(&self) -> IterSlices<'_, R, C, T> {
        IterSlices {
            matrix_buffer: self.inner,
        }
    }
    fn cols_mut(&mut self) -> IterSlicesMut<'_, R, C, T> {
        IterSlicesMut {
            matrix_buffer: self.inner,
        }
    }
    fn apply_all(&mut self, f: fn(&mut T)) {
        for el in self.inner.iter_mut() {
            f(el);
        }
    }
    fn pretty_print(&self) {
        let strings: Vec<Vec<String>> = (0..4)
            .map(|i| {
                self.get_row(i)
                    .into_iter()
                    .map(|el| format!("{:02x?}", el))
                    .collect::<Vec<String>>()
            })
            .collect();
        for v in strings {
            for (i, s) in v.iter().enumerate() {
                print!("{}", s);
                if i != C - 1 {
                    print!("-")
                }
            }
            println!();
        }
    }
}
impl<'a, const R: usize, const C: usize, T> RowPrioMatrix<'a, R, C, T>
    for Reftrix<'a, R, C, RowPrio, T>
where
    Self: 'a,
    T: Copy + Default + Debug,
{
    fn insert(&mut self, location: Position, value: T) {
        self.get_mut_row(location.0)[location.1] = value;
    }
    fn get(&self, location: Position) -> &T {
        &self.get_row(location.0)[location.1]
    }
    fn get_mut(&mut self, location: Position) -> &mut T {
        &mut self.get_mut_row(location.0)[location.1]
    }
    fn fill_row(&mut self, row: usize, data: &[T]) {
        assert_eq!(data.len(), C);
        assert!(row < R);
        let start = row * C;
        self.inner[start..start + C].copy_from_slice(data);
    }
    fn fill_col(&'a mut self, col: usize, data: &[T]) {
        assert_eq!(data.len(), R);
        assert!(col < C);
        for (dst, src) in self.get_mut_column(col).into_iter().zip(data.iter()) {
            *dst = *src;
        }
    }
    fn get_column(&self, col: usize) -> IntermittentSlice<'_, R, C, T> {
        assert!(
            col < C,
            "Column: {} out of bounds {}, be carefull columns are 0 indexed.",
            col,
            C
        );
        IntermittentSlice {
            start: &self.inner[col],
        }
    }
    fn get_mut_column(&mut self, col: usize) -> IntermittentSliceMut<'_, R, C, T> {
        assert!(
            col < C,
            "Column: {} out of bounds {}, be carefull columns are 0 indexed.",
            col,
            C
        );
        IntermittentSliceMut {
            start: &mut self.inner[col],
        }
    }
    fn get_row(&self, row: usize) -> &[T] {
        assert!(
            row < R,
            "Row: {} out of bounds {}, be carefull rows are 0 indexed.",
            row,
            R
        );
        let start = row * C;
        &self.inner[start..start + C]
    }
    fn get_mut_row(&mut self, row: usize) -> &mut [T] {
        assert!(
            row < R,
            "Row: {} out of bounds {}, be carefull rows are 0 indexed.",
            row,
            R
        );
        let start = row * C;
        &mut self.inner[start..start + C]
    }
    fn rows(&self) -> IterSlices<'_, R, C, T> {
        IterSlices {
            matrix_buffer: self.inner,
        }
    }
    fn rows_mut(&mut self) -> IterSlicesMut<'_, R, C, T> {
        IterSlicesMut {
            matrix_buffer: self.inner,
        }
    }
    fn cols(&self) -> IterIntermittentSlices<'_, R, C, T> {
        IterIntermittentSlices {
            slice_index: 0,
            matrix_buffer: self.inner,
        }
    }
    fn cols_mut(&mut self) -> IterMutIntermittentSlices<'_, R, C, T> {
        IterMutIntermittentSlices {
            slice_index: 0,
            matrix_buffer: self.inner,
        }
    }
    fn apply_all(&mut self, f: fn(&mut T)) {
        for el in self.inner.iter_mut() {
            f(el);
        }
    }
    fn pretty_print(&self) {
        let strings: Vec<String> = self.inner.iter().map(|el| format!("{:02x?}", el)).collect();
        let _column_width = strings.iter().map(|el| el.len()).max();
        let mut index = 0;
        for _ in 0..R {
            for i in 0..C {
                print!("{}", strings[index]);
                if i != C - 1 {
                    print!("-")
                }
                index += 1;
            }
            println!();
        }
    }
}