const std = @import("std");
const kindle = @import("kindle");
const zigimg = @import("zigimg");

const protocol = @import("eink_feed_protocol");

const Self = @This();

allocator: std.mem.Allocator,
framebuffer: *kindle.eink.fb.Framebuffer,
partial: bool,

pub fn init(allocator: std.mem.Allocator, framebuffer: *kindle.eink.fb.Framebuffer, partial: bool) Self {
    return .{
        .allocator = allocator,
        .framebuffer = framebuffer,
        .partial = partial,
    };
}

pub fn imageFromFrame(allocator: std.mem.Allocator, frame: *const protocol.models.Frame) !zigimg.Image {
    return zigimg.Image.fromRawPixels(
        allocator,
        @as(usize, frame.display.width),
        @as(usize, frame.display.height),
        frame.pixels,
        .grayscale8,
    );
}

pub fn fixImageForFb(self: *const Self, display: *const protocol.models.Display, image: *zigimg.Image) !zigimg.Image {
    try image.convert(.grayscale4);

    var fixed = try zigimg.Image.create(
        self.allocator,
        self.framebuffer.xres,
        self.framebuffer.yres,
        .grayscale4,
    );
    errdefer fixed.deinit();
    @memset(fixed.pixels.grayscale4, zigimg.color.Grayscale4{ .value = std.math.maxInt(u4) });

    switch (display.orientation) {
        .portrait_up => {
            const width = if (image.width > fixed.width) fixed.width else image.width;
            const height = if (image.height > fixed.height) fixed.height else image.height;
            for (0..height) |row| {
                for (0..width) |column| {
                    const idx = row * fixed.width + column;
                    fixed.pixels.grayscale4[idx].value = image.pixels.grayscale4[row * image.width + column].value;
                }
            }
        },
        .portrait_down => {
            const width = if (image.width > fixed.width) fixed.width else image.width;
            const height = if (image.height > fixed.height) fixed.height else image.height;
            for (0..height) |row| {
                for (0..width) |column| {
                    const idx = ((fixed.height - 1 - row) * fixed.width) + (fixed.width - 1 - column);
                    fixed.pixels.grayscale4[idx].value = image.pixels.grayscale4[row * image.width + column].value;
                }
            }
        },
        .landscape_left => {
            const width = if (image.width > fixed.height) fixed.height else image.width;
            const height = if (image.height > fixed.width) fixed.width else image.height;
            for (0..height) |row| {
                for (0..width) |column| {
                    const idx = ((fixed.height - 1 - column) * fixed.width) + row;
                    fixed.pixels.grayscale4[idx].value = image.pixels.grayscale4[row * image.width + column].value;
                }
            }
        },
        .landscape_right => {
            const width = if (image.width > fixed.height) fixed.height else image.width;
            const height = if (image.height > fixed.width) fixed.width else image.height;
            for (0..height) |row| {
                for (0..width) |column| {
                    const idx = ((column + 1) * fixed.width) - 1 - row;
                    fixed.pixels.grayscale4[idx].value = image.pixels.grayscale4[row * image.width + column].value;
                }
            }
        },
    }

    return fixed;
}

pub fn render(self: *Self, image: *const zigimg.Image) !void {
    if (image.width != self.framebuffer.xres or image.height != self.framebuffer.yres)
        return;

    self.framebuffer.fill(0x00);

    for (0..image.height) |row| {
        for (0..image.width) |column| {
            const color: u8 = 0xff - 16 * @as(u8, image.pixels.grayscale4[row * image.width + column].value);
            self.framebuffer.set(column, row, color);
        }
    }

    try self.framebuffer.update(if (self.partial) .partial else .full);
}
