/*
 * Copyright (C) 2019 Richard Hughes <richard@hughsie.com>
 *
 * SPDX-License-Identifier: LGPL-2.1+
 */

#pragma once

#include <fwupd.h>
#include <xmlb.h>

#include "fu-chunk.h"
#include "fu-firmware.h"

#define FU_TYPE_FIRMWARE (fu_firmware_get_type())
G_DECLARE_DERIVABLE_TYPE(FuFirmware, fu_firmware, FU, FIRMWARE, GObject)

/**
 * FU_FIRMWARE_EXPORT_FLAG_NONE:
 *
 * No flags set.
 *
 * Since: 1.6.0
 **/
#define FU_FIRMWARE_EXPORT_FLAG_NONE (0u)
/**
 * FU_FIRMWARE_EXPORT_FLAG_INCLUDE_DEBUG:
 *
 * Include debug information when exporting.
 *
 * Since: 1.6.0
 **/
#define FU_FIRMWARE_EXPORT_FLAG_INCLUDE_DEBUG (1u << 0)
/**
 * FU_FIRMWARE_EXPORT_FLAG_ASCII_DATA:
 *
 * Write the data as UTF-8 strings.
 *
 * Since: 1.6.0
 **/
#define FU_FIRMWARE_EXPORT_FLAG_ASCII_DATA (1u << 1)

/**
 * FuFirmwareExportFlags:
 *
 * The firmware export flags.
 **/
typedef guint64 FuFirmwareExportFlags;

struct _FuFirmwareClass {
	GObjectClass parent_class;
	gboolean (*parse)(FuFirmware *self,
			  GBytes *fw,
			  gsize offset,
			  FwupdInstallFlags flags,
			  GError **error) G_GNUC_WARN_UNUSED_RESULT;
	GBytes *(*write)(FuFirmware *self, GError **error)G_GNUC_WARN_UNUSED_RESULT;
	void (*export)(FuFirmware *self, FuFirmwareExportFlags flags, XbBuilderNode *bn);
	gboolean (*tokenize)(FuFirmware *self, GBytes *fw, FwupdInstallFlags flags, GError **error)
	    G_GNUC_WARN_UNUSED_RESULT;
	gboolean (*build)(FuFirmware *self, XbNode *n, GError **error) G_GNUC_WARN_UNUSED_RESULT;
	gchar *(*get_checksum)(FuFirmware *self,
			       GChecksumType csum_kind,
			       GError **error)G_GNUC_WARN_UNUSED_RESULT;
	gboolean (*check_magic)(FuFirmware *self, GBytes *fw, gsize offset, GError **error);
	gboolean (*check_compatible)(FuFirmware *self,
				     FuFirmware *other,
				     FwupdInstallFlags flags,
				     GError **error);
	/*< private >*/
	gpointer padding[24];
};

/**
 * FU_FIRMWARE_FLAG_NONE:
 *
 * No flags set.
 *
 * Since: 1.5.0
 **/
#define FU_FIRMWARE_FLAG_NONE (0u)
/**
 * FU_FIRMWARE_FLAG_DEDUPE_ID:
 *
 * Dedupe imges by ID.
 *
 * Since: 1.5.0
 **/
#define FU_FIRMWARE_FLAG_DEDUPE_ID (1u << 0)
/**
 * FU_FIRMWARE_FLAG_DEDUPE_IDX:
 *
 * Dedupe imges by IDX.
 *
 * Since: 1.5.0
 **/
#define FU_FIRMWARE_FLAG_DEDUPE_IDX (1u << 1)
/**
 * FU_FIRMWARE_FLAG_HAS_CHECKSUM:
 *
 * Has a CRC or checksum to test internal consistency.
 *
 * Since: 1.5.6
 **/
#define FU_FIRMWARE_FLAG_HAS_CHECKSUM (1u << 2)
/**
 * FU_FIRMWARE_FLAG_HAS_VID_PID:
 *
 * Has a vendor or product ID in the firmware.
 *
 * Since: 1.5.6
 **/
#define FU_FIRMWARE_FLAG_HAS_VID_PID (1u << 3)
/**
 * FU_FIRMWARE_FLAG_DONE_PARSE:
 *
 * The firmware object has been used by fu_firmware_parse_full().
 *
 * Since: 1.7.3
 **/
#define FU_FIRMWARE_FLAG_DONE_PARSE (1u << 4)
/**
 * FU_FIRMWARE_FLAG_HAS_STORED_SIZE:
 *
 * Encodes the image size in the firmware.
 *
 * Since: 1.8.2
 **/
#define FU_FIRMWARE_FLAG_HAS_STORED_SIZE (1u << 5)

/**
 * FuFirmwareFlags:
 *
 * The firmware flags.
 **/
typedef guint64 FuFirmwareFlags;

/**
 * FU_FIRMWARE_ID_PAYLOAD:
 *
 * The usual firmware ID string for the payload.
 *
 * Since: 1.6.0
 **/
#define FU_FIRMWARE_ID_PAYLOAD "payload"
/**
 * FU_FIRMWARE_ID_SIGNATURE:
 *
 * The usual firmware ID string for the signature.
 *
 * Since: 1.6.0
 **/
#define FU_FIRMWARE_ID_SIGNATURE "signature"
/**
 * FU_FIRMWARE_ID_HEADER:
 *
 * The usual firmware ID string for the header.
 *
 * Since: 1.6.0
 **/
#define FU_FIRMWARE_ID_HEADER "header"

#define FU_FIRMWARE_ALIGNMENT_1	   0x00
#define FU_FIRMWARE_ALIGNMENT_2	   0x01
#define FU_FIRMWARE_ALIGNMENT_4	   0x02
#define FU_FIRMWARE_ALIGNMENT_8	   0x03
#define FU_FIRMWARE_ALIGNMENT_16   0x04
#define FU_FIRMWARE_ALIGNMENT_32   0x05
#define FU_FIRMWARE_ALIGNMENT_64   0x06
#define FU_FIRMWARE_ALIGNMENT_128  0x07
#define FU_FIRMWARE_ALIGNMENT_256  0x08
#define FU_FIRMWARE_ALIGNMENT_512  0x09
#define FU_FIRMWARE_ALIGNMENT_1K   0x0A
#define FU_FIRMWARE_ALIGNMENT_2K   0x0B
#define FU_FIRMWARE_ALIGNMENT_4K   0x0C
#define FU_FIRMWARE_ALIGNMENT_8K   0x0D
#define FU_FIRMWARE_ALIGNMENT_16K  0x0E
#define FU_FIRMWARE_ALIGNMENT_32K  0x0F
#define FU_FIRMWARE_ALIGNMENT_64K  0x10
#define FU_FIRMWARE_ALIGNMENT_128K 0x11
#define FU_FIRMWARE_ALIGNMENT_256K 0x12
#define FU_FIRMWARE_ALIGNMENT_512K 0x13
#define FU_FIRMWARE_ALIGNMENT_1M   0x14
#define FU_FIRMWARE_ALIGNMENT_2M   0x15
#define FU_FIRMWARE_ALIGNMENT_4M   0x16
#define FU_FIRMWARE_ALIGNMENT_8M   0x17
#define FU_FIRMWARE_ALIGNMENT_16M  0x18
#define FU_FIRMWARE_ALIGNMENT_32M  0x19
#define FU_FIRMWARE_ALIGNMENT_64M  0x1A
#define FU_FIRMWARE_ALIGNMENT_128M 0x1B
#define FU_FIRMWARE_ALIGNMENT_256M 0x1C
#define FU_FIRMWARE_ALIGNMENT_512M 0x1D
#define FU_FIRMWARE_ALIGNMENT_1G   0x1E
#define FU_FIRMWARE_ALIGNMENT_2G   0x1F
#define FU_FIRMWARE_ALIGNMENT_4G   0x20

const gchar *
fu_firmware_flag_to_string(FuFirmwareFlags flag);
FuFirmwareFlags
fu_firmware_flag_from_string(const gchar *flag);

FuFirmware *
fu_firmware_new(void);
FuFirmware *
fu_firmware_new_from_bytes(GBytes *fw);
FuFirmware *
fu_firmware_new_from_gtypes(GBytes *fw, FwupdInstallFlags flags, GError **error, ...);
gchar *
fu_firmware_to_string(FuFirmware *self);
void
fu_firmware_export(FuFirmware *self, FuFirmwareExportFlags flags, XbBuilderNode *bn);
gchar *
fu_firmware_export_to_xml(FuFirmware *self, FuFirmwareExportFlags flags, GError **error);
const gchar *
fu_firmware_get_version(FuFirmware *self);
void
fu_firmware_set_version(FuFirmware *self, const gchar *version);
guint64
fu_firmware_get_version_raw(FuFirmware *self);
void
fu_firmware_set_version_raw(FuFirmware *self, guint64 version_raw);
void
fu_firmware_add_flag(FuFirmware *firmware, FuFirmwareFlags flag);
gboolean
fu_firmware_has_flag(FuFirmware *firmware, FuFirmwareFlags flag);
const gchar *
fu_firmware_get_filename(FuFirmware *self);
void
fu_firmware_set_filename(FuFirmware *self, const gchar *filename);
const gchar *
fu_firmware_get_id(FuFirmware *self);
void
fu_firmware_set_id(FuFirmware *self, const gchar *id);
guint64
fu_firmware_get_addr(FuFirmware *self);
void
fu_firmware_set_addr(FuFirmware *self, guint64 addr);
guint64
fu_firmware_get_offset(FuFirmware *self);
void
fu_firmware_set_offset(FuFirmware *self, guint64 offset);
gsize
fu_firmware_get_size(FuFirmware *self);
void
fu_firmware_set_size(FuFirmware *self, gsize size);
guint64
fu_firmware_get_idx(FuFirmware *self);
void
fu_firmware_set_idx(FuFirmware *self, guint64 idx);
GBytes *
fu_firmware_get_bytes(FuFirmware *self, GError **error);
GBytes *
fu_firmware_get_bytes_with_patches(FuFirmware *self, GError **error);
void
fu_firmware_set_bytes(FuFirmware *self, GBytes *bytes);
guint8
fu_firmware_get_alignment(FuFirmware *self);
void
fu_firmware_set_alignment(FuFirmware *self, guint8 alignment);
void
fu_firmware_add_chunk(FuFirmware *self, FuChunk *chk);
GPtrArray *
fu_firmware_get_chunks(FuFirmware *self, GError **error);
FuFirmware *
fu_firmware_get_parent(FuFirmware *self);
void
fu_firmware_set_parent(FuFirmware *self, FuFirmware *parent);

gboolean
fu_firmware_tokenize(FuFirmware *self, GBytes *fw, FwupdInstallFlags flags, GError **error)
    G_GNUC_WARN_UNUSED_RESULT;
gboolean
fu_firmware_build(FuFirmware *self, XbNode *n, GError **error) G_GNUC_WARN_UNUSED_RESULT;
gboolean
fu_firmware_build_from_xml(FuFirmware *self,
			   const gchar *xml,
			   GError **error) G_GNUC_WARN_UNUSED_RESULT;
gboolean
fu_firmware_parse(FuFirmware *self, GBytes *fw, FwupdInstallFlags flags, GError **error)
    G_GNUC_WARN_UNUSED_RESULT;
gboolean
fu_firmware_parse_file(FuFirmware *self, GFile *file, FwupdInstallFlags flags, GError **error)
    G_GNUC_WARN_UNUSED_RESULT;
gboolean
fu_firmware_parse_full(FuFirmware *self,
		       GBytes *fw,
		       gsize offset,
		       FwupdInstallFlags flags,
		       GError **error) G_GNUC_WARN_UNUSED_RESULT;
GBytes *
fu_firmware_write(FuFirmware *self, GError **error) G_GNUC_WARN_UNUSED_RESULT;
GBytes *
fu_firmware_write_chunk(FuFirmware *self, guint64 address, guint64 chunk_sz_max, GError **error)
    G_GNUC_WARN_UNUSED_RESULT;
gboolean
fu_firmware_write_file(FuFirmware *self, GFile *file, GError **error) G_GNUC_WARN_UNUSED_RESULT;
gchar *
fu_firmware_get_checksum(FuFirmware *self, GChecksumType csum_kind, GError **error);
gboolean
fu_firmware_check_compatible(FuFirmware *self,
			     FuFirmware *other,
			     FwupdInstallFlags flags,
			     GError **error);

void
fu_firmware_add_image(FuFirmware *self, FuFirmware *img);
gboolean
fu_firmware_remove_image(FuFirmware *self, FuFirmware *img, GError **error);
gboolean
fu_firmware_remove_image_by_idx(FuFirmware *self, guint64 idx, GError **error);
gboolean
fu_firmware_remove_image_by_id(FuFirmware *self, const gchar *id, GError **error);
GPtrArray *
fu_firmware_get_images(FuFirmware *self);
FuFirmware *
fu_firmware_get_image_by_id(FuFirmware *self, const gchar *id, GError **error);
GBytes *
fu_firmware_get_image_by_id_bytes(FuFirmware *self, const gchar *id, GError **error);
FuFirmware *
fu_firmware_get_image_by_idx(FuFirmware *self, guint64 idx, GError **error);
GBytes *
fu_firmware_get_image_by_idx_bytes(FuFirmware *self, guint64 idx, GError **error);
FuFirmware *
fu_firmware_get_image_by_checksum(FuFirmware *self, const gchar *checksum, GError **error);
void
fu_firmware_add_patch(FuFirmware *self, gsize offset, GBytes *blob);
