const std = @import("std");
const c = @import("c.zig");
const BitmapGlyph = @import("BitmapGlyph.zig");
const Stroker = @import("Stroker.zig");
const types = @import("types.zig");
const Error = @import("error.zig").Error;
const intToError = @import("error.zig").intToError;

const Glyph = @This();

pub const BBoxMode = enum(u2) {
    // https://freetype.org/freetype2/docs/reference/ft2-glyph_management.html#ft_glyph_bbox_mode
    // both `unscaled` and `subpixel` constants are set to 0
    unscaled_or_subpixels = c.FT_GLYPH_BBOX_UNSCALED,
    gridfit = c.FT_GLYPH_BBOX_GRIDFIT,
    truncate = c.FT_GLYPH_BBOX_TRUNCATE,
    pixels = c.FT_GLYPH_BBOX_PIXELS,
};
pub const GlyphMetrics = c.FT_Glyph_Metrics;
pub const GlyphFormat = enum(u32) {
    none = c.FT_GLYPH_FORMAT_NONE,
    composite = c.FT_GLYPH_FORMAT_COMPOSITE,
    bitmap = c.FT_GLYPH_FORMAT_BITMAP,
    outline = c.FT_GLYPH_FORMAT_OUTLINE,
    plotter = c.FT_GLYPH_FORMAT_PLOTTER,
    svg = c.FT_GLYPH_FORMAT_SVG,
};

handle: c.FT_Glyph,

pub fn init(handle: c.FT_Glyph) Glyph {
    return Glyph{ .handle = handle };
}

pub fn deinit(self: Glyph) void {
    c.FT_Done_Glyph(self.handle);
}

pub fn clone(self: Glyph) Error!Glyph {
    var res = std.mem.zeroes(c.FT_Glyph);
    try intToError(c.FT_Glyph_Copy(self.handle, &res));
    return Glyph.init(res);
}

pub fn transform(self: Glyph, matrix: ?types.Matrix, delta: ?types.Vector) Error!void {
    var m = matrix orelse std.mem.zeroes(types.Matrix);
    var d = delta orelse std.mem.zeroes(types.Vector);
    try intToError(c.FT_Glyph_Transform(self.handle, @ptrCast(*c.FT_Matrix, &m), @ptrCast(*c.FT_Vector, &d)));
}

pub fn getCBox(self: Glyph, bbox_mode: BBoxMode) types.BBox {
    var res = std.mem.zeroes(types.BBox);
    c.FT_Glyph_Get_CBox(self.handle, @enumToInt(bbox_mode), @ptrCast(*c.FT_BBox, &res));
    return res;
}

pub fn toBitmap(self: Glyph, render_mode: types.RenderMode, origin: ?types.Vector) Error!BitmapGlyph {
    var res = self.handle;
    var o = origin orelse std.mem.zeroes(types.Vector);
    try intToError(c.FT_Glyph_To_Bitmap(&res, @enumToInt(render_mode), @ptrCast(*c.FT_Vector, &o), 0));
    return BitmapGlyph.init(@ptrCast(c.FT_BitmapGlyph, self.handle));
}

pub fn stroke(self: Glyph, stroker: Stroker) Error!Glyph {
    var res = self.handle;
    try intToError(c.FT_Glyph_Stroke(&res, stroker.handle, 0));
    return Glyph.init(res);
}

pub fn strokeBorder(self: Glyph, stroker: Stroker, inside: bool) Error!Glyph {
    var res = self.handle;
    try intToError(c.FT_Glyph_StrokeBorder(&res, stroker.handle, if (inside) 1 else 0, 0));
    return Glyph.init(res);
}

pub fn format(self: Glyph) GlyphFormat {
    return @intToEnum(GlyphFormat, self.handle.*.format);
}

pub fn advanceX(self: Glyph) isize {
    return self.handle.*.advance.x;
}

pub fn advanceY(self: Glyph) isize {
    return self.handle.*.advance.y;
}
