const std = @import("std");

const zgl = @import("zgl");

const zstbi = @import("zstbi");
const Image = zstbi.Image;

const Texture = @This();

id: zgl.Texture,
target: zgl.TextureTarget,
unit: u32,

pub fn init(unit: u32, target: zgl.TextureTarget) Texture {
    const id = zgl.Texture.create(target);

    return .{ .id = id, .unit = unit, .target = target };
}

pub fn loadCubemapFromFile(self: Texture, pathnames: []const [:0]const u8) !void {
    const info = Image.info(pathnames[0]);
    var internal_formal: zgl.TextureInternalFormat = undefined;
    var pixel_formal: zgl.PixelFormat = undefined;
    internal_formal, pixel_formal = switch (info.num_components) {
        1 => .{ .r8, .red },
        2 => .{ .rg8, .rg },
        3 => .{ .rgb8, .rgb },
        4 => .{ .rgba8, .rgba },
        else => unreachable,
    };
    self.id.storage2D(1, internal_formal, info.width, info.height);

    for (pathnames, 0..) |pathname, i| {
        var image = try Image.loadFromFile(pathname, info.num_components);
        defer image.deinit();
        self.id.subImage3D(
            0,
            0,
            0,
            i,
            info.width,
            info.height,
            1,
            pixel_formal,
            if (zstbi.is16bit(pathname)) .unsigned_short else .unsigned_byte,
            image.data.ptr,
        );
    }
}

pub fn loadCubemap(self: Texture, images: []const [*]const u8, width: usize, height: usize, pixel_type: zgl.PixelType, channels: u32) void {
    var internal_formal: zgl.TextureInternalFormat = undefined;
    var pixel_formal: zgl.PixelFormat = undefined;
    internal_formal, pixel_formal = switch (channels) {
        1 => .{ .r8, .red },
        2 => .{ .rg8, .rg },
        3 => .{ .rgb8, .rgb },
        4 => .{ .rgba8, .rgba },
        else => unreachable,
    };

    self.id.storage2D(1, internal_formal, width, height);
    for (0..6) |i| {
        self.id.subImage3D(0, 0, 0, i, width, height, 1, pixel_formal, pixel_type, images[i]);
    }
}

pub fn loadFromFile(self: Texture, pathname: [:0]const u8) !void {
    var image = try Image.loadFromFile(pathname, 0);
    defer image.deinit();

    load(
        self,
        image.data.ptr,
        image.width,
        image.height,
        if (zstbi.is16bit(pathname)) .unsigned_short else .unsigned_byte,
        image.num_components,
    );
}

pub fn load(self: Texture, image: [*]const u8, width: usize, height: usize, pixel_type: zgl.PixelType, channels: u32) void {
    var internal_formal: zgl.TextureInternalFormat = undefined;
    var pixel_formal: zgl.PixelFormat = undefined;
    internal_formal, pixel_formal = switch (channels) {
        1 => .{ .r8, .red },
        2 => .{ .rg8, .rg },
        3 => .{ .rgb8, .rgb },
        4 => .{ .rgba8, .rgba },
        else => unreachable,
    };

    self.id.storage2D(1, internal_formal, width, height);
    self.id.subImage2D(0, 0, 0, width, height, pixel_formal, pixel_type, image);
    self.id.generateMipmap();
}

pub fn bind(self: Texture) void {
    self.id.bindTo(self.unit);
}

pub fn unbind(self: Texture) void {
    zgl.bindTextureUnit(.invalid, self.unit);
}

pub fn delete(self: Texture) void {
    self.id.delete();
}
