extern crate la;
use la::sm::apply;
use la::sm::syncser;

/* Currently returning a sequence with closures is not possible
without workarounds, so use a macro.  What would help is Box<Fn>
implementing <Fn> to use boxed closures, or abstract return types to
allow unboxed closures. */

macro_rules! test_seq {
    ($c: expr, $data_in: expr, $period: expr) => (
        $data_in.iter()
        .flat_map(|&data|
                  // expand data word into bits
                  (0..$c.nb_bits).flat_map(move |shift| {
                      let bit = (data >> ($c.nb_bits - 1 - shift)) & 1;
                      // expand bit into clocked bit sequence
                      (0..2).map(move |clock|
                                 ($c.frame_active                 << $c.frame_channel) |
                                 (($c.clock_polarity ^ clock ^ 1) << $c.clock_channel) |
                                 (bit                             << $c.data_channel))
                  })
                  // follow with 1 bit frame release
                  .chain((0..1).map(|_|
                                    (($c.frame_active ^ 1) << $c.frame_channel) |
                                    ($c.clock_polarity     << $c.clock_channel)
                                    ))
                  )
            
        // oversample
        .flat_map(|bus|
                  (0..$period).map(move |_| bus))
        )
}

fn test_test_seq(c: &syncser::Config) {
    for bus in test_seq!(c, [0x55], 1) {
        println!("{:01$b}", bus, 3);
    }
}

fn test_vec(syncser: &mut syncser::SyncSer, data_in: Vec<usize>, period: usize) {
    let c = syncser.config;
    let data_out: Vec<_> =
        apply(syncser,
              test_seq!(c, data_in, period)
        ).collect();
    assert_eq!(data_out, data_in);
}

fn test_configs() {
    let nb_bits = 8;
    for edge in 0..2 {
        for period in 1..10 {
            let mut syncser = syncser::init(
                syncser::Config {
                    data_channel: 0,
                    clock_channel: 1,
                    frame_channel: 2,
                    frame_enable: true,
                    clock_edge: edge,
                    clock_polarity: edge ^ 1,
                    frame_active: 0,
                    frame_timeout: 0,
                    timeout_enable: false,
                    nb_bits: nb_bits,
                }
            );
            let n = 1 << nb_bits;
            let s = (0..n).rev();
            test_test_seq(&syncser.config);
            test_vec(&mut syncser, s.collect(), period);
        }
    }
    println!("syncser OK");
}

fn main() {
    test_configs();
}

#[test]
fn run_tests() {
    main()
}