#[cfg(test)]
mod testinner {

    use crate::*;

    use std::hash::Hasher;

    #[test]
    fn test_main() {
        use page_store::*;

        let limits = Limits::default();

        // Construct BlockPageStg.
        let file = atom_file::MultiFileStorage::new("test.db");
        let upd = atom_file::FastFileStorage::new("test.upd");
        let af = atom_file::AtomicFile::new_with_limits(file, upd, &limits.af_lim);
        let ps = BlockPageStg::new(af, &limits);
        let is_new = ps.is_new();

        let spd = SharedPagedData::new_from_ps(ps);

        if false {
            let psi = &spd.psi;
            println!("max page size={}", psi.max_size_page());
            for i in 0..psi.sizes() {
                println!("page size={}", psi.size(1 + i));
            }
        }

        let mut ps = PageSet::new(spd.new_writer());

        if false {
            let (root, len) = if is_new { (ps.new_page(), 0) } else { (3, 410) };
            test_tv(root, len, &mut ps);
        }

        /*
        if false {
            test_table(&mut ps);
        }
        */

        if false {
            test_varval(&mut ps);
        }

        if false {
            test_cust(&mut ps);
        }

        // crate::vbucket::test_vbucket(&mut ps);

        //test_vbuckmap(&mut ps);
        test_store(&mut ps);

        // ps.save();

        spd.shutdown();
    }

    struct CustEmailKey<'a> {
        email: &'a str,
        dt: &'a DataType,
        map: &'a TabInfo,
    }

    impl<'a> Hash for CustEmailKey<'a> {
        fn hash<H: Hasher>(&self, h: &mut H) {
            self.email.hash(h);
        }
    }

    impl<'a> Key<VarValAddr> for CustEmailKey<'a> {
        fn ok(&self, addr: VarValAddr, ps: &mut PageSet) -> Option<(VarValAddr, Value)> {
            let dt = DataType::IList;

            // Get the list of ids.
            let list = addr.get(&dt, ps);

            // Check the email of first element in the list.
            let rid = list.ilist()[0];
            let key = IdKey { rid, dt: self.dt };

            let mut bm = BuckMap::restore(self.map.main, ps);
            let (_, cr) = bm.get(&key).unwrap();

            // Check the email address in the customer record matches self.email
            let email = cr.list()[2].string();

            if *self.email == **email {
                return Some((addr, list));
            }
            None
        }
    }

    fn cust_dt() -> DataType {
        use pstd::veca;

        let optint = DataType::Enum(veca![
            (LString::from("None"), DataType::Empty),
            (LString::from("Some"), DataType::Int),
        ]);

        DataType::Struct(veca![
            (LString::from("Id"), DataType::Int),
            (LString::from("Name"), DataType::String),
            (LString::from("Email"), DataType::String),
            (LString::from("Postal"), DataType::String),
            (LString::from("HPass"), DataType::Binary),
            (LString::from("Age"), optint),
        ])
    }

    fn make_cust(
        ps: &mut PageSet,
        dt: &DataType,
        map: &mut TabInfo,
        name: &str,
        email: &str,
        postal: &str,
    ) {
        use pstd::veca;

        let rid = map.id();

        // Set up a customer record in memory (must match cdt).
        let cr = Value::List(veca![
            Value::Int(rid),
            Value::String(LString::from(name)),
            Value::String(LString::from(email)),
            Value::String(LString::from(postal)),
            Value::Binary(LVec::from([1, 2, 3, 4])),
            Value::Enum(1, LBox::new(Value::Int(68))),
        ]);

        let key = IdKey { rid, dt };
        key.insert(&cr, &mut map.main, ps);

        // Index record by email.

        let av = {
            let emkey = CustEmailKey { email, dt, map };

            // Get list of ids associated with cust email.
            {
                let mut bm = BuckMap::restore(map.index[0], ps);
                let av = bm.remove(&emkey);
                if bm.root_changed() {
                    map.index[0] = bm.save();
                }
                av
            }
        };

        let dtil = DataType::IList;
        let addr = if let Some((addr, mut list)) = av {
            // List already exists, append to it.
            list.ilist_mut().push(rid);
            addr.update(&dtil, &list, ps)
        } else {
            // Create list with single element and store it.
            let list = Value::IList(veca![rid]);
            VarValAddr::new(&dtil, &list, ps)
        };

        // Associate list addr with emkey.
        {
            let emkey = CustEmailKey { email, dt, map };

            let mut bm = BuckMap::restore(map.index[0], ps);
            bm.insert(&emkey, addr);
            if bm.root_changed() {
                map.index[0] = bm.save();
            }
        }
    }

    fn list_cust_ids_with_email(ps: &mut PageSet, map: &TabInfo, dt: &DataType, email: &str) {
        // Print customer ids with specified email address.

        let key = CustEmailKey { email, dt, map };
        let mut bm = BuckMap::restore(map.index[0], ps);

        if let Some((_, list)) = bm.get(&key) {
            println!("List of cust ids for {} = {:?}", email, list.ilist());
        } else {
            println!("No cust ids found for email {}", email);
        }
    }

    fn list_cust_all(ps: &mut PageSet, map: &TabInfo, cdt: &DataType) {
        let mut bm = BuckMap::<VarValAddr>::restore(map.main, ps);

        let x = bm.all_addr();

        // x.sort();
        let mut v = LVec::new();

        for addr in x {
            let cr = addr.get(cdt, ps);
            // println!("list_cust_all addr={:?} cr={:?}", addr, cr);
            v.push(cr);
        }
        v.sort();
        println!("list cust all v={:?}", v);
    }

    fn test_cust(ps: &mut PageSet) {
        /* CREATE TABLE cust( name string, email string, postal string ... ) */

        create_table("cust", cust_dt(), 1, ps);

        /* INSERT INTO cust( name, email, postal ... )
           VALUES ( 'George Barwood', 'george@gmail.com', '33 Sandpiper Close..' )
        */

        let (tid, mut obj) = get_obj_from_name("cust", ps).unwrap();

        let cust_map = decode_table(&mut obj);
        let cdt = get_table_datatype(tid, ps);
        println!("cdt={:?}", &cdt);

        make_cust(
            ps,
            &cdt,
            cust_map,
            "George Barwood",
            "george@gmail.com",
            "33 Sandpiper Close, GL2 4LZ",
        );

        // Make a duplicate
        make_cust(
            ps,
            &cdt,
            cust_map,
            "George Barwood",
            "george@gmail.com",
            "33 Sandpipe Close, GL2 4LZ",
        );

        // Another duplicate
        make_cust(
            ps,
            &cdt,
            cust_map,
            "George Barwood",
            "george@gmail.com",
            "33 Sandpipe Close, GL2 4LZ",
        );

        for _ in 102..120 {
            make_cust(
                ps,
                &cdt,
                cust_map,
                "Marilyn Barwood",
                "maz.barwood@gmail.com",
                "33 Sandpipe Close, GL2 4LZ",
            );
        }

        list_cust_all(ps, cust_map, &cdt);

        list_cust_ids_with_email(ps, cust_map, &cdt, "george@gmail.com");
        list_cust_ids_with_email(ps, cust_map, &cdt, "maz.barwood@gmail.com");
        list_cust_ids_with_email(ps, cust_map, &cdt, "mary@gmail.com");

        save_table(tid, obj, ps);

        list_obj_all(ps);
    }
} // end mod
