use std::fmt::Debug;

#[derive(Clone, Debug, PartialEq)]
pub struct Matrix<T> {
    pub rows: usize,
    pub cols: usize,
    pub data: Vec<Vec<T>>,
}

impl<T: Clone + Copy + Debug> Matrix<T> {
    pub fn new(data: Vec<Vec<T>>) -> Self {
        let rows = data.len();
        let cols = data
            .iter()
            .fold(0, |max, row| std::cmp::max(max, row.len()));
        Self { rows, cols, data }
    }

    pub fn row(&self, row_idx: usize) -> Vec<T> {
        self.data[row_idx].clone()
    }

    pub fn col(&self, col_idx: usize) -> Vec<T> {
        let mut col = Vec::new();
        (0..self.rows).for_each(|row_idx| {
            col.push(self.data[row_idx][col_idx]);
        });
        col
    }

    pub fn rotate_cw(self) -> Self {
        let mut data = Vec::new();
        for col_idx in 0..self.cols {
            let mut row = vec![];
            for row_idx in 0..self.rows {
                row.push(self.data[self.rows - row_idx - 1][col_idx]);
            }
            data.push(row);
        }

        Self {
            rows: self.cols,
            cols: self.rows,
            data,
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn it_can_rotate_symmetrix_matrix() {
        let matrix = Matrix::new(vec![vec!['A', 'B'], vec!['C', 'D']]);
        let expected = Matrix::new(vec![vec!['C', 'A'], vec!['D', 'B']]);
        assert_eq!(matrix.rotate_cw(), expected);
    }

    #[test]
    fn it_can_rotate_asymmetric_matrix() {
        let matrix = Matrix::new(vec![
            vec![Some('A'), Some('B'), Some('C')],
            vec![Some('D'), Some('E'), Some('F')],
        ]);
        let expected = Matrix::new(vec![
            vec![Some('D'), Some('A')],
            vec![Some('E'), Some('B')],
            vec![Some('F'), Some('C')],
        ]);
        assert_eq!(matrix.rotate_cw(), expected);
    }
}