// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

#include "umock_c/umock_c.h"
#include "umock_c/umockcall.h"
#include "umock_c/umocktypes.h"
#include "umock_c/umocktypes_c.h"
#include "umock_c/umockcallrecorder.h"
#include "umock_c/umock_log.h"

typedef enum UMOCK_C_STATE_TAG
{
    UMOCK_C_STATE_NOT_INITIALIZED,
    UMOCK_C_STATE_INITIALIZED
} UMOCK_C_STATE;

static ON_UMOCK_C_ERROR on_umock_c_error_function;
static UMOCK_C_STATE umock_c_state = UMOCK_C_STATE_NOT_INITIALIZED;
static UMOCKCALLRECORDER_HANDLE umock_call_recorder = NULL;

int umock_c_init(ON_UMOCK_C_ERROR on_umock_c_error)
{
    int result;

    if (umock_c_state != UMOCK_C_STATE_NOT_INITIALIZED)
    {
        /* Codes_SRS_UMOCK_C_01_007: [ umock_c_init when umock is already initialized shall fail and return a non-zero value. ]*/
        UMOCK_LOG("umock_c: umock_c already initialized.");
        result = __LINE__;
    }
    else
    {
        /* Codes_SRS_UMOCK_C_LIB_01_144: [ Out of the box umock_c shall support the following types through the header umocktypes_c.h: ]*/
        /* Codes_SRS_UMOCK_C_LIB_01_028: [**char**] */
        /* Codes_SRS_UMOCK_C_LIB_01_029 : [**unsigned char**] */
        /* Codes_SRS_UMOCK_C_LIB_01_030 : [**short**] */
        /* Codes_SRS_UMOCK_C_LIB_01_031 : [**unsigned short**] */
        /* Codes_SRS_UMOCK_C_LIB_01_032 : [**int**] */
        /* Codes_SRS_UMOCK_C_LIB_01_033 : [**unsigned int**] */
        /* Codes_SRS_UMOCK_C_LIB_01_034 : [**long**] */
        /* Codes_SRS_UMOCK_C_LIB_01_035 : [**unsigned long**] */
        /* Codes_SRS_UMOCK_C_LIB_01_036 : [**long long**] */
        /* Codes_SRS_UMOCK_C_LIB_01_037 : [**unsigned long long**] */
        /* Codes_SRS_UMOCK_C_LIB_01_038 : [**float**] */
        /* Codes_SRS_UMOCK_C_LIB_01_039 : [**double**] */
        /* Codes_SRS_UMOCK_C_LIB_01_040 : [**long double**] */
        /* Codes_SRS_UMOCK_C_LIB_01_041 : [**size_t**] */
        /* Codes_SRS_UMOCK_C_LIB_01_151: [ void\* ]*/
        /* Codes_SRS_UMOCK_C_LIB_01_152: [ const void\* ]*/
        /* Codes_SRS_UMOCK_C_01_023: [ umock_c_init shall initialize the umock types by calling umocktypes_init. ]*/
        if ((umocktypes_init() != 0) ||
            /* Codes_SRS_UMOCK_C_01_002: [ umock_c_init shall register the C naive types by calling umocktypes_c_register_types. ]*/
            (umocktypes_c_register_types() != 0))
        {
            /* Codes_SRS_UMOCK_C_01_005: [ If any of the calls fails, umock_c_init shall fail and return a non-zero value. ]*/
            UMOCK_LOG("umock_c: Could not register standard C types with umock_c.");
            result = __LINE__;
        }
        else
        {
            /* Codes_SRS_UMOCK_C_01_003: [ umock_c_init shall create a call recorder by calling umockcallrecorder_create. ]*/
            umock_call_recorder = umockcallrecorder_create();
            if (umock_call_recorder == NULL)
            {
                /* Codes_SRS_UMOCK_C_01_005: [ If any of the calls fails, umock_c_init shall fail and return a non-zero value. ]*/
                UMOCK_LOG("umock_c: Could not create the call recorder.");
                result = __LINE__;
            }
            else
            {
                /* Codes_SRS_UMOCK_C_01_024: [ on_umock_c_error shall be optional. ]*/
                /* Codes_SRS_UMOCK_C_01_006: [ The on_umock_c_error callback shall be stored to be used for later error callbacks. ]*/
                on_umock_c_error_function = on_umock_c_error;

                /* Codes_SRS_UMOCK_C_01_001: [umock_c_init shall initialize the umock library.] */
                umock_c_state = UMOCK_C_STATE_INITIALIZED;

                /* Codes_SRS_UMOCK_C_01_004: [ On success, umock_c_init shall return 0. ]*/
                result = 0;
            }
        }
    }

    return result;
}

void umock_c_deinit(void)
{
    /* Codes_SRS_UMOCK_C_LIB_01_012: [If umock_c was not initialized, umock_c_deinit shall do nothing.] */
    /* Codes_SRS_UMOCK_C_01_010: [ If the module is not initialized, umock_c_deinit shall do nothing. ] */
    if (umock_c_state == UMOCK_C_STATE_INITIALIZED)
    {
        /* Codes_SRS_UMOCK_C_01_009: [ umock_c_deinit shall free the call recorder created in umock_c_init. ]*/
        umockcallrecorder_destroy(umock_call_recorder);
        umock_c_state = UMOCK_C_STATE_NOT_INITIALIZED;

        /* Codes_SRS_UMOCK_C_01_008: [ umock_c_deinit shall deinitialize the umock types by calling umocktypes_deinit. ]*/
        umocktypes_deinit();
    }
}

void umock_c_reset_all_calls(void)
{
    /* Codes_SRS_UMOCK_C_01_012: [ If the module is not initialized, umock_c_reset_all_calls shall do nothing. ]*/
    if (umock_c_state == UMOCK_C_STATE_INITIALIZED)
    {
        /* Codes_SRS_UMOCK_C_01_011: [ umock_c_reset_all_calls shall reset all calls by calling umockcallrecorder_reset_all_calls on the call recorder created in umock_c_init. ]*/
        if (umockcallrecorder_reset_all_calls(umock_call_recorder) != 0)
        {
            /* Codes_SRS_UMOCK_C_01_025: [ If the underlying umockcallrecorder_reset_all_calls fails, the on_umock_c_error callback shall be triggered with UMOCK_C_RESET_CALLS_ERROR. ]*/
            umock_c_indicate_error(UMOCK_C_RESET_CALLS_ERROR);
        }
    }
}

int umock_c_add_expected_call(UMOCKCALL_HANDLE mock_call)
{
    int result;

    if (umock_c_state != UMOCK_C_STATE_INITIALIZED)
    {
        /* Codes_SRS_UMOCK_C_01_020: [ If the module is not initialized, umock_c_add_expected_call shall return a non-zero value. ]*/
        UMOCK_LOG("umock_c: Cannot add an expected call, umock_c not initialized.");
        result = __LINE__;
    }
    else
    {
        /* Codes_SRS_UMOCK_C_01_019: [ umock_c_add_expected_call shall add an expected call by calling umockcallrecorder_add_expected_call on the call recorder created in umock_c_init. ]*/
        result = umockcallrecorder_add_expected_call(umock_call_recorder, mock_call);
    }

    return result;
}

int umock_c_add_actual_call(UMOCKCALL_HANDLE mock_call, UMOCKCALL_HANDLE* matched_call)
{
    int result;

    if (umock_c_state != UMOCK_C_STATE_INITIALIZED)
    {
        /* Codes_SRS_UMOCK_C_01_022: [ If the module is not initialized, umock_c_add_actual_call shall return a non-zero value. ]*/
        UMOCK_LOG("umock_c: Cannot add an actual call, umock_c not initialized.");
        result = __LINE__;
    }
    else
    {
        /* Codes_SRS_UMOCK_C_01_021: [ umock_c_add_actual_call shall add an actual call by calling umockcallrecorder_add_actual_call on the call recorder created in umock_c_init. ]*/
        result = umockcallrecorder_add_actual_call(umock_call_recorder, mock_call, matched_call);
    }

    return result;
}

const char* umock_c_get_expected_calls(void)
{
    const char* result;

    if (umock_c_state != UMOCK_C_STATE_INITIALIZED)
    {
        /* Codes_SRS_UMOCK_C_01_016: [ If the module is not initialized, umock_c_get_expected_calls shall return NULL. ]*/
        UMOCK_LOG("umock_c: Cannot get the expected calls, umock_c not initialized.");
        result = NULL;
    }
    else
    {
        /* Codes_SRS_UMOCK_C_01_015: [ umock_c_get_expected_calls shall return the string for the recorded expected calls by calling umockcallrecorder_get_expected_calls on the call recorder created in umock_c_init. ]*/
        result = umockcallrecorder_get_expected_calls(umock_call_recorder);
    }

    return result;
}

const char* umock_c_get_actual_calls(void)
{
    const char* result;

    if (umock_c_state != UMOCK_C_STATE_INITIALIZED)
    {
        /* Codes_SRS_UMOCK_C_01_014: [ If the module is not initialized, umock_c_get_actual_calls shall return NULL. ]*/
        UMOCK_LOG("umock_c: Cannot get the actual calls, umock_c not initialized.");
        result = NULL;
    }
    else
    {
        /* Codes_SRS_UMOCK_C_01_013: [ umock_c_get_actual_calls shall return the string for the recorded actual calls by calling umockcallrecorder_get_actual_calls on the call recorder created in umock_c_init. ]*/
        result = umockcallrecorder_get_actual_calls(umock_call_recorder);
    }

    return result;
}

UMOCKCALL_HANDLE umock_c_get_last_expected_call(void)
{
    UMOCKCALL_HANDLE result;

    if (umock_c_state != UMOCK_C_STATE_INITIALIZED)
    {
        /* Codes_SRS_UMOCK_C_01_018: [ If the module is not initialized, umock_c_get_last_expected_call shall return NULL. ]*/
        UMOCK_LOG("umock_c: Cannot get the last expected call, umock_c not initialized.");
        result = NULL;
    }
    else
    {
        /* Codes_SRS_UMOCK_C_01_017: [ umock_c_get_last_expected_call shall return the last expected call by calling umockcallrecorder_get_last_expected_call on the call recorder created in umock_c_init. ]*/
        result = umockcallrecorder_get_last_expected_call(umock_call_recorder);
    }

    return result;
}

void umock_c_indicate_error(UMOCK_C_ERROR_CODE error_code)
{
    if (on_umock_c_error_function != NULL)
    {
        on_umock_c_error_function(error_code);
    }
}

UMOCKCALLRECORDER_HANDLE umock_c_get_call_recorder(void)
{
    UMOCKCALLRECORDER_HANDLE result;

    if (umock_c_state != UMOCK_C_STATE_INITIALIZED)
    {
        /* Codes_SRS_UMOCK_C_01_027: [ If the module is not initialized, umock_c_get_call_recorder shall return NULL. ]*/
        UMOCK_LOG("umock_c_get_call_recorder: Cannot get the call recorder, umock_c not initialized.");
        result = NULL;
    }
    else
    {
        /* Codes_SRS_UMOCK_C_01_026: [ umock_c_get_call_recorder shall return the handle to the currently used call recorder. ]*/
        result = umock_call_recorder;
    }

    return result;
}

int umock_c_set_call_recorder(UMOCKCALLRECORDER_HANDLE call_recorder)
{
    int result;

    if (umock_c_state != UMOCK_C_STATE_INITIALIZED)
    {
        /* Codes_SRS_UMOCK_C_01_033: [ If the module is not initialized, umock_c_set_call_recorder shall return a non-zero value. ]*/
        UMOCK_LOG("umock_c_set_call_recorder: Cannot set the call recorder, umock_c not initialized.");
        result = __LINE__;
    }
    else if (call_recorder == NULL)
    {
        /* Codes_SRS_UMOCK_C_01_030: [ If call_recorder is NULL, umock_c_set_call_recorder shall return a non-zero value. ]*/
        UMOCK_LOG("umock_c_set_call_recorder: NULL call_recorder.");
        result = __LINE__;
    }
    else
    {
        UMOCKCALLRECORDER_HANDLE new_call_recorder;

        /* Codes_SRS_UMOCK_C_01_028: [ umock_c_set_call_recorder shall replace the currently used call recorder with the one identified by the call_recorder argument. ]*/
        /* Codes_SRS_UMOCK_C_01_031: [ umock_c_set_call_recorder shall make a copy of call_recorder by calling umockcallrecorder_clone and use the copy for future actions. ]*/
        new_call_recorder = umockcallrecorder_clone(call_recorder);
        if (new_call_recorder == NULL)
        {
            /* Codes_SRS_UMOCK_C_01_032: [ If umockcallrecorder_clone fails, umock_c_set_call_recorder shall return a non-zero value. ]*/
            UMOCK_LOG("umock_c_set_call_recorder: Failed cloning call recorder.");
            result = __LINE__;
        }
        else
        {
            /* Codes_SRS_UMOCK_C_01_034: [ The previously used call recorder shall be destroyed by calling umockcallrecorder_destroy. ]*/
            umockcallrecorder_destroy(umock_call_recorder);
            umock_call_recorder = new_call_recorder;

            /* Codes_SRS_UMOCK_C_01_029: [ On success, umock_c_set_call_recorder shall return 0. ]*/
            result = 0;
        }
    }

    return result;
}
