const std = @import("std");
pub const c = @import("c.zig").c;

pub const PixelFormat = enum(c_int) {
    pixelformatYv12 = 0x32315659, //Planar mode: Y + V + U  (3 planes)
    pixelformatIyuv = 0x56555949, //Planar mode: Y + U + V  (3 planes)
    pixelformatYuy2 = 0x32595559, //Packed mode: Y0+U0+Y1+V0 (1 plane)
    pixelformatUyvy = 0x59565955, //Packed mode: U0+Y0+V0+Y1 (1 plane)
    pixelformatYvyu = 0x55595659, //Packed mode: Y0+V0+Y1+U0 (1 plane)
    pixelformatNv12 = 0x3231564e, //Planar mode: Y + U/V interleaved  (2 planes)
    pixelformatNv21 = 0x3132564e, //Planar mode: Y + V/U interleaved  (2 planes)
    pixelformatP010 = 0x30313050, //Planar mode: Y + U/V interleaved  (2 planes)
    pixelformatExternalOes = 0x2053454f, //Android video texture format
};

pub const BlendMode = u32;

pub const IOStream = opaque {
    pub inline fn loadBMP_IO(iostream: *IOStream, closeio: bool) ?*Surface {
        return @ptrCast(c.SDL_LoadBMP_IO(@ptrCast(iostream), @bitCast(closeio)));
    }
};

pub const Rect = extern struct {
    x: c_int,
    y: c_int,
    w: c_int,
    h: c_int,
};

pub const Palette = extern struct {
    ncolors: c_int, // number of elements in `colors`.
    colors: ?*Color, // an array of colors, `ncolors` long.
    version: u32, // internal use only, do not touch.
    refcount: c_int, // internal use only, do not touch.
};

pub const Color = extern struct {
    r: u8,
    g: u8,
    b: u8,
    a: u8,
};

pub const Colorspace = enum(c_int) {
    colorspaceSrgb = 0x120005a0, //Equivalent to DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709
    colorRangeFull,
    colorPrimariesBt709,
    transferCharacteristicsSrgb,
    matrixCoefficientsIdentity,
    colorspaceSrgbLinear = 0x12000500, //Equivalent to DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709
    transferCharacteristicsLinear,
    colorspaceHdr10 = 0x12002600, //Equivalent to DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020
    colorPrimariesBt2020,
    transferCharacteristicsPq,
    colorspaceJpeg = 0x220004c6, //Equivalent to DXGI_COLOR_SPACE_YCBCR_FULL_G22_NONE_P709_X601
    transferCharacteristicsBt601,
    matrixCoefficientsBt601,
    colorspaceBt601Limited = 0x211018c6, //Equivalent to DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P601
    colorRangeLimited,
    colorPrimariesBt601,
    colorspaceBt601Full = 0x221018c6, //Equivalent to DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P601
    colorspaceBt709Limited = 0x21100421, //Equivalent to DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709
    transferCharacteristicsBt709,
    matrixCoefficientsBt709,
    colorspaceBt709Full = 0x22100421, //Equivalent to DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709
    colorspaceBt2020Limited = 0x21102609, //Equivalent to DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P2020
    matrixCoefficientsBt2020Ncl,
    colorspaceBt2020Full = 0x22102609, //Equivalent to DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P2020

    pub const colorspaceRgbDefault = .colorspaceSrgb; //The default colorspace for RGB surfaces if no colorspace is specified
    pub const colorspaceYuvDefault = .colorspaceJpeg; //The default colorspace for YUV surfaces if no colorspace is specified
};

pub const PropertiesID = u32;

pub const SurfaceFlags = packed struct(u32) {
    surfacePreallocated: bool = false, // Surface uses preallocated pixel memory
    surfaceLockNeeded: bool = false, // Surface needs to be locked to access pixels
    surfaceLocked: bool = false, // Surface is currently locked
    surfaceSimdAligned: bool = false, // Surface uses pixel memory allocated with SDL_aligned_alloc()
    pad0: u27 = 0,
    rsvd: bool = false,

    pub const None = SurfaceFlags{};
};

pub const ScaleMode = enum(c_int) {
    scalemodeNearest, //nearest pixel sampling
    scalemodeLinear, //linear filtering
};

pub const FlipMode = enum(c_int) {
    flipNone, //Do not flip
    flipHorizontal, //flip horizontally
    flipVertical, //flip vertically
};

pub const Surface = opaque {
    pub inline fn destroySurface(surface: *Surface) void {
        return c.SDL_DestroySurface(@ptrCast(surface));
    }

    pub inline fn getSurfaceProperties(surface: *Surface) PropertiesID {
        return c.SDL_GetSurfaceProperties(@ptrCast(surface));
    }

    pub inline fn setSurfaceColorspace(surface: *Surface, colorspace: Colorspace) bool {
        return @bitCast(c.SDL_SetSurfaceColorspace(@ptrCast(surface), colorspace));
    }

    pub inline fn getSurfaceColorspace(surface: *Surface) Colorspace {
        return c.SDL_GetSurfaceColorspace(@ptrCast(surface));
    }

    pub inline fn createSurfacePalette(surface: *Surface) ?*Palette {
        return @ptrCast(c.SDL_CreateSurfacePalette(@ptrCast(surface)));
    }

    pub inline fn setSurfacePalette(surface: *Surface, palette: ?*Palette) bool {
        return @bitCast(c.SDL_SetSurfacePalette(@ptrCast(surface), @ptrCast(palette)));
    }

    pub inline fn getSurfacePalette(surface: *Surface) ?*Palette {
        return @ptrCast(c.SDL_GetSurfacePalette(@ptrCast(surface)));
    }

    pub inline fn addSurfaceAlternateImage(surface: *Surface, image: ?*Surface) bool {
        return @bitCast(c.SDL_AddSurfaceAlternateImage(@ptrCast(surface), @ptrCast(image)));
    }

    pub inline fn surfaceHasAlternateImages(surface: *Surface) bool {
        return @bitCast(c.SDL_SurfaceHasAlternateImages(@ptrCast(surface)));
    }

    pub inline fn getSurfaceImages(surface: *Surface, count: *c_int) [*c]?*Surface {
        return c.SDL_GetSurfaceImages(@ptrCast(surface), @ptrCast(count));
    }

    pub inline fn removeSurfaceAlternateImages(surface: *Surface) void {
        return c.SDL_RemoveSurfaceAlternateImages(@ptrCast(surface));
    }

    pub inline fn lockSurface(surface: *Surface) bool {
        return @bitCast(c.SDL_LockSurface(@ptrCast(surface)));
    }

    pub inline fn unlockSurface(surface: *Surface) void {
        return c.SDL_UnlockSurface(@ptrCast(surface));
    }

    pub inline fn saveBMP_IO(surface: *Surface, dst: ?*IOStream, closeio: bool) bool {
        return @bitCast(c.SDL_SaveBMP_IO(@ptrCast(surface), @ptrCast(dst), @bitCast(closeio)));
    }

    pub inline fn saveBMP(surface: *Surface, file: [*c]const u8) bool {
        return @bitCast(c.SDL_SaveBMP(@ptrCast(surface), file));
    }

    pub inline fn setSurfaceRLE(surface: *Surface, enabled: bool) bool {
        return @bitCast(c.SDL_SetSurfaceRLE(@ptrCast(surface), @bitCast(enabled)));
    }

    pub inline fn surfaceHasRLE(surface: *Surface) bool {
        return @bitCast(c.SDL_SurfaceHasRLE(@ptrCast(surface)));
    }

    pub inline fn setSurfaceColorKey(surface: *Surface, enabled: bool, key: u32) bool {
        return @bitCast(c.SDL_SetSurfaceColorKey(@ptrCast(surface), @bitCast(enabled), key));
    }

    pub inline fn surfaceHasColorKey(surface: *Surface) bool {
        return @bitCast(c.SDL_SurfaceHasColorKey(@ptrCast(surface)));
    }

    pub inline fn getSurfaceColorKey(surface: *Surface, key: *u32) bool {
        return @bitCast(c.SDL_GetSurfaceColorKey(@ptrCast(surface), @ptrCast(key)));
    }

    pub inline fn setSurfaceColorMod(surface: *Surface, r: u8, g: u8, b: u8) bool {
        return @bitCast(c.SDL_SetSurfaceColorMod(@ptrCast(surface), r, g, b));
    }

    pub inline fn getSurfaceColorMod(surface: *Surface, r: [*c]u8, g: [*c]u8, b: [*c]u8) bool {
        return @bitCast(c.SDL_GetSurfaceColorMod(@ptrCast(surface), r, g, b));
    }

    pub inline fn setSurfaceAlphaMod(surface: *Surface, alpha: u8) bool {
        return @bitCast(c.SDL_SetSurfaceAlphaMod(@ptrCast(surface), alpha));
    }

    pub inline fn getSurfaceAlphaMod(surface: *Surface, alpha: [*c]u8) bool {
        return @bitCast(c.SDL_GetSurfaceAlphaMod(@ptrCast(surface), alpha));
    }

    pub inline fn setSurfaceBlendMode(surface: *Surface, blendMode: BlendMode) bool {
        return @bitCast(c.SDL_SetSurfaceBlendMode(@ptrCast(surface), @intFromEnum(blendMode)));
    }

    pub inline fn getSurfaceBlendMode(surface: *Surface, blendMode: ?*BlendMode) bool {
        return @bitCast(c.SDL_GetSurfaceBlendMode(@ptrCast(surface), @ptrCast(blendMode)));
    }

    pub inline fn setSurfaceClipRect(surface: *Surface, rect: ?*const Rect) bool {
        return @bitCast(c.SDL_SetSurfaceClipRect(@ptrCast(surface), @ptrCast(rect)));
    }

    pub inline fn getSurfaceClipRect(surface: *Surface, rect: ?*Rect) bool {
        return @bitCast(c.SDL_GetSurfaceClipRect(@ptrCast(surface), @ptrCast(rect)));
    }

    pub inline fn flipSurface(surface: *Surface, flip: FlipMode) bool {
        return @bitCast(c.SDL_FlipSurface(@ptrCast(surface), @intFromEnum(flip)));
    }

    pub inline fn duplicateSurface(surface: *Surface) ?*Surface {
        return @ptrCast(c.SDL_DuplicateSurface(@ptrCast(surface)));
    }

    pub inline fn scaleSurface(surface: *Surface, width: c_int, height: c_int, scaleMode: ScaleMode) ?*Surface {
        return @ptrCast(c.SDL_ScaleSurface(@ptrCast(surface), width, height, @intFromEnum(scaleMode)));
    }

    pub inline fn convertSurface(surface: *Surface, format: PixelFormat) ?*Surface {
        return @ptrCast(c.SDL_ConvertSurface(@ptrCast(surface), @bitCast(format)));
    }

    pub inline fn convertSurfaceAndColorspace(surface: *Surface, format: PixelFormat, palette: ?*Palette, colorspace: Colorspace, props: PropertiesID) ?*Surface {
        return @ptrCast(c.SDL_ConvertSurfaceAndColorspace(@ptrCast(surface), @bitCast(format), @ptrCast(palette), colorspace, props));
    }

    pub inline fn premultiplySurfaceAlpha(surface: *Surface, linear: bool) bool {
        return @bitCast(c.SDL_PremultiplySurfaceAlpha(@ptrCast(surface), @bitCast(linear)));
    }

    pub inline fn clearSurface(surface: *Surface, r: f32, g: f32, b: f32, a: f32) bool {
        return @bitCast(c.SDL_ClearSurface(@ptrCast(surface), r, g, b, a));
    }

    pub inline fn fillSurfaceRect(surface: *Surface, rect: ?*const Rect, color: u32) bool {
        return @bitCast(c.SDL_FillSurfaceRect(@ptrCast(surface), @ptrCast(rect), color));
    }

    pub inline fn fillSurfaceRects(surface: *Surface, rects: ?*const Rect, count: c_int, color: u32) bool {
        return @bitCast(c.SDL_FillSurfaceRects(@ptrCast(surface), @ptrCast(rects), count, color));
    }

    pub inline fn blitSurface(surface: *Surface, srcrect: ?*const Rect, dst: ?*Surface, dstrect: ?*const Rect) bool {
        return @bitCast(c.SDL_BlitSurface(@ptrCast(surface), @ptrCast(srcrect), @ptrCast(dst), @ptrCast(dstrect)));
    }

    pub inline fn blitSurfaceUnchecked(surface: *Surface, srcrect: ?*const Rect, dst: ?*Surface, dstrect: ?*const Rect) bool {
        return @bitCast(c.SDL_BlitSurfaceUnchecked(@ptrCast(surface), @ptrCast(srcrect), @ptrCast(dst), @ptrCast(dstrect)));
    }

    pub inline fn blitSurfaceScaled(surface: *Surface, srcrect: ?*const Rect, dst: ?*Surface, dstrect: ?*const Rect, scaleMode: ScaleMode) bool {
        return @bitCast(c.SDL_BlitSurfaceScaled(@ptrCast(surface), @ptrCast(srcrect), @ptrCast(dst), @ptrCast(dstrect), @intFromEnum(scaleMode)));
    }

    pub inline fn blitSurfaceUncheckedScaled(surface: *Surface, srcrect: ?*const Rect, dst: ?*Surface, dstrect: ?*const Rect, scaleMode: ScaleMode) bool {
        return @bitCast(c.SDL_BlitSurfaceUncheckedScaled(@ptrCast(surface), @ptrCast(srcrect), @ptrCast(dst), @ptrCast(dstrect), @intFromEnum(scaleMode)));
    }

    pub inline fn stretchSurface(surface: *Surface, srcrect: ?*const Rect, dst: ?*Surface, dstrect: ?*const Rect, scaleMode: ScaleMode) bool {
        return @bitCast(c.SDL_StretchSurface(@ptrCast(surface), @ptrCast(srcrect), @ptrCast(dst), @ptrCast(dstrect), @intFromEnum(scaleMode)));
    }

    pub inline fn blitSurfaceTiled(surface: *Surface, srcrect: ?*const Rect, dst: ?*Surface, dstrect: ?*const Rect) bool {
        return @bitCast(c.SDL_BlitSurfaceTiled(@ptrCast(surface), @ptrCast(srcrect), @ptrCast(dst), @ptrCast(dstrect)));
    }

    pub inline fn blitSurfaceTiledWithScale(surface: *Surface, srcrect: ?*const Rect, scale: f32, scaleMode: ScaleMode, dst: ?*Surface, dstrect: ?*const Rect) bool {
        return @bitCast(c.SDL_BlitSurfaceTiledWithScale(@ptrCast(surface), @ptrCast(srcrect), scale, @intFromEnum(scaleMode), @ptrCast(dst), @ptrCast(dstrect)));
    }

    pub inline fn blitSurface9Grid(surface: *Surface, srcrect: ?*const Rect, left_width: c_int, right_width: c_int, top_height: c_int, bottom_height: c_int, scale: f32, scaleMode: ScaleMode, dst: ?*Surface, dstrect: ?*const Rect) bool {
        return @bitCast(c.SDL_BlitSurface9Grid(@ptrCast(surface), @ptrCast(srcrect), left_width, right_width, top_height, bottom_height, scale, @intFromEnum(scaleMode), @ptrCast(dst), @ptrCast(dstrect)));
    }

    pub inline fn mapSurfaceRGB(surface: *Surface, r: u8, g: u8, b: u8) u32 {
        return c.SDL_MapSurfaceRGB(@ptrCast(surface), r, g, b);
    }

    pub inline fn mapSurfaceRGBA(surface: *Surface, r: u8, g: u8, b: u8, a: u8) u32 {
        return c.SDL_MapSurfaceRGBA(@ptrCast(surface), r, g, b, a);
    }

    pub inline fn readSurfacePixel(surface: *Surface, x: c_int, y: c_int, r: [*c]u8, g: [*c]u8, b: [*c]u8, a: [*c]u8) bool {
        return @bitCast(c.SDL_ReadSurfacePixel(@ptrCast(surface), x, y, r, g, b, a));
    }

    pub inline fn readSurfacePixelFloat(surface: *Surface, x: c_int, y: c_int, r: *f32, g: *f32, b: *f32, a: *f32) bool {
        return @bitCast(c.SDL_ReadSurfacePixelFloat(@ptrCast(surface), x, y, @ptrCast(r), @ptrCast(g), @ptrCast(b), @ptrCast(a)));
    }

    pub inline fn writeSurfacePixel(surface: *Surface, x: c_int, y: c_int, r: u8, g: u8, b: u8, a: u8) bool {
        return @bitCast(c.SDL_WriteSurfacePixel(@ptrCast(surface), x, y, r, g, b, a));
    }

    pub inline fn writeSurfacePixelFloat(surface: *Surface, x: c_int, y: c_int, r: f32, g: f32, b: f32, a: f32) bool {
        return @bitCast(c.SDL_WriteSurfacePixelFloat(@ptrCast(surface), x, y, r, g, b, a));
    }
};

pub inline fn createSurface(width: c_int, height: c_int, format: PixelFormat) ?*Surface {
    return @ptrCast(c.SDL_CreateSurface(width, height, @bitCast(format)));
}

pub inline fn createSurfaceFrom(width: c_int, height: c_int, format: PixelFormat, pixels: ?*anyopaque, pitch: c_int) ?*Surface {
    return @ptrCast(c.SDL_CreateSurfaceFrom(width, height, @bitCast(format), pixels, pitch));
}

pub inline fn loadBMP(file: [*c]const u8) ?*Surface {
    return @ptrCast(c.SDL_LoadBMP(file));
}

pub inline fn convertPixels(width: c_int, height: c_int, src_format: PixelFormat, src: ?*const anyopaque, src_pitch: c_int, dst_format: PixelFormat, dst: ?*anyopaque, dst_pitch: c_int) bool {
    return @bitCast(c.SDL_ConvertPixels(width, height, @bitCast(src_format), src, src_pitch, @bitCast(dst_format), dst, dst_pitch));
}

pub inline fn convertPixelsAndColorspace(width: c_int, height: c_int, src_format: PixelFormat, src_colorspace: Colorspace, src_properties: PropertiesID, src: ?*const anyopaque, src_pitch: c_int, dst_format: PixelFormat, dst_colorspace: Colorspace, dst_properties: PropertiesID, dst: ?*anyopaque, dst_pitch: c_int) bool {
    return @bitCast(c.SDL_ConvertPixelsAndColorspace(width, height, @bitCast(src_format), src_colorspace, src_properties, src, src_pitch, @bitCast(dst_format), dst_colorspace, dst_properties, dst, dst_pitch));
}

pub inline fn premultiplyAlpha(width: c_int, height: c_int, src_format: PixelFormat, src: ?*const anyopaque, src_pitch: c_int, dst_format: PixelFormat, dst: ?*anyopaque, dst_pitch: c_int, linear: bool) bool {
    return @bitCast(c.SDL_PremultiplyAlpha(width, height, @bitCast(src_format), src, src_pitch, @bitCast(dst_format), dst, dst_pitch, @bitCast(linear)));
}
