/*
 *
 * 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_scaler_module.h"

vx_status app_init_scaler(vx_context context, ScalerObj *scalerObj, vx_int32 num_ch)
{
    vx_status status = VX_SUCCESS;

    tivx_vpac_msc_coefficients_t coeffs;

    vx_image output1  = vxCreateImage(context, scalerObj->info_Output1.width, scalerObj->info_Output1.height, scalerObj->info_Output1.dataFormat);
    scalerObj->arr_Output1  = vxCreateObjectArray(context, (vx_reference)output1, num_ch);
    vxReleaseImage(&output1);

    vx_image output2  = vxCreateImage(context, scalerObj->info_Output2.width, scalerObj->info_Output2.height, scalerObj->info_Output2.dataFormat);
    scalerObj->arr_Output2  = vxCreateObjectArray(context, (vx_reference)output2, num_ch);
    vxReleaseImage(&output2);

    scale_set_coeff(&coeffs, VX_INTERPOLATION_BILINEAR);
    /* Set Coefficients */
    scalerObj->coeff_obj = vxCreateUserDataObject(context,
                                    "tivx_vpac_msc_coefficients_t",
                                    sizeof(tivx_vpac_msc_coefficients_t),
                                    NULL);
    status = vxGetStatus((vx_reference)scalerObj->coeff_obj);
    if(status == VX_SUCCESS)
    {
        status = vxCopyUserDataObject(scalerObj->coeff_obj, 0,
                                        sizeof(tivx_vpac_msc_coefficients_t),
                                        &coeffs,
                                        VX_WRITE_ONLY,
                                        VX_MEMORY_TYPE_HOST);
    }
    else
    {
        APP_PRINTF("init convert failed\n");
    }

    return status;
}

void app_deinit_scaler(ScalerObj *scalerObj)
{
  vxReleaseUserDataObject(&scalerObj->coeff_obj);
  vxReleaseObjectArray(&scalerObj->arr_Output1);
  vxReleaseObjectArray(&scalerObj->arr_Output2);
}

void app_delete_scaler(ScalerObj *scalerObj)
{
  if(scalerObj->node != NULL)
  {
    vxReleaseNode(&scalerObj->node);
  }
}

vx_status app_create_graph_scaler(vx_graph graph, ScalerObj *scalerObj, vx_object_array arrInput)
{
    vx_status status = VX_SUCCESS;

    vx_image imgInput = (vx_image)vxGetObjectArrayItem(arrInput, 0);
    vx_image imgOutput1 = (vx_image)vxGetObjectArrayItem(scalerObj->arr_Output1, 0);
    vx_image imgOutput2 = (vx_image)vxGetObjectArrayItem(scalerObj->arr_Output2, 0);

    scalerObj->node = tivxVpacMscScaleNode(graph, imgInput, imgOutput1, imgOutput2, NULL, NULL, NULL);
    vxSetNodeTarget(scalerObj->node, VX_TARGET_STRING, scalerObj->name_Target);
    vxSetReferenceName((vx_reference)scalerObj->node, scalerObj->name_NodeObj);

    vxReplicateNode(graph, scalerObj->node, scalerObj->arr_Replicate, sizeof(scalerObj->arr_Replicate)/sizeof(vx_bool));
    if (vxGetStatus((vx_reference)scalerObj->node) != VX_SUCCESS)
    {
        APP_PRINTF("Create Node: MscScaler failed! target is %s !!!\n", scalerObj->name_Target);
        return VX_FAILURE;
    }
    else
    {
        APP_PRINTF("Create Node: MscScaler success! target is %s !!!\n", scalerObj->name_Target);
    }

    vxReleaseImage(&imgInput);
    vxReleaseImage(&imgOutput1);
    vxReleaseImage(&imgOutput2);
    return status;
}


vx_status app_create_graph_scaler_write_output(vx_graph graph, ScalerObj *scalerObj)
{
    vx_status status = VX_SUCCESS;

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

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

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

    vxReleaseImage(&output_img);

    return (status);
}

vx_status app_send_cmd_scaler_write_node(ScalerObj *scalerObj, 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(scalerObj->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)scalerObj->write_cmd;

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

        if(VX_SUCCESS != status)
        {
            printf("Scaler write node send command failed!\n");
        }

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

    return (status);
}

vx_status app_sendcmd2Scaler(ScalerObj *scalerObj)
{
    vx_status status = VX_SUCCESS;
    vx_reference refs[1];

    refs[0] = (vx_reference)scalerObj->coeff_obj;

    status = tivxNodeSendCommand(scalerObj->node, 0u,
                                 TIVX_VPAC_MSC_CMD_SET_COEFF,
                                 refs, 1u);

    if(VX_SUCCESS != status)
    {
        APP_PRINTF("MSC: Node send command failed!\n");
    }
    else
    {
        APP_PRINTF("MSC: Node send command success!\n");
    }

    /* wait a while for prints to flush */
    tivxTaskWaitMsecs(100);

    return status;
}


vx_status readScalerInput(char* file_name, vx_object_array img_arr, int32_t ch_num)
{
  vx_status status;

  status = vxGetStatus((vx_reference)img_arr);

  if(status == VX_SUCCESS)
  {
    FILE * fp = fopen(file_name,"rb");
    vx_size arr_len;
    vx_int32 i;

    if(fp == NULL)
    {
      printf("Unable to open file %s \n", file_name);
      return (VX_FAILURE);
    }

    vxQueryObjectArray(img_arr, VX_OBJECT_ARRAY_NUMITEMS, &arr_len, sizeof(vx_size));

    for (i = 0; i < arr_len; i++)
    {
      vx_rectangle_t rect;
      vx_imagepatch_addressing_t image_addr;
      vx_map_id map_id;
      void * data_ptr;
      vx_uint32  img_width;
      vx_uint32  img_height;
      vx_image   in_img;
      vx_uint32  num_bytes;

      in_img = (vx_image)vxGetObjectArrayItem(img_arr, i);

      vxQueryImage(in_img, VX_IMAGE_WIDTH, &img_width, sizeof(vx_uint32));
      vxQueryImage(in_img, VX_IMAGE_HEIGHT, &img_height, sizeof(vx_uint32));

      rect.start_x = 0;
      rect.start_y = 0;
      rect.end_x = img_width;
      rect.end_y = img_height;
      status = vxMapImagePatch(in_img,
                               &rect,
                               0,
                               &map_id,
                               &image_addr,
                               &data_ptr,
                               VX_WRITE_ONLY,
                               VX_MEMORY_TYPE_HOST,
                               VX_NOGAP_X);

      vx_int32 one_frame = img_width * img_height * 1.5;

      if(ch_num == -1)
      {
        fseek(fp, one_frame * i, SEEK_SET);
      }
      else
      {
        fseek(fp, one_frame * ch_num, SEEK_SET);
      }

      //Copy Luma
      num_bytes = fread(data_ptr,1,img_width*img_height, fp);

      if(num_bytes != (img_width*img_height))
        printf("Luma bytes read = %d, expected = %d", num_bytes, img_width*img_height);

      vxUnmapImagePatch(in_img, map_id);

      rect.start_x = 0;
      rect.start_y = 0;
      rect.end_x = img_width;
      rect.end_y = img_height / 2;
      status = vxMapImagePatch(in_img,
                               &rect,
                               1,
                               &map_id,
                               &image_addr,
                               &data_ptr,
                               VX_WRITE_ONLY,
                               VX_MEMORY_TYPE_HOST,
                               VX_NOGAP_X);


      //Copy CbCr
      num_bytes = fread(data_ptr,1,img_width*img_height/2, fp);

      if(num_bytes != (img_width*img_height/2))
        printf("CbCr bytes read = %d, expected = %d", num_bytes, img_width*img_height/2);

      vxUnmapImagePatch(in_img, map_id);

      vxReleaseImage(&in_img);
    }

    fclose(fp);
  }

  return(status);
}

vx_status writeScalerOutput(char* file_name, vx_object_array img_arr)
{
  vx_status status;

  status = vxGetStatus((vx_reference)img_arr);

  if(status == VX_SUCCESS)
  {
    FILE * fp = fopen(file_name,"wb");
    vx_size arr_len;
    vx_int32 i;

    if(fp == NULL)
    {
      printf("Unable to open file %s \n", file_name);
      return (VX_FAILURE);
    }

    vxQueryObjectArray(img_arr, VX_OBJECT_ARRAY_NUMITEMS, &arr_len, sizeof(vx_size));

    for (i = 0; i < arr_len; i++)
    {
      vx_rectangle_t rect;
      vx_imagepatch_addressing_t image_addr;
      vx_map_id map_id;
      void * data_ptr;
      vx_uint32  img_width;
      vx_uint32  img_height;
      vx_image   out_img;
      vx_uint32  num_bytes;

      out_img = (vx_image)vxGetObjectArrayItem(img_arr, i);

      vxQueryImage(out_img, VX_IMAGE_WIDTH, &img_width, sizeof(vx_uint32));
      vxQueryImage(out_img, VX_IMAGE_HEIGHT, &img_height, sizeof(vx_uint32));

      rect.start_x = 0;
      rect.start_y = 0;
      rect.end_x = img_width;
      rect.end_y = img_height;
      status = vxMapImagePatch(out_img,
                               &rect,
                               0,
                               &map_id,
                               &image_addr,
                               &data_ptr,
                               VX_READ_ONLY,
                               VX_MEMORY_TYPE_HOST,
                               VX_NOGAP_X);

      //Copy Luma
      num_bytes = fwrite(data_ptr,1,img_width*img_height, fp);

      if(num_bytes != (img_width*img_height))
        printf("Luma bytes written = %d, expected = %d", num_bytes, img_width*img_height);

      vxUnmapImagePatch(out_img, map_id);

      rect.start_x = 0;
      rect.start_y = 0;
      rect.end_x = img_width;
      rect.end_y = img_height / 2;
      status = vxMapImagePatch(out_img,
                               &rect,
                               1,
                               &map_id,
                               &image_addr,
                               &data_ptr,
                               VX_READ_ONLY,
                               VX_MEMORY_TYPE_HOST,
                               VX_NOGAP_X);


      //Copy CbCr
      num_bytes = fwrite(data_ptr,1,img_width*img_height/2, fp);

      if(num_bytes != (img_width*img_height/2))
        printf("CbCr bytes written = %d, expected = %d", num_bytes, img_width*img_height/2);

      vxUnmapImagePatch(out_img, map_id);

      vxReleaseImage(&out_img);
    }

    fclose(fp);
  }

  return(status);
}

void scale_set_coeff(tivx_vpac_msc_coefficients_t *coeff, uint32_t interpolation)
{
    uint32_t i;
    uint32_t idx;
    uint32_t weight;

    idx = 0;
    coeff->single_phase[0][idx ++] = 0;
    coeff->single_phase[0][idx ++] = 0;
    coeff->single_phase[0][idx ++] = 256;
    coeff->single_phase[0][idx ++] = 0;
    coeff->single_phase[0][idx ++] = 0;
    idx = 0;
    coeff->single_phase[1][idx ++] = 0;
    coeff->single_phase[1][idx ++] = 0;
    coeff->single_phase[1][idx ++] = 256;
    coeff->single_phase[1][idx ++] = 0;
    coeff->single_phase[1][idx ++] = 0;

    if (VX_INTERPOLATION_BILINEAR == interpolation)
    {
        idx = 0;
        for(i=0; i<32; i++)
        {
            weight = i<<2;
            coeff->multi_phase[0][idx ++] = 0;
            coeff->multi_phase[0][idx ++] = 0;
            coeff->multi_phase[0][idx ++] = 256-weight;
            coeff->multi_phase[0][idx ++] = weight;
            coeff->multi_phase[0][idx ++] = 0;
        }
        idx = 0;
        for(i=0; i<32; i++)
        {
            weight = (i+32)<<2;
            coeff->multi_phase[1][idx ++] = 0;
            coeff->multi_phase[1][idx ++] = 0;
            coeff->multi_phase[1][idx ++] = 256-weight;
            coeff->multi_phase[1][idx ++] = weight;
            coeff->multi_phase[1][idx ++] = 0;
        }
        idx = 0;
        for(i=0; i<32; i++)
        {
            weight = i<<2;
            coeff->multi_phase[2][idx ++] = 0;
            coeff->multi_phase[2][idx ++] = 0;
            coeff->multi_phase[2][idx ++] = 256-weight;
            coeff->multi_phase[2][idx ++] = weight;
            coeff->multi_phase[2][idx ++] = 0;
        }
        idx = 0;
        for(i=0; i<32; i++)
        {
            weight = (i+32)<<2;
            coeff->multi_phase[3][idx ++] = 0;
            coeff->multi_phase[3][idx ++] = 0;
            coeff->multi_phase[3][idx ++] = 256-weight;
            coeff->multi_phase[3][idx ++] = weight;
            coeff->multi_phase[3][idx ++] = 0;
        }
    }
    else /* STR_VX_INTERPOLATION_NEAREST_NEIGHBOR */
    {
        idx = 0;
        for(i=0; i<32; i++)
        {
            coeff->multi_phase[0][idx ++] = 0;
            coeff->multi_phase[0][idx ++] = 0;
            coeff->multi_phase[0][idx ++] = 256;
            coeff->multi_phase[0][idx ++] = 0;
            coeff->multi_phase[0][idx ++] = 0;
        }
        idx = 0;
        for(i=0; i<32; i++)
        {
            coeff->multi_phase[1][idx ++] = 0;
            coeff->multi_phase[1][idx ++] = 0;
            coeff->multi_phase[1][idx ++] = 0;
            coeff->multi_phase[1][idx ++] = 256;
            coeff->multi_phase[1][idx ++] = 0;
        }
        idx = 0;
        for(i=0; i<32; i++)
        {
            coeff->multi_phase[2][idx ++] = 0;
            coeff->multi_phase[2][idx ++] = 0;
            coeff->multi_phase[2][idx ++] = 256;
            coeff->multi_phase[2][idx ++] = 0;
            coeff->multi_phase[2][idx ++] = 0;
        }
        idx = 0;
        for(i=0; i<32; i++)
        {
            coeff->multi_phase[3][idx ++] = 0;
            coeff->multi_phase[3][idx ++] = 0;
            coeff->multi_phase[3][idx ++] = 0;
            coeff->multi_phase[3][idx ++] = 256;
            coeff->multi_phase[3][idx ++] = 0;
        }
    }
}
