#include <stdlib.h>
#include <stdio.h>
#include <arpa/inet.h>

#include <setjmp.h>
#include <cmocka.h>

#include "sapi/tpm20.h"
#include "sysapi_util.h"

#define MAX_SIZE_CTX 4096
/**
 *
 */
static int
CopyCommandHeader_sys_setup (void **state)
{
    _TSS2_SYS_CONTEXT_BLOB *sys_ctx;
    UINT32 size_ctx;

    size_ctx = Tss2_Sys_GetContextSize (MAX_SIZE_CTX);
    sys_ctx = calloc (1, size_ctx);
    assert_non_null (sys_ctx);
    /**
     *  This is the important part: the CopyCommandHeader function builds up
     *  the command buffer in the memory pointed to by tpmInitBuffPtr. This
     *  must point to the data after the context structure.
     */
    sys_ctx->tpmInBuffPtr = (UINT8*) (sys_ctx + sizeof (_TSS2_SYS_CONTEXT_BLOB));

    *state = sys_ctx;
    return 0;
}

static int
CopyCommandHeader_sys_teardown (void **state)
{
    _TSS2_SYS_CONTEXT_BLOB *sys_ctx = (_TSS2_SYS_CONTEXT_BLOB*)*state;

    if (sys_ctx)
        free (sys_ctx);

    return 0;
}

/**
 *  CopyCommandHeader creates the standard TPM command header (tag, size,
 *  command_code) to the data buffer in the context structure. It also
 *  advances the 'nextData' pointer to the address after the header. This
 *  test will fail if the nextData pointer isn't set as expected
 */
static void
CopyCommandHeader_nextData_unit (void **state)
{
    _TSS2_SYS_CONTEXT_BLOB *sys_ctx = (_TSS2_SYS_CONTEXT_BLOB*)*state;
    TPM_CC cc = TPM_CC_GetCapability;

    CopyCommandHeader (sys_ctx, cc);
    assert_int_equal (sys_ctx->nextData - sys_ctx->tpmInBuffPtr, sizeof (TPM20_Header_In));
}

/**
 * After a call to CopyCommandHeader the tag in the TPM20_Header_In portion of
 * the tpmInBuffPtr member of the sys context should be TPM_ST_NO_SESSIONS
 * transformed into network byte order.
 */
static void
CopyCommandHeader_tag_unit (void **state)
{
    _TSS2_SYS_CONTEXT_BLOB *sys_ctx = (_TSS2_SYS_CONTEXT_BLOB*)*state;
    TPM_CC cc = TPM_CC_GetCapability;
    TPM20_Header_In *header = (TPM20_Header_In*)sys_ctx->tpmInBuffPtr;
    /* The TSS code uses a custom function to convert stuff to network byte
     * order but we can just use htons. Not sure why we don't use htons/l
     * everywhere.
     */
    TPMI_ST_COMMAND_TAG tag_net = htons (TPM_ST_NO_SESSIONS);

    CopyCommandHeader (sys_ctx, cc);
    assert_int_equal (tag_net, header->tag);
}
/**
 * After a call to CopyCommandHeader the commandCode in the TPM20_Header_In
 * portion of the tpmInBuffPtr member of the sys context should be the command
 * code parameter in network byte order.
 */
static void
CopyCommandHeader_commandcode_unit (void **state)
{
    _TSS2_SYS_CONTEXT_BLOB *sys_ctx = (_TSS2_SYS_CONTEXT_BLOB*)*state;
    TPM_CC cc = TPM_CC_GetCapability;
    TPM_CC cc_net = htonl (cc);
    TPM20_Header_In *header = (TPM20_Header_In*)sys_ctx->tpmInBuffPtr;

    CopyCommandHeader (sys_ctx, cc);
    assert_int_equal (cc_net, header->commandCode);
}

int
main (int argc, char* argv[])
{
    const struct CMUnitTest tests[] = {
        cmocka_unit_test_setup_teardown (CopyCommandHeader_nextData_unit,
                                  CopyCommandHeader_sys_setup,
                                  CopyCommandHeader_sys_teardown),
        cmocka_unit_test_setup_teardown (CopyCommandHeader_tag_unit,
                                  CopyCommandHeader_sys_setup,
                                  CopyCommandHeader_sys_teardown),
        cmocka_unit_test_setup_teardown (CopyCommandHeader_commandcode_unit,
                                  CopyCommandHeader_sys_setup,
                                  CopyCommandHeader_sys_teardown),
    };
    return cmocka_run_group_tests (tests, NULL, NULL);
}
