/*
 * 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 "CNvMMAX96712_96705_AR0144.hpp"
#include "cameramodule/MAX96712cameramodule/CNvMTransportLink_Max96712_96705.hpp"
#include "ModuleIF/CNvMCameraModuleExport.hpp"

extern "C" {
#include "cdi_ar0144.h"
}

#define OSC_MHZ 27

namespace nvsipl
{

void CNvMMAX96712_96705_AR0144::SetSensorConnectionProperty(ConnectionProperty::SensorConnectionProperty *sensorConnectionProperty) {
    // These setting depends on HW configuration.
    sensorConnectionProperty->sensorReset.isNeeded = false;
    sensorConnectionProperty->sensorReset.pinNum = 0;
    sensorConnectionProperty->sensorReset.releaseResetLevel = false;

    sensorConnectionProperty->frameSync.pinNum = 8;

    sensorConnectionProperty->refClock.isNeeded = false;
    sensorConnectionProperty->refClock.pinNum = 0;

    sensorConnectionProperty->phyLanes.isLaneSwapNeeded = false;
    sensorConnectionProperty->phyLanes.isTwoLane = false;

    sensorConnectionProperty->pclk = 74250000;
}

SIPLStatus CNvMMAX96712_96705_AR0144::SetConfigModule(const SensorInfo *sensorInfo, CNvMDevice::DeviceParams *params) {

    auto& sensor = m_upSensor;

    std::string resolution;
    NvMediaStatus nvmediaStatus = NVMEDIA_STATUS_OK;

    resolution = std::to_string(sensor->GetWidth()) + "x" +  std::to_string(sensor->GetHeight());
    nvmediaStatus = GetAR0144ConfigSet((char *)resolution.c_str(),
                                       sensorInfo->vcInfo.inputFormat,
                                       &m_ConfigIndex,
                                       (uint32_t)sensor->GetFrameRate());
    CHK_NVMERROR_CONVERT_AND_RETURN(nvmediaStatus, "%s: Failed to get config");

    return NVSIPL_STATUS_OK;
}

SIPLStatus CNvMMAX96712_96705_AR0144::DetectModule() {
    SIPLStatus status = NVSIPL_STATUS_OK;
    NvMediaStatus nvmStatus = NVMEDIA_STATUS_OK;
    auto& sensor = m_upSensor;

    if (m_oDeviceParams.bEnableSimulator or m_oDeviceParams.bSlave) {
        status = InitSimulatorAndSlave();
        if (NVSIPL_IsFailed(status)) {
            LOG_ERR("CNvMMAX96712_96705_AR0144::InitSimulatorAndSlave failed \n");
        }
        return status;
    }

    /*! Check SENSOR is present */
    LOG_INFO("Check SENSOR is present\n");
    nvmStatus = AR0144CheckPresence(sensor->GetCDIDeviceHandle());
    CHK_NVMERROR_CONVERT_AND_RETURN(nvmStatus, "DevBlkCDICheckPresence failed");

    return NVSIPL_STATUS_OK;
}

SIPLStatus CNvMMAX96712_96705_AR0144::InitModule() {
    SIPLStatus status = NVSIPL_STATUS_OK;
    NvMediaStatus nvmediaStatus = NVMEDIA_STATUS_OK;
    auto& sensor = m_upSensor;

    if (m_oDeviceParams.bEnableSimulator or m_oDeviceParams.bSlave) {
        status = InitSimulatorAndSlave();
        if (NVSIPL_IsFailed(status)) {
            LOG_ERR("CNvMMAX96712_96705_AR0144::InitSimulatorAndSlave failed \n");
        }
        return status;
    }

    /*! Set defaults */
    LOG_INFO("Set defaults in %s\n", "CNvMSensor::Init");
    nvmediaStatus = AR0144SetDefaults(sensor->GetCDIDeviceHandle());
    CHK_NVMERROR_CONVERT_AND_RETURN(nvmediaStatus, "DevBlkCDISetDefaults");

    if (sensor->GetEnableTPG()) {
        /*! Enable sensor tpg */
        nvmediaStatus = AR0144SetDeviceConfig(sensor->GetCDIDeviceHandle(), CDI_CONFIG_AR0144_ENABLE_PG);
        CHK_NVMERROR_CONVERT_AND_RETURN(nvmediaStatus, "CDI_CONFIG_AR0144_ENABLE_PG");
    }

    /*! Enable sensor streaming */
    nvmediaStatus = AR0144SetDeviceConfig(sensor->GetCDIDeviceHandle(), CDI_CONFIG_AR0144_ENABLE_STREAMING);
    CHK_NVMERROR_CONVERT_AND_RETURN(nvmediaStatus, "CDI_CONFIG_AR0144_ENABLE_STREAMING");

    SLEEP_MS(10);

    return NVSIPL_STATUS_OK;
}

SIPLStatus CNvMMAX96712_96705_AR0144::StartModule() {
    return NVSIPL_STATUS_OK;
}

SIPLStatus CNvMMAX96712_96705_AR0144::StopModule() {
    return NVSIPL_STATUS_OK;
}

SIPLStatus CNvMMAX96712_96705_AR0144::DeinitModule() {
    return NVSIPL_STATUS_OK;
}

DevBlkCDIDeviceDriver *CNvMMAX96712_96705_AR0144::GetCDIDeviceDriver() {
    return GetAR0144Driver();
}

std::unique_ptr<CNvMDevice::DriverContext> CNvMMAX96712_96705_AR0144::GetCDIDeviceContext() {
    auto driverContext = new CNvMDevice::DriverContextImpl<ContextAR0144>();
    if (driverContext == nullptr) {
        return nullptr;
    }

    return std::unique_ptr<CNvMDevice::DriverContext>(driverContext);
}

SIPLStatus CNvMMAX96712_96705_AR0144::InitSimulatorAndSlave()
{
    return NVSIPL_STATUS_OK;
}

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

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

} // end of namespace
