/*
 *
 * Copyright (c) 2020 Texas Instruments Incorporated
 *
 * All rights reserved not granted herein.
 *
 * Limited License.
 *
 * Texas Instruments Incorporated grants a world-wide, royalty-free, non-exclusive
 * license under copyrights and patents it now or hereafter owns or controls to make,
 * have made, use, import, offer to sell and sell ("Utilize") this software subject to the
 * terms herein.  With respect to the foregoing patent license, such license is granted
 * solely to the extent that any such patent is necessary to Utilize the software alone.
 * The patent license shall not apply to any combinations which include this software,
 * other than combinations with devices manufactured by or for TI ("TI Devices").
 * No hardware patent is licensed hereunder.
 *
 * Redistributions must preserve existing copyright notices and reproduce this license
 * (including the above copyright notice and the disclaimer and (if applicable) source
 * code license limitations below) in the documentation and/or other materials provided
 * with the distribution
 *
 * Redistribution and use in binary form, without modification, are permitted provided
 * that the following conditions are met:
 *
 * *       No reverse engineering, decompilation, or disassembly of this software is
 * permitted with respect to any software provided in binary form.
 *
 * *       any redistribution and use are licensed by TI for use only with TI Devices.
 *
 * *       Nothing shall obligate TI to provide you with source code for the software
 * licensed and provided to you in object code.
 *
 * If software source code is provided to you, modification and redistribution of the
 * source code are permitted provided that the following conditions are met:
 *
 * *       any redistribution and use of the source code, including any resulting derivative
 * works, are licensed by TI for use only with TI Devices.
 *
 * *       any redistribution and use of any object code compiled from the source code
 * and any resulting derivative works, are licensed by TI for use only with TI Devices.
 *
 * Neither the name of Texas Instruments Incorporated nor the names of its suppliers
 *
 * may be used to endorse or promote products derived from this software without
 * specific prior written permission.
 *
 * DISCLAIMER.
 *
 * THIS SOFTWARE IS PROVIDED BY TI AND TI'S LICENSORS "AS IS" AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL TI AND TI'S LICENSORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#include "app_ldc_module.h"
#include "ldc_lut_1920x1080.h"

static uint8_t  g_ldc_lut[] = LDC_LUT_1920_1080;

vx_status app_init_ldc(vx_context context, LDCObj *ldcObj, vx_int32 num_ch)
{
    vx_status status = VX_SUCCESS;

    vx_uint32 table_width_ds, table_height_ds;
    vx_imagepatch_addressing_t image_addr;
    vx_rectangle_t rect;

    ldcObj->table_width  = LDC_TABLE_WIDTH;
    ldcObj->table_height = LDC_TABLE_HEIGHT;
    ldcObj->ds_factor    = LDC_DS_FACTOR;

#if 0
    if(sensor_dcc_enabled)
    {
        int32_t dcc_buff_size;
        uint8_t * dcc_buf;
        vx_map_id dcc_buf_map_id;

        dcc_buff_size = appIssGetDCCSizeLDC(sensorObj->sensor_name, sensorObj->sensor_wdr_enabled);
        if (dcc_buff_size > 0)
        {
            ldcObj->dcc_config = vxCreateUserDataObject(context, "dcc_ldc", dcc_buff_size, NULL );

            vxMapUserDataObject(
                    ldcObj->dcc_config, 0,
                    dcc_buff_size,
                    &dcc_buf_map_id,
                    (void **)&dcc_buf,
                    VX_WRITE_ONLY,
                    VX_MEMORY_TYPE_HOST, 0);

            status = appIssGetDCCBuffLDC(sensorObj->sensor_name, sensorObj->sensor_wdr_enabled,  dcc_buf, dcc_buff_size);
            if(status != VX_SUCCESS)
            {
                printf("Error getting LDC DCC buffer \n");
                return VX_FAILURE;
            }
            vxUnmapUserDataObject(ldcObj->dcc_config, dcc_buf_map_id);
        }
    }
    else
    {
        ldcObj->dcc_config = NULL;
    }
#endif
    table_width_ds = (((ldcObj->table_width / (1 << ldcObj->ds_factor)) + 1u) + 15u) & (~15u);
    table_height_ds = ((ldcObj->table_height / (1 << ldcObj->ds_factor)) + 1u);

    /* Mesh Image */
    ldcObj->mesh_img = vxCreateImage(context, table_width_ds, table_height_ds, VX_DF_IMAGE_U32);

    /* Copy Mesh table */
    rect.start_x = 0;
    rect.start_y = 0;
    rect.end_x = table_width_ds;
    rect.end_y = table_height_ds;

    image_addr.dim_x = table_width_ds;
    image_addr.dim_y = table_height_ds;
    image_addr.stride_x = 4u;
    image_addr.stride_y = table_width_ds * 4u;

    status = vxCopyImagePatch(ldcObj->mesh_img,
                              &rect, 0,
                              &image_addr,
                              g_ldc_lut,
                              VX_WRITE_ONLY,
                              VX_MEMORY_TYPE_HOST);
    if (VX_SUCCESS != status)
    {
        APP_PRINTF("Copy Image Failed\n");
        return VX_FAILURE;
    }

    /* Mesh Parameters */
    memset(&ldcObj->mesh_params, 0, sizeof(tivx_vpac_ldc_mesh_params_t));
    tivx_vpac_ldc_mesh_params_init(&ldcObj->mesh_params);
    ldcObj->mesh_params.mesh_frame_width  = ldcObj->table_width;
    ldcObj->mesh_params.mesh_frame_height = ldcObj->table_height;
    ldcObj->mesh_params.subsample_factor  = ldcObj->ds_factor;

    ldcObj->mesh_config = vxCreateUserDataObject(context, "tivx_vpac_ldc_mesh_params_t", sizeof(tivx_vpac_ldc_mesh_params_t), NULL);
    vxCopyUserDataObject(ldcObj->mesh_config, 0,
                         sizeof(tivx_vpac_ldc_mesh_params_t),
                         &ldcObj->mesh_params,
                         VX_WRITE_ONLY,
                         VX_MEMORY_TYPE_HOST);

    /* Block Size parameters */
    ldcObj->region_params.out_block_width  = LDC_BLOCK_WIDTH;
    ldcObj->region_params.out_block_height = LDC_BLOCK_HEIGHT;
    ldcObj->region_params.pixel_pad        = LDC_PIXEL_PAD;

    ldcObj->region_config = vxCreateUserDataObject(context, "tivx_vpac_ldc_region_params_t", sizeof(tivx_vpac_ldc_region_params_t),  NULL);
    vxCopyUserDataObject(ldcObj->region_config, 0,
                         sizeof(tivx_vpac_ldc_region_params_t),
                         &ldcObj->region_params,
                         VX_WRITE_ONLY,
                         VX_MEMORY_TYPE_HOST);

    vx_uint32 dccId = 0;

    /* LDC Configuration */
    tivx_vpac_ldc_params_init(&ldcObj->params);
    ldcObj->params.luma_interpolation_type = 1;
    ldcObj->params.dcc_camera_id = dccId;

    ldcObj->config = vxCreateUserDataObject(context, "tivx_vpac_ldc_params_t", sizeof(tivx_vpac_ldc_params_t), NULL);

    vxCopyUserDataObject(ldcObj->config, 0,
                         sizeof(tivx_vpac_ldc_params_t),
                         &ldcObj->params,
                         VX_WRITE_ONLY,
                         VX_MEMORY_TYPE_HOST);

    /* LDC Output image in NV12 format */
    vx_image output_img = vxCreateImage(context, ldcObj->info_Output1.width, ldcObj->info_Output1.height, ldcObj->info_Output1.dataFormat);
    ldcObj->arr_Output1 = vxCreateObjectArray(context, (vx_reference)output_img, num_ch);
    ldcObj->img_Output1 = (vx_image)vxGetObjectArrayItem(ldcObj->arr_Output1, 0);
    vxReleaseImage(&output_img);

    /* Default LDC Input image in NV12 format */
    vx_int32 i = 0;
    vx_image input_img = vxCreateImage(context, ldcObj->info_Input.width, ldcObj->info_Input.height, ldcObj->info_Input.dataFormat);
    for (i=0; i<NUM_BUFS; i++)
    {
        ldcObj->arr_Input[i] = vxCreateObjectArray(context, (vx_reference)input_img, num_ch);
        ldcObj->img_Input[i] = (vx_image)vxGetObjectArrayItem(ldcObj->arr_Input[i], 0);
        APP_ASSERT_VALID_REF(ldcObj->img_Input[i]);
    }
    vxReleaseImage(&input_img);

    if(ldcObj->en_out_ldc_write == 1)
    {
        char file_path[TIVX_FILEIO_FILE_PATH_LENGTH];
        char file_prefix[TIVX_FILEIO_FILE_PREFIX_LENGTH];

        strcpy(file_path, ldcObj->output_file_path);
        ldcObj->file_path   = vxCreateArray(context, VX_TYPE_UINT8, TIVX_FILEIO_FILE_PATH_LENGTH);
        vxAddArrayItems(ldcObj->file_path, TIVX_FILEIO_FILE_PATH_LENGTH, &file_path[0], 1);

        strcpy(file_prefix, "ldc_output");
        ldcObj->file_prefix = vxCreateArray(context, VX_TYPE_UINT8, TIVX_FILEIO_FILE_PREFIX_LENGTH);
        vxAddArrayItems(ldcObj->file_prefix, TIVX_FILEIO_FILE_PREFIX_LENGTH, &file_prefix[0], 1);

        ldcObj->write_cmd = vxCreateUserDataObject(context, "tivxFileIOWriteCmd", sizeof(tivxFileIOWriteCmd), NULL);
    }
    else
    {
        ldcObj->file_path   = NULL;
        ldcObj->file_prefix = NULL;
        ldcObj->write_node  = NULL;
        ldcObj->write_cmd   = NULL;
    }

    return (status);
}

void app_deinit_ldc(LDCObj *ldcObj)
{
    vxReleaseUserDataObject(&ldcObj->config);
    vxReleaseUserDataObject(&ldcObj->region_config);
    vxReleaseUserDataObject(&ldcObj->mesh_config);

    vxReleaseImage(&ldcObj->mesh_img);
    vx_int32 i = 0;
    for (i=0; i<4; i++)
    {
        vxReleaseImage(&ldcObj->img_Input[i]);
    }

    vxReleaseImage(&ldcObj->img_Output1);
    vxReleaseObjectArray(&ldcObj->arr_Output1);

    if(ldcObj->dcc_config != NULL)
    {
        vxReleaseUserDataObject(&ldcObj->dcc_config);
    }


    if(ldcObj->en_out_ldc_write == 1)
    {
        vxReleaseArray(&ldcObj->file_path);
        vxReleaseArray(&ldcObj->file_prefix);
        vxReleaseUserDataObject(&ldcObj->write_cmd);
    }
}

void app_delete_ldc(LDCObj *ldcObj)
{
    if(ldcObj->node != NULL)
    {
        vxReleaseNode(&ldcObj->node);
    }
    if(ldcObj->write_node != NULL)
    {
        vxReleaseNode(&ldcObj->write_node);
    }
}

vx_status app_create_graph_ldc(vx_graph graph, LDCObj *ldcObj)
{
    vx_status status = VX_SUCCESS;

    ldcObj->node = tivxVpacLdcNode(graph, ldcObj->config, NULL,
                                   NULL, NULL,
                                   NULL, NULL, ldcObj->img_Input[0],
                                   ldcObj->img_Output1, NULL);

    status = vxGetStatus((vx_reference)ldcObj->node);

    vxSetNodeTarget(ldcObj->node, VX_TARGET_STRING, ldcObj->name_Target);
    vxSetReferenceName((vx_reference)ldcObj->node, ldcObj->name_NodeObj);

    vx_bool replicate[] = { vx_false_e, vx_false_e, vx_false_e, vx_false_e, vx_false_e, vx_false_e, vx_true_e, vx_true_e, vx_false_e};
    vxReplicateNode(graph, ldcObj->node, replicate, 9);
    APP_ASSERT_VALID_REF(ldcObj->node);

#if 0
    if (ldcObj->dcc_config != NULL)
    {
        ldcObj->node = tivxVpacLdcNode(graph, ldcObj->config, NULL,
                                       NULL, NULL,
                                       NULL, ldcObj->dcc_config, input_img,
                                       output_img, NULL);

        status = vxGetStatus((vx_reference)ldcObj->node);
        vxSetNodeTarget(ldcObj->node, VX_TARGET_STRING, TIVX_TARGET_VPAC_LDC1);
        vxSetReferenceName((vx_reference)ldcObj->node, "ldc_node");

        vx_bool replicate[] = { vx_false_e, vx_false_e, vx_false_e, vx_false_e, vx_false_e, vx_false_e, vx_true_e, vx_true_e, vx_false_e};
        vxReplicateNode(graph, ldcObj->node, replicate, 9);

    }
    else
    {
        ldcObj->node = tivxVpacLdcNode(graph, ldcObj->config, NULL,
                                    ldcObj->region_config, ldcObj->mesh_config,
                                    ldcObj->mesh_img, NULL, input_img,
                                    output_img, NULL);
    }
#endif


    if(ldcObj->en_out_ldc_write == 1)
    {
        APP_PRINTF("Adding LDC write node on graph .. \n");
        status = app_create_graph_ldc_write_output(graph, ldcObj);
        APP_PRINTF("LDC write node added! \n");
    }

    return status;
}

vx_status app_create_graph_ldc_write_output(vx_graph graph, LDCObj *ldcObj)
{
    vx_status status = VX_SUCCESS;

    vx_image output_img = (vx_image)vxGetObjectArrayItem(ldcObj->arr_Output1, 0);

    ldcObj->write_node = tivxWriteImageNode(graph, output_img, ldcObj->file_path, ldcObj->file_prefix);
    status = vxGetStatus((vx_reference)ldcObj->write_node);
    vxSetNodeTarget(ldcObj->write_node, VX_TARGET_STRING, TIVX_TARGET_A72_0);
    vxSetReferenceName((vx_reference)ldcObj->write_node, "LDC_Write_Node");

    vx_bool replicate[] = { vx_true_e, vx_false_e, vx_false_e};
    vxReplicateNode(graph, ldcObj->write_node, replicate, 3);

    vxReleaseImage(&output_img);

    return (status);
}

vx_status app_send_cmd_ldc_write_node(LDCObj *ldcObj, vx_uint32 start_frame, vx_uint32 num_frames, vx_uint32 num_skip)
{
    vx_status status = VX_SUCCESS;

    tivxFileIOWriteCmd write_cmd;

    write_cmd.start_frame = start_frame;
    write_cmd.num_frames = num_frames;
    write_cmd.num_skip = num_skip;

    status = vxCopyUserDataObject(ldcObj->write_cmd, 0, sizeof(tivxFileIOWriteCmd),\
                  &write_cmd, VX_WRITE_ONLY, VX_MEMORY_TYPE_HOST);

    if(status == VX_SUCCESS)
    {
        vx_reference refs[2];

        refs[0] = (vx_reference)ldcObj->write_cmd;

        status = tivxNodeSendCommand(ldcObj->write_node, TIVX_CONTROL_CMD_SEND_TO_ALL_REPLICATED_NODES,
                                 TIVX_FILEIO_CMD_SET_FILE_WRITE,
                                 refs, 1u);

        if(VX_SUCCESS != status)
        {
            printf("LDC Node send command failed!\n");
        }

        APP_PRINTF("LDC node send command success!\n");
    }

    return (status);
}
