/* Copyright (c) 2018-2020, NVIDIA CORPORATION.  All rights reserved.
 *
 * NVIDIA CORPORATION and its licensors retain all intellectual property
 * and proprietary rights in and to this software, related documentation
 * and any modifications thereto.  Any use, reproduction, disclosure or
 * distribution of this software and related documentation without an express
 * license agreement from NVIDIA CORPORATION is strictly prohibited.
 */

#include <string.h>

#include "cmdline.h"
#include "utils.h"
#include "dump.h"
#include "parameter_parser.h"

#include "nveeprom.h"

#define MAX_FILE_PREFIX_LENGTH  1024
#define FILENAME_BUFFER_LENGTH  (MAX_FILE_PREFIX_LENGTH + 128)
#define FACTORY_FILE_POSTFIX    "_factory.txt"
#define WRITABLE_FILE_POSTFIX   "_writable.txt"
#define BINARY_FILE_POSTFIX     ".bin"

static NvMediaStatus
sDumpParameterFile(
    FILE *file,
    NvEEPROMManager *eepromManager,
    bool readOnly,
    bool shortPrint)
{
    NvMediaStatus status;
    ParameterValue *parameterValues;
    uint32_t numParameters = NVEEPROM_NUM_PARAMS;

    /* Read the parameters from the EEPROM manager */
    parameterValues = malloc(sizeof(ParameterValue) * NVEEPROM_NUM_PARAMS);
    if (NULL == parameterValues) {
        return NVMEDIA_STATUS_OUT_OF_MEMORY;
    }

    status = ReadNvEEPROMManager(eepromManager,
                                 parameterValues,
                                 &numParameters,
                                 readOnly);
    if (NVMEDIA_STATUS_OK != status) {
        LOG_ERR("Failed to read parameters from EEPROM Manager\n");
        free(parameterValues);
        return status;
    }

    /* Write the parameters to the file */
    status = WriteParameterFile(file,
                                parameterValues,
                                numParameters,
                                shortPrint);
    if (NVMEDIA_STATUS_OK != status) {
        LOG_ERR("Unable to output parameters\n");
        free(parameterValues);
        return status;
    }

    return NVMEDIA_STATUS_OK;
}

/* If a file prefix was provided, construct the filename and open the file */
static FILE*
sOpenFile(
    const char *prefix,
    uint32_t link,
    const char *postfix)
{
    FILE *file;
    char filename[FILENAME_BUFFER_LENGTH];

    if (NULL != prefix) {
        sprintf(filename, "%s_link%02d%s",
                prefix,
                link,
                postfix);
        file = fopen(filename, "w");
        if (NULL == file) {
            LOG_ERR("Failed to open file \"%s\" for writing\n", filename);
        }
    } else {
        file = stdout;
    }
    return file;
}

static FILE*
sCloseFile(
    FILE *file)
{
    if ( (NULL != file) && (stdout != file) ) {
        fclose(file);
    }
    return NULL;
}

NvMediaStatus
DumpEEPROM(
    NvEEPROMContext *context)
{
    NvMediaStatus status;

    NvEEPROMManager *eepromManager;
    uint32_t link;
    FILE *file = NULL;
    const char *filename;
    bool shortPrint;

    if (context->allArgs->outputFile.isUsed){
        filename = context->allArgs->outputFile.stringValue;
        shortPrint = false;
    } else {
        filename = NULL;
        shortPrint = !context->allArgs->longPrint.isUsed;
    }

    if ( (NULL != filename) && (MAX_FILE_PREFIX_LENGTH < strlen(filename)) ) {
        LOG_ERR("Provided output file prefix is longer than the maximum value of "
                "%d characters\n", MAX_FILE_PREFIX_LENGTH);
        return NVMEDIA_STATUS_ERROR;
    }

    status = InitializeExtImgDevice(context);
    if (NVMEDIA_STATUS_OK != status) {
        return status;
    }

    for (link = 0; link < context->numSensors; link++) {

        /* Provide a message if the user is not outputting to a file */
        if (NULL == filename) {
            printf("*************************************************************************\n"
                   "* EEPROM on link %d                                                     *\n"
                   "*************************************************************************\n\n",
                   link);
        }

        /* Initialize the EEPROM Manager */
        eepromManager = NvEEPROMManagerCreate(context->extImgDev->iscEeprom[link], 0);
        if (NULL == eepromManager) {
            LOG_ERR("Failed to initialize EEPROMManager on link %d\n", link);
            goto fail;
        }

        /* Provide a message if the user is not outputting to a file */
        if (NULL == filename) {
            printf("************************ Contents of Factory Blob ***********************\n");
        }

        /* If a file prefix was provided, construct the filename and open the file */
        file = sOpenFile(filename, link, FACTORY_FILE_POSTFIX);
        if (NULL == file) {
            goto fail;
        }

        /* Read the read only parameters from the EEPROM manager and dump to the file */
        status = sDumpParameterFile(file,
                                    eepromManager,
                                    true,
                                    shortPrint);
        file = sCloseFile(file);
        if (NVMEDIA_STATUS_OK != status) {
            goto fail;
        }

        /* Provide a message if the user is not outputting to a file */
        if (NULL == filename) {
            printf("************************ Contents of Writable Section *******************\n");
        }

        /* If a file prefix was provided, construct the filename and open the file */
        file = sOpenFile(filename, link, WRITABLE_FILE_POSTFIX);
        if (NULL == file) {
            goto fail;
        }

        /* Read the writable parameters from the EEPROM manager and dump to the file */
        status = sDumpParameterFile(file,
                                    eepromManager,
                                    false,
                                    shortPrint);
        file = sCloseFile(file);
        if (NVMEDIA_STATUS_OK != status) {
            goto fail;
        }

        NvEEPROMManagerDestroy(eepromManager);
        eepromManager = NULL;
        file = NULL;
    }

    fail:
    if (NULL != eepromManager) {
        NvEEPROMManagerDestroy(eepromManager);
    }
    if (NULL != file) {
        file = sCloseFile(file);
    }
    DeinitializeExtImgDevice(context);
    return status;
}
