#![feature(concat_idents)]
use std::slice::from_raw_parts;

use criterion::{criterion_group, criterion_main, Criterion};

use opencv::core::{Mat, Size, CV_32F, CV_8U};
use opencv::imgproc::{cvt_color_def, ColorConversionCodes};
use opencv::prelude::{MatTraitConst, VideoCaptureTrait, VideoCaptureTraitConst};
use opencv::videoio::{VideoCapture, VideoCaptureProperties, VideoWriter, VideoWriterTrait};

use hercules_rt::{runner, HerculesImmBox, HerculesImmBoxTo};

use juno_edge_detection::*;

juno_build::juno!("edge_detection");

fn edge_detection_bench(c: &mut Criterion) {
    let mut group = c.benchmark_group("edge detection bench");
    group.sample_size(10);

    let input = "examples/formula1_scaled.mp4";

    let gs: usize = 7;
    let gaussian_filter: Vec<f32> = vec![
        0.000036, 0.000363, 0.001446, 0.002291, 0.001446, 0.000363, 0.000036, 0.000363, 0.003676,
        0.014662, 0.023226, 0.014662, 0.003676, 0.000363, 0.001446, 0.014662, 0.058488, 0.092651,
        0.058488, 0.014662, 0.001446, 0.002291, 0.023226, 0.092651, 0.146768, 0.092651, 0.023226,
        0.002291, 0.001446, 0.014662, 0.058488, 0.092651, 0.058488, 0.014662, 0.001446, 0.000363,
        0.003676, 0.014662, 0.023226, 0.014662, 0.003676, 0.000363, 0.000036, 0.000363, 0.001446,
        0.002291, 0.001446, 0.000363, 0.000036,
    ];
    let gaussian_filter_h = HerculesImmBox::from(gaussian_filter.as_slice());

    let sz: usize = 3;
    let structure: Vec<f32> = vec![1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0];
    let structure_h = HerculesImmBox::from(structure.as_slice());

    let sb: usize = 3;
    let sx: Vec<f32> = vec![-1.0, 0.0, 1.0, -2.0, 0.0, 2.0, -1.0, 0.0, 1.0];
    let sx_h = HerculesImmBox::from(sx.as_slice());

    let sy: Vec<f32> = vec![-1.0, -2.0, -1.0, 0.0, 0.0, 0.0, 1.0, 2.0, 1.0];
    let sy_h = HerculesImmBox::from(sy.as_slice());

    let theta: f32 = 0.1;

    let mut video = VideoCapture::from_file_def(&input).expect("Error loading video");
    assert!(video.is_opened().unwrap());

    let fps = video
        .get(VideoCaptureProperties::CAP_PROP_FPS.into())
        .expect("Error getting fps");

    let _num_frames = video
        .get(VideoCaptureProperties::CAP_PROP_FRAME_COUNT.into())
        .expect("Error getting number of frames") as usize;
    let width = video
        .get(VideoCaptureProperties::CAP_PROP_FRAME_WIDTH.into())
        .expect("Error getting width") as usize;
    let height = video
        .get(VideoCaptureProperties::CAP_PROP_FRAME_HEIGHT.into())
        .expect("Error getting height") as usize;
    let num_frames = 5;

    let mut r = runner!(edge_detection);
    let gaussian_filter_h = gaussian_filter_h.to();
    let structure_h = structure_h.to();
    let sx_h = sx_h.to();
    let sy_h = sy_h.to();

    let frames: Vec<_> = (0..num_frames).map(|_| load_frame(&mut video)).collect();

    group.bench_function("edge detection bench", |b| {
        b.iter(|| {
            for i in 0..num_frames {
                let frame = &frames[i];
                let ptr = frame.ptr_def().unwrap() as *const f32;

                assert!(frame.rows() as usize == height);
                assert!(frame.cols() as usize == width);

                let input = unsafe { from_raw_parts(ptr, height * width) };

                let input_h = HerculesImmBox::from(input);

                let result = async_std::task::block_on(async {
                    r.run(
                        height as u64,
                        width as u64,
                        input_h.to(),
                        gaussian_filter_h,
                        structure_h,
                        sx_h,
                        sy_h,
                        theta,
                    )
                    .await
                });
            }
        })
    });
}

criterion_group!(benches, edge_detection_bench);
criterion_main!(benches);