/*
 * Copyright (c) 2019-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 "CNvMMAX96712TPG.hpp"
#include "devices/MAX96712DeserializerDriver/CNvMMax96712.hpp"
#include "ModuleIF/CNvMCameraModuleExport.hpp"

namespace nvsipl
{

SIPLStatus CNvMMAX96712TPG::Init() {
    NvMediaStatus nvmediaStatus = NVMEDIA_STATUS_OK;
    DevBlkCDIDevice *cdiDeserializer = m_pDeserializer->GetCDIDeviceHandle();
    DataTypeMAX96712 dataType = CDI_MAX96712_DATA_TYPE_INVALID;
    WriteParametersParamMAX96712 paramsMAX96712 = {};
    NvMediaICPInputFormatType inputFormatType = m_upCameraModuleProperty->sensorProperty.inputFormat.inputFormatType;
    LinkPipelineMapMAX96712 *pipeLineMap = NULL;

    if (m_bSlave) {
        return NVSIPL_STATUS_OK;
    }

    if (m_initLinkMask != 0) {
        LOG_INFO("Set MAX96712 TPG defaults\n");
        nvmediaStatus = MAX96712SetDeviceConfig(cdiDeserializer,
                                                  CDI_CONFIG_MAX96712_SET_PG_1920x1236);
        CHK_NVMERROR_CONVERT_AND_RETURN(nvmediaStatus, "CDI_CONFIG_MAX96712_SET_PG_1920x1236 failed\n");

        if (inputFormatType == NVMEDIA_IMAGE_CAPTURE_INPUT_FORMAT_TYPE_RAW12) {
            dataType = CDI_MAX96712_DATA_TYPE_RAW12;
        } else if (inputFormatType == NVMEDIA_IMAGE_CAPTURE_INPUT_FORMAT_TYPE_RGB888) {
            dataType =  CDI_MAX96712_DATA_TYPE_RGB;
        } else {
            LOG_ERR("Input format not supported\n");
            return NVSIPL_STATUS_ERROR;
        }

        /* Update mapping table */
        for (auto i = 0u; i < MAX96712_MAX_NUM_LINK; i++) {
            pipeLineMap = &paramsMAX96712.PipelineMapping.linkPipelineMap[i];
            pipeLineMap->isEmbDataType = false;
            pipeLineMap->vcID = i;
            pipeLineMap->dataType = dataType;
            pipeLineMap->isDTOverride = true;

            /* When enabling TPG on Max96712, 1st TPG output is going to pipeline 0 ~ 3,
            * 2nd TPG output is going to pipeline 4 ~ 7.
            * And pipeline 0/4 is going to controller 0, pipeline 1/5 is going to controller 1
            * pipeline 2/6 is going to controller 2, pipeline 3/7 is going to controller 3 by default.
            * Since there is no way to disable TPG and TPG is behind the pipeline,
            * undesired pipeline output has to be mapped to unused controller.
            */
            pipeLineMap->isMapToUnusedCtrl = (m_initLinkMask & (1 << i)) ? false : true;
        }

        LOG_INFO("Set MAX96712 pipeline mapping\n");
        paramsMAX96712.PipelineMapping.link = CDI_MAX96712_LINK_ALL;
        LOG_DEBUG("Set pipeline mapping\n");
        nvmediaStatus = MAX96712WriteParameters(cdiDeserializer,
                                                  CDI_WRITE_PARAM_CMD_MAX96712_PIPELINE_MAPPING,
                                                  sizeof(paramsMAX96712.PipelineMapping),
                                                  &paramsMAX96712);
        CHK_NVMERROR_CONVERT_AND_RETURN(nvmediaStatus, "CDI_WRITE_PARAM_CMD_MAX96712_PIPELINE_MAPPING failed\n");

        // Override DataType
        paramsMAX96712.PipelineMapping.link = CDI_MAX96712_LINK_ALL;
        nvmediaStatus = MAX96712WriteParameters(cdiDeserializer,
                                                CDI_WRITE_PARAM_CMD_MAX96712_OVERRIDE_DATATYPE,
                                                sizeof(paramsMAX96712.PipelineMapping),
                                                &paramsMAX96712);
        CHK_NVMERROR_CONVERT_AND_RETURN(nvmediaStatus, "CDI_WRITE_PARAM_CMD_MAX96712_OVERRIDE_DATATYPE");

        LOG_INFO("Set MAX96712 enable PG\n");
        nvmediaStatus = MAX96712SetDeviceConfig(cdiDeserializer,
                                                  CDI_CONFIG_MAX96712_ENABLE_PG);
        CHK_NVMERROR_CONVERT_AND_RETURN(nvmediaStatus, "CDI_CONFIG_MAX96712_ENABLE_PG failed\n");

        // Delay to skip some frames at the beginning
        SLEEP_MS(100);
    }

    return NVSIPL_STATUS_OK;
}

SIPLStatus CNvMMAX96712TPG::PostInit() {
    return NVSIPL_STATUS_OK;
}

SIPLStatus CNvMMAX96712TPG::Reconfigure() {
    return NVSIPL_STATUS_NOT_SUPPORTED;
}

SIPLStatus CNvMMAX96712TPG::ReadEEPROMData(const std::uint16_t address,
                                           const std::uint32_t length,
                                           std::uint8_t * const buffer) {
    return NVSIPL_STATUS_NOT_SUPPORTED;
}

#if !NV_IS_SAFETY
SIPLStatus CNvMMAX96712TPG::WriteEEPROMData(const std::uint16_t address,
                                            const std::uint32_t length,
                                            std::uint8_t * const buffer) {
    return NVSIPL_STATUS_NOT_SUPPORTED;
}
#endif // !NV_IS_SAFETY

SIPLStatus CNvMMAX96712TPG::ToggleLED(bool enable) {
    return NVSIPL_STATUS_NOT_SUPPORTED;
}

SIPLStatus CNvMMAX96712TPG::Start() {
    return NVSIPL_STATUS_OK;
}

SIPLStatus CNvMMAX96712TPG::Stop() {
    return NVSIPL_STATUS_OK;
}

SIPLStatus CNvMMAX96712TPG::Deinit() {
    return NVSIPL_STATUS_OK;
}

SIPLStatus CNvMMAX96712TPG::SetConfig(const CameraModuleConfig *cameraModuleConfig, const uint8_t linkIndex)
{

    SIPLStatus status;

    m_pDeserializer = cameraModuleConfig->deserializer;
    m_initLinkMask = cameraModuleConfig->initLinkMask;
    m_bSlave = cameraModuleConfig->params->bSlave;

    const CameraModuleInfo *moduleInfo = cameraModuleConfig->cameraModuleInfo;

    // Create camera module property
    m_upCameraModuleProperty = std::move(std::unique_ptr<Property>(new Property));

    m_upSensor.reset(new CNvMSensor());
    status = m_upSensor->SetConfig(&moduleInfo->sensorInfo, cameraModuleConfig->params);
    CHK_NVSIPLEERROR_AND_RETURN(status, "Sensor SetConfig");

    CNvMSensor* sensor = m_upSensor.get();
    CNvMCameraModule::Property::SensorProperty oSensorProperty = {
        .id = moduleInfo->sensorInfo.id,
        .virtualChannelID = linkIndex,
        .inputFormat = sensor->GetInputFormat(),
        .surfaceType = sensor->GetSurfaceType(),
        .width = sensor->GetWidth(),
        .height = sensor->GetHeight(),
        .startX = 0,
        .startY = 0,
        .embeddedTop = sensor->GetEmbLinesTop(),
        .embeddedBot = sensor->GetEmbLinesBot(),
        .frameRate = sensor->GetFrameRate(),
        .embeddedDataType = sensor->GetEmbDataType(),
        .pCDIHandle = nullptr,
    };

    m_upCameraModuleProperty->sensorProperty = oSensorProperty;

    return NVSIPL_STATUS_OK;
}

CNvMCameraModule::Property* CNvMMAX96712TPG::GetCameraModuleProperty() {
    return m_upCameraModuleProperty.get();
}

SIPLStatus CNvMMAX96712TPG::CreateCDIDevice(DevBlkCDIRootDevice* cdiRoot){
    return NVSIPL_STATUS_OK;
}

uint16_t CNvMMAX96712TPG::GetPowerOnDelayMs() {
    return 0;
}

std::string CNvMMAX96712TPG::GetSupportedDeserailizer() {
    return "MAX96712";
}

CNvMDeserializer::LinkMode CNvMMAX96712TPG::GetLinkMode() {
    return CNvMDeserializer::LinkMode::LINK_MODE_GMSL1;
}

CNvMCameraModule *CNvMCameraModule_Create() {
    return new CNvMMAX96712TPG();
}

const char** CNvMCameraModule_GetNames() {
    static const char* names[] = {
        "MAX96712TPG",
        NULL
    };
    return names;
}


} // end of namespace
