#ifndef TPM2_COMMON_H
#define TPM2_COMMON_H

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <errno.h>
#include <string.h>
#include <dlfcn.h>
#include <sys/utsname.h>

#include <tss2/tss2_common.h>
#include <tss2/tss2_sys.h>
#include <tss2/tss2_tpm2_types.h>
#include <tss2/tss2_tcti_device.h>
#include <tss2/tss2_tcti_mssim.h>
// #include <tss2/tss2_mu.h>
#include <systemd/sd-journal.h>

#ifdef __cplusplus
extern "C" {
#endif

#define BUFFER_SIZE(type, field) (sizeof((((type *)NULL)->field)))

#define TPM2B_TYPE_INIT(type, field) { .size = BUFFER_SIZE(type, field), }
#define TPM2B_INIT(xsize) { .size = xsize, }
#define TPM2B_EMPTY_INIT TPM2B_INIT(0)
#define TPM2B_SENSITIVE_CREATE_EMPTY_INIT { \
           .sensitive = { \
                .data.size = 0, \
                .userAuth.size = 0, \
            }, \
    }

#define TPMS_AUTH_COMMAND_INIT(session_handle) { \
        .sessionHandle = session_handle,\
	    .nonce = TPM2B_EMPTY_INIT, \
	    .sessionAttributes = 0, \
	    .hmac = TPM2B_EMPTY_INIT \
    }

#define TPMS_AUTH_COMMAND_EMPTY_INIT TPMS_AUTH_COMMAND_INIT(0)


#define TPMT_TK_CREATION_EMPTY_INIT { \
        .tag = 0, \
		.hierarchy = 0, \
		.digest = TPM2B_EMPTY_INIT \
    }

#define TPML_PCR_SELECTION_EMPTY_INIT { \
        .count = 0, \
    }

#define TPMS_CAPABILITY_DATA_EMPTY_INIT { \
        .capability = 0, \
    }

#define TPMT_TK_HASHCHECK_EMPTY_INIT { \
		.tag = 0, \
		.hierarchy = 0, \
		.digest = TPM2B_EMPTY_INIT \
    }

#define TSS2L_SYS_AUTH_COMMAND_INIT(cnt, array) { \
        .count = cnt, \
        .auths = array, \
    }


#define TSS2_RETRY_EXP(expression)                         \
    ({                                                     \
        TSS2_RC __result = 0;                              \
        do {                                               \
            __result = (expression);                       \
        } while ((__result & 0x0000ffff) == TPM2_RC_RETRY); \
        __result;                                          \
    })

#define TC_DEVICE_MARK "dev"
#define TC_TABRMD_MARK "tabrmd"
#define TSS2_TCTI_TABRMD "/usr/lib/%s-linux-gnu/libtss2-tcti-tabrmd.so.0"
#define TSS2_TCTI_INFO_SYMBOL "Tss2_Tcti_Info"

typedef enum {
    UNKNOWN_TCTI,
    DEVICE_TCTI,
    SOCKET_TCTI,
    N_TCTI,
} TCTI_TYPE;

typedef struct {
    TCTI_TYPE tcti_type;
    char *device_file;
    char *socket_address;
    uint16_t socket_port;
}OPTIONS;

/* TPM2 Device Init */
TSS2_TCTI_CONTEXT *
tcti_device_init(char const *device_path);

TSS2_TCTI_CONTEXT *
tcti_tabrmd_init(const char *name);

TSS2_SYS_CONTEXT *
sapi_init_from_tcti_ctx(TSS2_TCTI_CONTEXT *tcti_ctx);

TSS2_SYS_CONTEXT *
sapi_init_from_opts(OPTIONS *opts);

/* TPM2 Init */
TSS2_SYS_CONTEXT *
tpm2_init(uint8_t *device_name);

/* TPM2 Free */
TSS2_RC 
tpm2_free(TSS2_SYS_CONTEXT *sapi_context);

/* TPM2 Flush context */
TSS2_RC
tpm2_flush_context(
    TSS2_SYS_CONTEXT        *sapi_context,
    TPMI_DH_CONTEXT          flush_handle);

/*  setup algo details  */
int
setup_createprimary_alg(TPM2B_PUBLIC *public_info);

int
setup_create_alg(TPM2B_PUBLIC *public_info);

int
setup_signature_scheme(
    TPMI_ALG_PUBLIC         key_type,
    TPMI_ALG_HASH           halg,
    TPMT_SIG_SCHEME        *scheme);

/*  type conversion */
UINT32 
tpm2_util_endian_swap_32(UINT32 data);


/*  simple interface    */
TSS2_RC
tpm_hash(
    TSS2_SYS_CONTEXT *sapi_context,
    TPMI_ALG_HASH     hash_type,
    TPM2_HANDLE       hierarchy,
    TPM2B_DIGEST     *digest,
    uint8_t          *message,
    uint32_t          message_len);

TSS2_RC 
tpm2_util_nv_read_public(
    TSS2_SYS_CONTEXT *sapi_context,
    TPMI_RH_NV_INDEX  nv_index,
    TPM2B_NV_PUBLIC  *nv_public);

TSS2_RC
tpm2_util_nv_max_buffer_size(
    TSS2_SYS_CONTEXT *sapi_context,
    UINT32           *size);

#ifdef __cplusplus
}
#endif

#endif /* TPM2_COMMOND_H */