use insta_image::assert_png_snapshot;
use std::{
    ptr::{self, NonNull},
    time::Instant,
};
use wings_rs::pixmap::ffi::*;
use wings_rs_tests::HeadlessApplication;
use wrlib_rs::ffi::{RLoadImage, RReleaseImage};

#[test]
fn draw_blank_pixmap() {
    let mut app = HeadlessApplication::new();
    unsafe {
        let display = (*app.screen.as_ptr()).display;
        let win = x11::xlib::XCreateSimpleWindow(
            display,
            (*app.screen.as_ptr()).rootWin,
            0,
            0,
            512,
            512,
            1,
            1,
            x11::xlib::XWhitePixel(display, 0),
        );
        x11::xlib::XMapWindow(display, win);

        let pixmap = NonNull::new(WMCreatePixmap(app.screen.as_ptr(), 128, 196, 24, 1)).unwrap();
        WMDrawPixmap(pixmap.as_ptr(), win, 64, 96);
        while app.pump_event_queue(Instant::now()) {}
        assert_png_snapshot!("empty_window", app.xvfb.png_screenshot());

        let gc = x11::xlib::XCreateGC(display, (*pixmap.as_ptr()).pixmap(), 0, ptr::null_mut());
        x11::xlib::XSetForeground(display, gc, 0);
        x11::xlib::XFillRectangle(display, (*pixmap.as_ptr()).pixmap(), gc, 0, 0, 128, 196);
        WMDrawPixmap(pixmap.as_ptr(), win, 64, 96);
        while app.pump_event_queue(Instant::now()) {}
        assert_png_snapshot!("empty_pixmap_on_window", app.xvfb.png_screenshot());

        x11::xlib::XFreeGC(display, gc);
        WMReleasePixmap(pixmap.as_ptr());
    }
}

// WMCreatePixmapFromXPixmaps is not tested because it simply fills in struct
// fields. Coverage from other tests in this file should be adequate.

#[test]
fn draw_pixmap_from_file() {
    let mut app = HeadlessApplication::new();
    unsafe {
        let display = (*app.screen.as_ptr()).display;
        let win = x11::xlib::XCreateSimpleWindow(
            display,
            (*app.screen.as_ptr()).rootWin,
            0,
            0,
            512,
            512,
            1,
            1,
            x11::xlib::XWhitePixel(display, 0),
        );
        x11::xlib::XMapWindow(display, win);

        let pixmap = NonNull::new(WMCreatePixmapFromFile(
            app.screen.as_ptr(),
            c"tests/image_128x120.png".as_ptr(),
        ))
        .unwrap();
        WMDrawPixmap(pixmap.as_ptr(), win, 64, 96);

        while app.pump_event_queue(Instant::now()) {}
        assert_png_snapshot!("from_file_pixmap", app.xvfb.png_screenshot());
        WMReleasePixmap(pixmap.as_ptr());
    }
}

#[test]
fn draw_pixmap_from_r_image_no_mask() {
    let mut app = HeadlessApplication::new();
    unsafe {
        let display = (*app.screen.as_ptr()).display;
        let win = x11::xlib::XCreateSimpleWindow(
            display,
            (*app.screen.as_ptr()).rootWin,
            0,
            0,
            512,
            512,
            1,
            1,
            x11::xlib::XWhitePixel(display, 0),
        );
        x11::xlib::XMapWindow(display, win);

        let rimage = RLoadImage(
            (*app.screen.as_ptr()).rcontext,
            c"tests/image_128x120.png".as_ptr(),
            0,
        );
        let pixmap =
            NonNull::new(WMCreatePixmapFromRImage(app.screen.as_ptr(), rimage, 255)).unwrap();
        WMDrawPixmap(pixmap.as_ptr(), win, 70, 74);

        while app.pump_event_queue(Instant::now()) {}
        assert_png_snapshot!("from_r_image_no_mask", app.xvfb.png_screenshot());
        WMReleasePixmap(pixmap.as_ptr());
    }
}

#[test]
fn draw_pixmap_from_r_image_with_mask() {
    let mut app = HeadlessApplication::new();
    unsafe {
        let display = (*app.screen.as_ptr()).display;
        let win = x11::xlib::XCreateSimpleWindow(
            display,
            (*app.screen.as_ptr()).rootWin,
            0,
            0,
            512,
            512,
            1,
            1,
            x11::xlib::XBlackPixel(display, 0),
        );
        x11::xlib::XMapWindow(display, win);

        let rimage = NonNull::new(RLoadImage(
            (*app.screen.as_ptr()).rcontext,
            c"tests/image_128x120_varying_alpha.png".as_ptr(),
            255,
        ))
        .unwrap();

        // The threshold given to WMCreatePixmapFromRImage is compared against
        // the image's alpha channel. Pixels whose transparency are below the
        // threshold are masked out. This should draw a quartet of images with
        // varying masks.

        // Totally masked.
        let pixmap_255 = NonNull::new(WMCreatePixmapFromRImage(
            app.screen.as_ptr(),
            rimage.as_ptr(),
            255,
        ))
        .unwrap();
        WMDrawPixmap(pixmap_255.as_ptr(), win, 70, 74);
        // Three bubbles masked.
        let pixmap_200 = NonNull::new(WMCreatePixmapFromRImage(
            app.screen.as_ptr(),
            rimage.as_ptr(),
            200,
        ))
        .unwrap();
        WMDrawPixmap(pixmap_200.as_ptr(), win, 200, 74);
        // Two bubbles masked.
        let pixmap_128 = NonNull::new(WMCreatePixmapFromRImage(
            app.screen.as_ptr(),
            rimage.as_ptr(),
            128,
        ))
        .unwrap();
        WMDrawPixmap(pixmap_128.as_ptr(), win, 70, 200);
        // Nothing masked.
        let pixmap_0 = NonNull::new(WMCreatePixmapFromRImage(
            app.screen.as_ptr(),
            rimage.as_ptr(),
            0,
        ))
        .unwrap();
        WMDrawPixmap(pixmap_0.as_ptr(), win, 200, 200);

        while app.pump_event_queue(Instant::now()) {}
        assert_png_snapshot!("from_r_image_with_mask", app.xvfb.png_screenshot());
        WMReleasePixmap(pixmap_255.as_ptr());
        WMReleasePixmap(pixmap_200.as_ptr());
        WMReleasePixmap(pixmap_128.as_ptr());
        WMReleasePixmap(pixmap_0.as_ptr());
        RReleaseImage(rimage.as_ptr());
    }
}

#[test]
fn draw_blended_pixmap_from_r_image() {
    let mut app = HeadlessApplication::new();
    unsafe {
        let display = (*app.screen.as_ptr()).display;
        let win = x11::xlib::XCreateSimpleWindow(
            display,
            (*app.screen.as_ptr()).rootWin,
            0,
            0,
            512,
            512,
            1,
            1,
            x11::xlib::XWhitePixel(display, 0),
        );
        x11::xlib::XMapWindow(display, win);

        let rimage = RLoadImage(
            (*app.screen.as_ptr()).rcontext,
            c"tests/image_128x120.png".as_ptr(),
            0,
        );
        let pixmap =
            NonNull::new(WMCreateGreyedPixmapFromRImage(app.screen.as_ptr(), rimage)).unwrap();
        WMDrawPixmap(pixmap.as_ptr(), win, 70, 74);

        while app.pump_event_queue(Instant::now()) {}
        assert_png_snapshot!("from_r_image_no_mask", app.xvfb.png_screenshot());
        WMReleasePixmap(pixmap.as_ptr());
    }
}

#[test]
fn draw_blended_pixmap_from_file() {
    let mut app = HeadlessApplication::new();
    unsafe {
        let display = (*app.screen.as_ptr()).display;
        let win = x11::xlib::XCreateSimpleWindow(
            display,
            (*app.screen.as_ptr()).rootWin,
            0,
            0,
            512,
            512,
            1,
            1,
            x11::xlib::XWhitePixel(display, 0),
        );
        x11::xlib::XMapWindow(display, win);

        let pixmap = NonNull::new(WMCreatePixmapFromFile(
            app.screen.as_ptr(),
            c"tests/image_with_transparent_dot_128x120.png".as_ptr(),
        ))
        .unwrap();
        WMDrawPixmap(pixmap.as_ptr(), win, 64, 96);
        while app.pump_event_queue(Instant::now()) {}
        assert_png_snapshot!("from_file_blended_base_pixmap", app.xvfb.png_screenshot());

        x11::xlib::XUnmapWindow(display, win);
        WMReleasePixmap(pixmap.as_ptr());

        let win = x11::xlib::XCreateSimpleWindow(
            display,
            (*app.screen.as_ptr()).rootWin,
            0,
            0,
            512,
            512,
            1,
            1,
            x11::xlib::XWhitePixel(display, 0),
        );
        x11::xlib::XMapWindow(display, win);
        while app.pump_event_queue(Instant::now()) {}
        assert_png_snapshot!(
            "from_file_blended_cleared_window",
            app.xvfb.png_screenshot()
        );

        let pixmap = NonNull::new(WMCreateGreyedPixmapFromFile(
            app.screen.as_ptr(),
            c"tests/image_with_transparent_dot_128x120.png".as_ptr(),
        ))
        .unwrap();
        WMDrawPixmap(pixmap.as_ptr(), win, 64, 96);

        while app.pump_event_queue(Instant::now()) {}
        assert_png_snapshot!("from_file_blended_with_grey", app.xvfb.png_screenshot());
        WMReleasePixmap(pixmap.as_ptr());
    }
}

#[test]
fn draw_scaled_blended_pixmap_from_file() {
    let mut app = HeadlessApplication::new();
    unsafe {
        let display = (*app.screen.as_ptr()).display;
        let win = x11::xlib::XCreateSimpleWindow(
            display,
            (*app.screen.as_ptr()).rootWin,
            0,
            0,
            512,
            512,
            1,
            1,
            x11::xlib::XWhitePixel(display, 0),
        );
        x11::xlib::XMapWindow(display, win);

        let pixmap = NonNull::new(WMCreatePixmapFromFile(
            app.screen.as_ptr(),
            c"tests/image_with_transparent_dot_128x120.png".as_ptr(),
        ))
        .unwrap();
        WMDrawPixmap(pixmap.as_ptr(), win, 64, 96);
        while app.pump_event_queue(Instant::now()) {}
        assert_png_snapshot!(
            "from_file_scaled_blended_base_pixmap",
            app.xvfb.png_screenshot()
        );

        x11::xlib::XUnmapWindow(display, win);
        WMReleasePixmap(pixmap.as_ptr());

        let win = x11::xlib::XCreateSimpleWindow(
            display,
            (*app.screen.as_ptr()).rootWin,
            0,
            0,
            512,
            512,
            1,
            1,
            x11::xlib::XWhitePixel(display, 0),
        );
        x11::xlib::XMapWindow(display, win);
        while app.pump_event_queue(Instant::now()) {}
        assert_png_snapshot!(
            "from_file_scaled_blended_cleared_window",
            app.xvfb.png_screenshot()
        );

        let pixmap = NonNull::new(WMCreateScaledGreyedPixmapFromFile(
            app.screen.as_ptr(),
            c"tests/image_with_transparent_dot_128x120.png".as_ptr(),
            60,
            64,
        ))
        .unwrap();
        WMDrawPixmap(pixmap.as_ptr(), win, 64, 96);

        while app.pump_event_queue(Instant::now()) {}
        assert_png_snapshot!(
            "from_file_scaled_blended_with_grey",
            app.xvfb.png_screenshot()
        );
        WMReleasePixmap(pixmap.as_ptr());
    }
}

#[test]
fn draw_scaled_blended_pixmap_from_file_non_integral_scaling() {
    let mut app = HeadlessApplication::new();
    unsafe {
        let display = (*app.screen.as_ptr()).display;
        let win = x11::xlib::XCreateSimpleWindow(
            display,
            (*app.screen.as_ptr()).rootWin,
            0,
            0,
            512,
            512,
            1,
            1,
            x11::xlib::XWhitePixel(display, 0),
        );
        x11::xlib::XMapWindow(display, win);

        let pixmap = NonNull::new(WMCreatePixmapFromFile(
            app.screen.as_ptr(),
            c"tests/image_128x120.png".as_ptr(),
        ))
        .unwrap();
        WMDrawPixmap(pixmap.as_ptr(), win, 64, 96);
        while app.pump_event_queue(Instant::now()) {}
        assert_png_snapshot!(
            "from_file_scaled_blended_non_integral_base_pixmap",
            app.xvfb.png_screenshot()
        );

        x11::xlib::XUnmapWindow(display, win);
        WMReleasePixmap(pixmap.as_ptr());

        let win = x11::xlib::XCreateSimpleWindow(
            display,
            (*app.screen.as_ptr()).rootWin,
            0,
            0,
            512,
            512,
            1,
            1,
            x11::xlib::XWhitePixel(display, 0),
        );
        x11::xlib::XMapWindow(display, win);
        while app.pump_event_queue(Instant::now()) {}
        assert_png_snapshot!(
            "from_file_scaled_blended_non_integral_cleared_window",
            app.xvfb.png_screenshot()
        );

        let pixmap = NonNull::new(WMCreateScaledGreyedPixmapFromFile(
            app.screen.as_ptr(),
            // No alpha channel on this image, so no color blending happens.
            c"tests/image_128x120.png".as_ptr(),
            // Shrink by only a little bit, such that simple integer division
            // might be thrown off. This test ensures that rescaling will
            // actually happen.
            110,
            114,
        ))
        .unwrap();
        WMDrawPixmap(pixmap.as_ptr(), win, 64, 96);

        while app.pump_event_queue(Instant::now()) {}
        assert_png_snapshot!(
            "from_file_scaled_blended_non_integral_scaled",
            app.xvfb.png_screenshot()
        );
        WMReleasePixmap(pixmap.as_ptr());
    }
}
