pub const ext = @import("ext.zig");
const gcab = @This();

const std = @import("std");
const compat = @import("compat");
const gio = @import("gio2");
const gobject = @import("gobject2");
const glib = @import("glib2");
const gmodule = @import("gmodule2");
/// An opaque object holding a Cabinet file reference.
pub const Cabinet = opaque {
    pub const Parent = gobject.Object;
    pub const Implements = [_]type{};
    pub const Class = gcab.CabinetClass;
    pub const virtual_methods = struct {};

    pub const properties = struct {
        pub const reserved = struct {
            pub const name = "reserved";

            pub const Type = ?[*]u8;
        };

        pub const signature = struct {
            pub const name = "signature";

            pub const Type = ?[*]u8;
        };
    };

    pub const signals = struct {};

    /// Create a new `gcab.Cabinet` object to read or create a Cabinet
    /// archive.
    extern fn gcab_cabinet_new() *gcab.Cabinet;
    pub const new = gcab_cabinet_new;

    /// Adds a compression kind to the allow-list. By default, GCab will use all decompression support
    /// compiled in at build time. Once this function has been called only specific compression kinds
    /// will be used in functions like `gcab.Cabinet.load`.
    extern fn gcab_cabinet_add_allowed_compression(p_self: *Cabinet, p_compression: gcab.Compression) void;
    pub const addAllowedCompression = gcab_cabinet_add_allowed_compression;

    /// Add `folder` to `cabinet`.
    extern fn gcab_cabinet_add_folder(p_cabinet: *Cabinet, p_folder: *gcab.Folder, p_error: ?*?*glib.Error) c_int;
    pub const addFolder = gcab_cabinet_add_folder;

    /// Extract files to given path.
    ///
    /// If `path` is NULL then the files are decompressed to memory blobs stored on
    /// each `gcab.File`.
    extern fn gcab_cabinet_extract(p_cabinet: *Cabinet, p_path: ?*gio.File, p_file_callback: ?gcab.FileCallback, p_progress_callback: ?gio.FileProgressCallback, p_user_data: ?*anyopaque, p_cancellable: ?*gio.Cancellable, p_error: ?*?*glib.Error) c_int;
    pub const extract = gcab_cabinet_extract;

    /// Extract files to given path.
    extern fn gcab_cabinet_extract_simple(p_cabinet: *Cabinet, p_path: *gio.File, p_file_callback: ?gcab.FileCallback, p_user_data: ?*anyopaque, p_cancellable: ?*gio.Cancellable, p_error: ?*?*glib.Error) c_int;
    pub const extractSimple = gcab_cabinet_extract_simple;

    /// Get the Cabinet folders within the `cabinet`.
    /// Note that Cabinet folders are not like filesystem path, they are
    /// group of files sharing some layout parameters.
    extern fn gcab_cabinet_get_folders(p_cabinet: *Cabinet) *glib.PtrArray;
    pub const getFolders = gcab_cabinet_get_folders;

    /// Lookup the cabinet authenticode signature if any.
    extern fn gcab_cabinet_get_signature(p_cabinet: *Cabinet, p_cancellable: ?*gio.Cancellable, p_error: ?*?*glib.Error) ?*const glib.ByteArray;
    pub const getSignature = gcab_cabinet_get_signature;

    /// Get the size of the compressed cabinet file.
    extern fn gcab_cabinet_get_size(p_cabinet: *Cabinet) u32;
    pub const getSize = gcab_cabinet_get_size;

    /// Load a cabinet archive.
    extern fn gcab_cabinet_load(p_cabinet: *Cabinet, p_stream: *gio.InputStream, p_cancellable: ?*gio.Cancellable, p_error: ?*?*glib.Error) c_int;
    pub const load = gcab_cabinet_load;

    /// Save `cabinet` to the output stream `out`. `out` must be a `gio.Seekable`.
    extern fn gcab_cabinet_write(p_cabinet: *Cabinet, p_stream: *gio.OutputStream, p_file_callback: ?gcab.FileCallback, p_progress_callback: ?gio.FileProgressCallback, p_user_data: ?*anyopaque, p_cancellable: ?*gio.Cancellable, p_error: ?*?*glib.Error) c_int;
    pub const write = gcab_cabinet_write;

    /// Save `cabinet` to the output stream `out`. `out` must be a `gio.Seekable`.
    extern fn gcab_cabinet_write_simple(p_cabinet: *Cabinet, p_stream: *gio.OutputStream, p_file_callback: ?gcab.FileCallback, p_user_data: ?*anyopaque, p_cancellable: ?*gio.Cancellable, p_error: ?*?*glib.Error) c_int;
    pub const writeSimple = gcab_cabinet_write_simple;

    extern fn gcab_cabinet_get_type() usize;
    pub const getGObjectType = gcab_cabinet_get_type;

    extern fn g_object_ref(p_self: *gcab.Cabinet) void;
    pub const ref = g_object_ref;

    extern fn g_object_unref(p_self: *gcab.Cabinet) void;
    pub const unref = g_object_unref;

    pub fn as(p_instance: *Cabinet, comptime P_T: type) *P_T {
        return gobject.ext.as(P_T, p_instance);
    }

    test {
        @setEvalBranchQuota(100_000);
        std.testing.refAllDecls(@This());
    }
};

/// An opaque object, referencing a file in a Cabinet.
pub const File = opaque {
    pub const Parent = gobject.Object;
    pub const Implements = [_]type{};
    pub const Class = gcab.FileClass;
    pub const virtual_methods = struct {};

    pub const properties = struct {
        pub const bytes = struct {
            pub const name = "bytes";

            pub const Type = ?*glib.Bytes;
        };

        pub const file = struct {
            pub const name = "file";

            pub const Type = ?*gio.File;
        };

        pub const name = struct {
            pub const name = "name";

            pub const Type = ?[*:0]u8;
        };
    };

    pub const signals = struct {};

    /// Create a `gcab.File` from a given `glib.Bytes`.
    ///
    /// If this file is to be added to an archive you should also probably use
    /// `gcab.File.setDate` and `gcab.File.setAttributes` to set sensible values.
    extern fn gcab_file_new_with_bytes(p_name: [*:0]const u8, p_bytes: *glib.Bytes) *gcab.File;
    pub const newWithBytes = gcab_file_new_with_bytes;

    /// Create a `gcab.File` from a given `gio.File`, to be added to a
    /// `gcab.Cabinet` for archive creation.
    extern fn gcab_file_new_with_file(p_name: [*:0]const u8, p_file: *gio.File) *gcab.File;
    pub const newWithFile = gcab_file_new_with_file;

    /// Get the file attributes.
    extern fn gcab_file_get_attributes(p_file: *File) u32;
    pub const getAttributes = gcab_file_get_attributes;

    /// Get the `gio.File` associated with `file`. This will only be non-`NULL` if the
    /// `gcab.File` has been created using `gcab.File.newWithBytes`.
    extern fn gcab_file_get_bytes(p_file: *File) *glib.Bytes;
    pub const getBytes = gcab_file_get_bytes;

    /// Get the file date, in `result`.
    extern fn gcab_file_get_date(p_file: *File, p_result: *glib.TimeVal) c_int;
    pub const getDate = gcab_file_get_date;

    /// Gets the file date and returns it as a `glib.DateTime`..
    extern fn gcab_file_get_date_time(p_file: *File) *glib.DateTime;
    pub const getDateTime = gcab_file_get_date_time;

    /// Get the file name to use for extraction, or `NULL`.
    extern fn gcab_file_get_extract_name(p_file: *File) ?[*:0]const u8;
    pub const getExtractName = gcab_file_get_extract_name;

    /// If the cabinet is being created, get the `gio.File` associated with
    /// `file`. This must be an exisiting file that can be read, in order to
    /// be added to the archive during cabinet creation.
    ///
    /// If `file` is from an existing cabinet, the fuction will return
    /// `NULL`.
    extern fn gcab_file_get_file(p_file: *File) *gio.File;
    pub const getFile = gcab_file_get_file;

    /// Get the file name within the cabinet.
    extern fn gcab_file_get_name(p_file: *File) [*:0]const u8;
    pub const getName = gcab_file_get_name;

    /// Get the file size.
    extern fn gcab_file_get_size(p_file: *File) u32;
    pub const getSize = gcab_file_get_size;

    /// Set the file attributes.
    extern fn gcab_file_set_attributes(p_file: *File, p_attr: u32) void;
    pub const setAttributes = gcab_file_set_attributes;

    /// Replace the `glib.Bytes` associated with `self`.
    /// This is most usefule when the `gcab.File` has been created using
    /// `gcab.File.newWithBytes` and the data needs to be modified.
    extern fn gcab_file_set_bytes(p_file: *File, p_bytes: *glib.Bytes) void;
    pub const setBytes = gcab_file_set_bytes;

    /// Sets the file modification date, instead of the value provided by the GFile.
    extern fn gcab_file_set_date(p_file: *File, p_tv: *const glib.TimeVal) void;
    pub const setDate = gcab_file_set_date;

    /// Sets the file modification date (instead of the date provided by the GFile)
    extern fn gcab_file_set_date_time(p_file: *File, p_dt: *glib.DateTime) void;
    pub const setDateTime = gcab_file_set_date_time;

    /// Sets the file name to use for extraction, instead of the name
    /// provided by the Cabinet.
    extern fn gcab_file_set_extract_name(p_file: *File, p_name: ?[*:0]const u8) void;
    pub const setExtractName = gcab_file_set_extract_name;

    extern fn gcab_file_get_type() usize;
    pub const getGObjectType = gcab_file_get_type;

    extern fn g_object_ref(p_self: *gcab.File) void;
    pub const ref = g_object_ref;

    extern fn g_object_unref(p_self: *gcab.File) void;
    pub const unref = g_object_unref;

    pub fn as(p_instance: *File, comptime P_T: type) *P_T {
        return gobject.ext.as(P_T, p_instance);
    }

    test {
        @setEvalBranchQuota(100_000);
        std.testing.refAllDecls(@This());
    }
};

/// An opaque object, referencing a folder in a Cabinet.
pub const Folder = opaque {
    pub const Parent = gobject.Object;
    pub const Implements = [_]type{};
    pub const Class = gcab.FolderClass;
    pub const virtual_methods = struct {};

    pub const properties = struct {
        pub const compression = struct {
            pub const name = "compression";

            pub const Type = gcab.Compression;
        };

        pub const comptype = struct {
            pub const name = "comptype";

            pub const Type = c_int;
        };

        pub const reserved = struct {
            pub const name = "reserved";

            pub const Type = ?[*]u8;
        };
    };

    pub const signals = struct {};

    /// Creates a new empty Cabinet folder. Use `gcab.Folder.addFile` to
    /// add files to an archive.
    ///
    /// A Cabinet folder is not a file path, it is a container for files.
    extern fn gcab_folder_new(p_comptype: c_int) *gcab.Folder;
    pub const new = gcab_folder_new;

    /// Add `file` to the `gcab.Folder`.
    extern fn gcab_folder_add_file(p_cabfolder: *Folder, p_cabfile: *gcab.File, p_recurse: c_int, p_cancellable: ?*gio.Cancellable, p_error: ?*?*glib.Error) c_int;
    pub const addFile = gcab_folder_add_file;

    /// Returns the compression used in this folder.
    extern fn gcab_folder_get_comptype(p_cabfolder: *Folder) c_int;
    pub const getComptype = gcab_folder_get_comptype;

    /// Gets a specific `gcab.File` files contained in the `cabfolder`.
    extern fn gcab_folder_get_file_by_name(p_cabfolder: *Folder, p_name: [*:0]const u8) *gcab.File;
    pub const getFileByName = gcab_folder_get_file_by_name;

    /// Get the list of `gcab.File` files contained in the `cabfolder`.
    extern fn gcab_folder_get_files(p_cabfolder: *Folder) *glib.SList;
    pub const getFiles = gcab_folder_get_files;

    /// Get the number of files in this `folder`.
    extern fn gcab_folder_get_nfiles(p_cabfolder: *Folder) c_uint;
    pub const getNfiles = gcab_folder_get_nfiles;

    extern fn gcab_folder_get_type() usize;
    pub const getGObjectType = gcab_folder_get_type;

    extern fn g_object_ref(p_self: *gcab.Folder) void;
    pub const ref = g_object_ref;

    extern fn g_object_unref(p_self: *gcab.Folder) void;
    pub const unref = g_object_unref;

    pub fn as(p_instance: *Folder, comptime P_T: type) *P_T {
        return gobject.ext.as(P_T, p_instance);
    }

    test {
        @setEvalBranchQuota(100_000);
        std.testing.refAllDecls(@This());
    }
};

pub const CabinetClass = extern struct {
    pub const Instance = gcab.Cabinet;

    f_parent_class: gobject.ObjectClass,

    pub fn as(p_instance: *CabinetClass, comptime P_T: type) *P_T {
        return gobject.ext.as(P_T, p_instance);
    }

    test {
        @setEvalBranchQuota(100_000);
        std.testing.refAllDecls(@This());
    }
};

pub const FileClass = extern struct {
    pub const Instance = gcab.File;

    f_parent_class: gobject.ObjectClass,

    pub fn as(p_instance: *FileClass, comptime P_T: type) *P_T {
        return gobject.ext.as(P_T, p_instance);
    }

    test {
        @setEvalBranchQuota(100_000);
        std.testing.refAllDecls(@This());
    }
};

pub const FolderClass = extern struct {
    pub const Instance = gcab.Folder;

    f_parent_class: gobject.ObjectClass,

    pub fn as(p_instance: *FolderClass, comptime P_T: type) *P_T {
        return gobject.ext.as(P_T, p_instance);
    }

    test {
        @setEvalBranchQuota(100_000);
        std.testing.refAllDecls(@This());
    }
};

/// Compression used by the `gcab.Folder`.
pub const Compression = enum(c_int) {
    none = 0,
    mszip = 1,
    quantum = 2,
    lzx = 3,
    mask = 15,
    _,

    extern fn gcab_compression_get_type() usize;
    pub const getGObjectType = gcab_compression_get_type;

    test {
        @setEvalBranchQuota(100_000);
        std.testing.refAllDecls(@This());
    }
};

/// The various errors triggered by the GCab functions.
pub const Error = enum(c_int) {
    format = 0,
    failed = 1,
    not_supported = 2,
    invalid_data = 3,
    _,

    test {
        @setEvalBranchQuota(100_000);
        std.testing.refAllDecls(@This());
    }
};

/// Attributes associated with the `gcab.File`.
pub const FileAttribute = enum(c_int) {
    rdonly = 1,
    hidden = 2,
    system = 4,
    arch = 32,
    exec = 64,
    name_is_utf = 128,
    _,

    extern fn gcab_file_attribute_get_type() usize;
    pub const getGObjectType = gcab_file_attribute_get_type;

    test {
        @setEvalBranchQuota(100_000);
        std.testing.refAllDecls(@This());
    }
};

extern fn gcab_error_quark() glib.Quark;
pub const errorQuark = gcab_error_quark;

/// The type used for callback called when processing Cabinet archive
/// files.
pub const FileCallback = *const fn (p_file: *gcab.File, p_user_data: ?*anyopaque) callconv(.c) c_int;

test {
    @setEvalBranchQuota(100_000);
    std.testing.refAllDecls(@This());
    std.testing.refAllDecls(ext);
}
