use std::fmt;

// load sudoku
use crate::*;

/// Width of the sudoku board.
const WIDTH: u8 = 9;
// /// Size (width * height) of the board.
// const MAX_SIZE: u8 = 81;

// #[derive(Debug)]
/// Define a Group of sudoku positions.
/// This defines rows, columns, and cells for analysis.
#[derive(PartialEq)]
pub struct Group( pub [u8; WIDTH as usize]);

/*
pub struct Group {
    pub items: [u8; WIDTH as usize],
}
 */


/// Group of positions for rows.
const GROUP_ROW: [Group; 9] = [
    Group([      
            pos1(1, 1),
            pos1(2, 1),
            pos1(3, 1),
            pos1(4, 1),
            pos1(5, 1),
            pos1(6, 1),
            pos1(7, 1),
            pos1(8, 1),
            pos1(9, 1),
]),
    Group ([
            pos1(1, 2),
            pos1(2, 2),
            pos1(3, 2),
            pos1(4, 2),
            pos1(5, 2),
            pos1(6, 2),
            pos1(7, 2),
            pos1(8, 2),
            pos1(9, 2),
]),
   
    Group ([
            pos1(1, 3),
            pos1(2, 3),
            pos1(3, 3),
            pos1(4, 3),
            pos1(5, 3),
            pos1(6, 3),
            pos1(7, 3),
            pos1(8, 3),
            pos1(9, 3),
]),
    Group ([
            pos1(1, 4),
            pos1(2, 4),
            pos1(3, 4),
            pos1(4, 4),
            pos1(5, 4),
            pos1(6, 4),
            pos1(7, 4),
            pos1(8, 4),
            pos1(9, 4),
]),
    Group([
            pos1(1, 5),
            pos1(2, 5),
            pos1(3, 5),
            pos1(4, 5),
            pos1(5, 5),
            pos1(6, 5),
            pos1(7, 5),
            pos1(8, 5),
            pos1(9, 5),
]),
    Group([
            pos1(1, 6),
            pos1(2, 6),
            pos1(3, 6),
            pos1(4, 6),
            pos1(5, 6),
            pos1(6, 6),
            pos1(7, 6),
            pos1(8, 6),
            pos1(9, 6),
]),
    Group([
            pos1(1, 7),
            pos1(2, 7),
            pos1(3, 7),
            pos1(4, 7),
            pos1(5, 7),
            pos1(6, 7),
            pos1(7, 7),
            pos1(8, 7),
            pos1(9, 7),
]),
    Group ([
            pos1(1, 8),
            pos1(2, 8),
            pos1(3, 8),
            pos1(4, 8),
            pos1(5, 8),
            pos1(6, 8),
            pos1(7, 8),
            pos1(8, 8),
            pos1(9, 8),
    ]),
    Group ([
            pos1(1, 9),
            pos1(2, 9),
            pos1(3, 9),
            pos1(4, 9),
            pos1(5, 9),
            pos1(6, 9),
            pos1(7, 9),
            pos1(8, 9),
            pos1(9, 9),
    ])
];

/// Group of positions for columns.
const GROUP_COLUMN: [Group; 9] = [
    Group([
            pos1(1, 1),
            pos1(1, 2),
            pos1(1, 3),
            pos1(1, 4),
            pos1(1, 5),
            pos1(1, 6),
            pos1(1, 7),
            pos1(1, 8),
            pos1(1, 9),
    ]),
    Group ([
            pos1(2, 1),
            pos1(2, 2),
            pos1(2, 3),
            pos1(2, 4),
            pos1(2, 5),
            pos1(2, 6),
            pos1(2, 7),
            pos1(2, 8),
            pos1(2, 9),
    ]),
    Group ([
            pos1(3, 1),
            pos1(3, 2),
            pos1(3, 3),
            pos1(3, 4),
            pos1(3, 5),
            pos1(3, 6),
            pos1(3, 7),
            pos1(3, 8),
            pos1(3, 9),
    ]),
    Group ([
            pos1(4, 1),
            pos1(4, 2),
            pos1(4, 3),
            pos1(4, 4),
            pos1(4, 5),
            pos1(4, 6),
            pos1(4, 7),
            pos1(4, 8),
            pos1(4, 9),
    ]),
    Group ([
            pos1(5, 1),
            pos1(5, 2),
            pos1(5, 3),
            pos1(5, 4),
            pos1(5, 5),
            pos1(5, 6),
            pos1(5, 7),
            pos1(5, 8),
            pos1(5, 9),
    ]),
    Group ([
            pos1(6, 1),
            pos1(6, 2),
            pos1(6, 3),
            pos1(6, 4),
            pos1(6, 5),
            pos1(6, 6),
            pos1(6, 7),
            pos1(6, 8),
            pos1(6, 9),
    ]),
    Group ([
            pos1(7, 1),
            pos1(7, 2),
            pos1(7, 3),
            pos1(7, 4),
            pos1(7, 5),
            pos1(7, 6),
            pos1(7, 7),
            pos1(7, 8),
            pos1(7, 9),
    ]),
    Group ([
            pos1(8, 1),
            pos1(8, 2),
            pos1(8, 3),
            pos1(8, 4),
            pos1(8, 5),
            pos1(8, 6),
            pos1(8, 7),
            pos1(8, 8),
            pos1(8, 9),
    ]),
    Group ([
            pos1(9, 1),
            pos1(9, 2),
            pos1(9, 3),
            pos1(9, 4),
            pos1(9, 5),
            pos1(9, 6),
            pos1(9, 7),
            pos1(9, 8),
            pos1(9, 9),
    ]),
];

/// Group of positions for cells (3x3 grid).
const GROUP_CELL: [Group; 9] = [
    Group ([
            pos1(1, 1),
            pos1(2, 1),
            pos1(3, 1),
            pos1(1, 2),
            pos1(2, 2),
            pos1(3, 2),
            pos1(1, 3),
            pos1(2, 3),
            pos1(3, 3),
        ]),
    Group ([
            pos1(4, 1),
            pos1(5, 1),
            pos1(6, 1),
            pos1(4, 2),
            pos1(5, 2),
            pos1(6, 2),
            pos1(4, 3),
            pos1(5, 3),
            pos1(6, 3),
        ]),
    Group ([
            pos1(7, 1),
            pos1(8, 1),
            pos1(9, 1),
            pos1(7, 2),
            pos1(8, 2),
            pos1(9, 2),
            pos1(7, 3),
            pos1(8, 3),
            pos1(9, 3),
        ]),
    Group ([
            pos1(1, 4),
            pos1(2, 4),
            pos1(3, 4),
            pos1(1, 5),
            pos1(2, 5),
            pos1(3, 5),
            pos1(1, 6),
            pos1(2, 6),
            pos1(3, 6),
        ]),
    Group ([
            pos1(4, 4),
            pos1(5, 4),
            pos1(6, 4),
            pos1(4, 5),
            pos1(5, 5),
            pos1(6, 5),
            pos1(4, 6),
            pos1(5, 6),
            pos1(6, 6),
        ]),
    Group ([
            pos1(7, 4),
            pos1(8, 4),
            pos1(9, 4),
            pos1(7, 5),
            pos1(8, 5),
            pos1(9, 5),
            pos1(7, 6),
            pos1(8, 6),
            pos1(9, 6),
        ]),
    Group ([
            pos1(1, 7),
            pos1(2, 7),
            pos1(3, 7),
            pos1(1, 8),
            pos1(2, 8),
            pos1(3, 8),
            pos1(1, 9),
            pos1(2, 9),
            pos1(3, 9),
        ]),
    Group ([
            pos1(4, 7),
            pos1(5, 7),
            pos1(6, 7),
            pos1(4, 8),
            pos1(5, 8),
            pos1(6, 8),
            pos1(4, 9),
            pos1(5, 9),
            pos1(6, 9),
        ]),
    Group ([
            pos1(7, 7),
            pos1(8, 7),
            pos1(9, 7),
            pos1(7, 8),
            pos1(8, 8),
            pos1(9, 8),
            pos1(7, 9),
            pos1(8, 9),
            pos1(9, 9),
        ]),
];

impl fmt::Debug for Group {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        if f.alternate() {
            write!(f, "Group {{ {}[{},{}], {}[{},{}], {}[{},{}], {}[{},{}], {}[{},{}], {}[{},{}], {}[{},{}], {}[{},{}], {}[{},{}] }}",
                self.0[0], xy(self.0[0]).0, xy(self.0[0]).1,
                self.0[1], xy(self.0[1]).0, xy(self.0[1]).1,
                self.0[2], xy(self.0[2]).0, xy(self.0[2]).1,
                self.0[3], xy(self.0[3]).0, xy(self.0[3]).1,
                self.0[4], xy(self.0[4]).0, xy(self.0[4]).1,
                self.0[5], xy(self.0[5]).0, xy(self.0[5]).1,
                self.0[6], xy(self.0[6]).0, xy(self.0[6]).1,
                self.0[7], xy(self.0[7]).0, xy(self.0[7]).1,
                self.0[8], xy(self.0[8]).0, xy(self.0[8]).1,
            )
        } else {
            f.debug_struct("Group").field("items", &self).finish()
        }
    }
}

pub fn for_column(i: u8) -> &'static Group {
    &GROUP_COLUMN[i as usize]
}

pub fn for_row(i: u8) -> &'static Group {
    &GROUP_ROW[i as usize]
}

pub fn for_cell(i: u8) -> &'static Group {
    &GROUP_CELL[i as usize]
}

/// Which cell contains this x,y?
pub fn which_cell(x: u8, y: u8) -> u8 {
    (x / 3) + (y / 3) * 3
}

#[cfg(test)]
mod tests {
    // use crate::sudoku::*;
    use crate::group::*;
    // use crate::sudoku::group::*;
    // use crate::Group;
    // use group::*;

    // use crate::{for_cell, for_column, for_row};

    #[test]
    fn check_columns() {
        let mut g = Group::new();

        for i in 0..9 {
            println!("Index={}", i);
            g.for_column(i, 1);
            let new_g = for_column(i);
            assert_eq!(g, *new_g);
        }
    }

    #[test]
    fn check_rows() {
        let mut g = Group::new();

        for i in 0..9 {
            println!("Index={}", i);
            g.for_row(1, i);
            let new_g = for_row(i);
            assert_eq!(g, *new_g);
        }
    }

    #[test]
    fn check_cells() {
        let mut g = Group::new();
        for i in 0..9 {
            println!("Index={}", i);
            g.for_block((i % 3) * 3, (i / 3) * 3);
            let new_g = for_cell(i);
            assert_eq!(g, *new_g);
        }
    }
}

impl Group {
    pub fn new() -> Self {
        Group (
             [0; WIDTH as usize],
            )
    }

    pub fn for_column(&mut self, x: u8, _y: u8) {
        for y in 0..WIDTH {
            self.0[y as usize] = pos(x, y);
        }
    }
    pub fn for_row(&mut self, _x: u8, y: u8) {
        for x in 0..WIDTH {
            self.0[x as usize] = pos(x, y);
        }
    }
    pub fn for_block(&mut self, x: u8, y: u8) {
        // Find starting block positions
        let sb_x = x - (x % 3);
        let sb_y = y - (y % 3);
        for i in 0..WIDTH {
            let ix = i % 3;
            let iy = i / 3;
            // println!("i = {}, sb.x = {} sb.y = {}, ix = {} iy = {}", i, sb_x, sb_y, ix, iy);
            self.0[i as usize] = pos(sb_x + ix, sb_y + iy);
        }
    }

    pub fn for_iter(&mut self, i: u8) {
        let sb_x = (i % 3) * 3;
        let sb_y = (i / 3) * 3;
        self.for_block(sb_x, sb_y);
    }

    pub fn display(&self) {
        for i in 0..WIDTH {
            let v = self.0[i as usize];
            print!("{} [{},{}] ", v, xy(v).0, xy(v).1);
        }
        println!("");
    }
}