/*
 *
 * Copyright (c) 2017 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 <utils/console_io/include/app_get.h>
#include "avp_common.h"
#include "avp_tidl_module.h"

//#define APP_DEBUG

typedef struct {
  vx_node  node;

  vx_array config;
  tivxImgPreProcParams params;

  sTIDL_IOBufDesc_t ioBufDesc;

  vx_uint32 num_input_tensors;
  vx_uint32 num_output_tensors;

  vx_object_array  output_tensor_arr;

  vx_int32 graph_parameter_index;

  vx_char objName[APP_MAX_FILE_PATH];

} PreProcObj;

typedef struct {

    PreProcObj segPreProcObj;

    TIDLObj segTIDLObj;

    vx_char input_file_path[APP_MAX_FILE_PATH];
    vx_char output_file_path[APP_MAX_FILE_PATH];

    vx_df_image df_image;
    void *data_ptr;

    /* OpenVX references */
    vx_context context;
    vx_graph   graph;

    vx_int32 en_out_img_write;
    vx_int32 en_out_log_write;

    vx_int32 num_classes[TIVX_PIXEL_VIZ_MAX_CLASS];
    int32_t enable_sem_seg;

    void *bmp_context;
    vx_uint32 img_width;
    vx_uint32 img_height;
    vx_uint32 img_stride;

    uint8_t *pInPlanes;
    uint8_t *pOutPlanes;

} AppObj;

AppObj gAppObj;

static void app_parse_cmd_line_args(AppObj *obj, vx_int32 argc, vx_char *argv[]);
static int app_init(AppObj *obj);
static void app_deinit(AppObj *obj);
static vx_status app_create_graph(AppObj *obj);
static vx_status app_verify_graph(AppObj *obj);
static void app_delete_graph(AppObj *obj);

static void writeOutput(AppObj *obj)
{
  // TIDL output image tensor
    vx_tensor output_tensor[APP_MAX_TENSORS];
    TIDLObj *tidlObj = &obj->segTIDLObj;
    output_tensor[0] = (vx_tensor)vxGetObjectArrayItem((vx_object_array)tidlObj->output1_tensor_arr, 0);
    vx_status tidl_op_status;
    vx_size tidl_op_num_dims;
    tidl_op_status = vxQueryTensor(output_tensor[0],VX_TENSOR_NUMBER_OF_DIMS,&tidl_op_num_dims,sizeof(tidl_op_num_dims));
    if(VX_SUCCESS == tidl_op_status){
      printf("TIDL output number of dims = %lu\n",tidl_op_num_dims);
    }
    vx_size tidl_op_dims[tidl_op_num_dims];
    tidl_op_status = vxQueryTensor(output_tensor[0],VX_TENSOR_DIMS,&tidl_op_dims,sizeof(tidl_op_dims));
    if(VX_SUCCESS == tidl_op_status){
      for(int i=0;i<tidl_op_num_dims;i++){
          printf("TIDL output dims[%d] = %lu\n",i,tidl_op_dims[i]);
      }
    }
    vx_enum tidl_op_data_type;
    tidl_op_status = vxQueryTensor(output_tensor[0],VX_TENSOR_DATA_TYPE,&tidl_op_data_type,sizeof(tidl_op_data_type));
    if(VX_SUCCESS == tidl_op_status){
        printf("TIDL output data type = %d\n",tidl_op_data_type);
    }

    // accessing tensor data
    vx_size start[3];
    start[0] = start[1] = start[2] = 0;
    vx_map_id tidl_op_map_id;
    vx_size output_strides[3];
    output_strides[0] = output_strides[1] = output_strides[2] = 1;
    void *output_buffer;

    FILE * imgFile;
    char * imgName = obj->output_file_path;

    imgFile = fopen(imgName,"wb");
    if(imgFile == NULL)
    {
      printf(" Could Not open img %s\n", obj->output_file_path);
    }

    tivxMapTensorPatch(output_tensor[0], 3, start, tidl_op_dims, &tidl_op_map_id, output_strides, &output_buffer, VX_READ_ONLY, VX_MEMORY_TYPE_HOST);
    {
      fwrite(output_buffer, 1, obj->img_width*obj->img_height, imgFile);
      fclose(imgFile);
    }
    tivxUnmapTensorPatch(output_tensor[0],tidl_op_map_id);
    vxReleaseTensor(&output_tensor[0]);
}

static void readInput(AppObj *obj)
{

    vx_tensor input_tensor[APP_MAX_TENSORS];
    vx_object_array input_tensor_arr = obj->segPreProcObj.output_tensor_arr;
    input_tensor[0] = (vx_tensor)vxGetObjectArrayItem((vx_object_array)input_tensor_arr, 0);
    vx_status tidl_ip_status;
    vx_size tidl_ip_num_dims;
    tidl_ip_status = vxQueryTensor(input_tensor[0],VX_TENSOR_NUMBER_OF_DIMS,&tidl_ip_num_dims,sizeof(tidl_ip_num_dims));
    if(VX_SUCCESS == tidl_ip_status){
      printf("TIDL input number of dims = %lu\n",tidl_ip_num_dims);
    }
    vx_size tidl_ip_dims[tidl_ip_num_dims];
    tidl_ip_status = vxQueryTensor(input_tensor[0],VX_TENSOR_DIMS,&tidl_ip_dims,sizeof(tidl_ip_dims));
    if(VX_SUCCESS == tidl_ip_status){
      obj->img_width = tidl_ip_dims[0];
      obj->img_height = tidl_ip_dims[1];
      for(int i=0;i<tidl_ip_num_dims;i++){
          printf("TIDL input dims[%d] = %lu\n",i,tidl_ip_dims[i]);
      }
    }
    vx_enum tidl_ip_data_type;
    tidl_ip_status = vxQueryTensor(input_tensor[0],VX_TENSOR_DATA_TYPE,&tidl_ip_data_type,sizeof(tidl_ip_data_type));
    if(VX_SUCCESS == tidl_ip_status){
        printf("TIDL input data type = %d\n",tidl_ip_data_type);
    }

    // accessing tensor data
    vx_size start_in[3];
    start_in[0] = start_in[1] = start_in[2] = 0;
    vx_map_id tidl_ip_map_id;
    vx_size input_strides[3];
    input_strides[0] = input_strides[1] = input_strides[2] = 1;
    void *input_buffer;
    char *bmpFilename = obj->input_file_path;

    char * pR;
    char * pG;
    char * pB;

    tivxMapTensorPatch(input_tensor[0], 3, start_in, tidl_ip_dims, &tidl_ip_map_id, input_strides, &input_buffer, VX_WRITE_ONLY, VX_MEMORY_TYPE_HOST);
    {
      uint8_t *tidl_in_tensor_vals;
      tidl_in_tensor_vals = (uint8_t *)input_buffer;

      pB = (char*)input_buffer + tidl_ip_dims[0]*tidl_ip_dims[1]*0;
      pG = (char*)input_buffer + tidl_ip_dims[0]*tidl_ip_dims[1]*1;
      pR = (char*)input_buffer + tidl_ip_dims[0]*tidl_ip_dims[1]*2;  
    
      uint32_t width, height, stride;
      vx_df_image df_image;
      void *data_ptr = NULL;


      void *bmp_file_context;
      tivx_utils_bmp_file_read(
                bmpFilename,
                vx_false_e,
                &width, &height, &stride, &df_image, &data_ptr,
                &bmp_file_context);


      int i;
      char *bmpPtr = data_ptr;
      for(i=0; i<tidl_ip_dims[0]*tidl_ip_dims[1]; i++)
      {
        pR[i] = *bmpPtr++;
        pG[i] = *bmpPtr++;
        pB[i] = *bmpPtr++;
      }
      tivx_utils_bmp_file_read_release(bmp_file_context);
    }
    tivxUnmapTensorPatch(input_tensor[0],tidl_ip_map_id);
    vxReleaseTensor(&input_tensor[0]);
}

static void app_show_usage(vx_int32 argc, vx_char* argv[])
{
    printf("\n");
    printf(" TIDL AVP Demo - (c) Texas Instruments 2018\n");
    printf(" ========================================================\n");
    printf("\n");
    printf(" Usage,\n");
    printf("  %s --cfg <config file>\n", argv[0]);
    printf("\n");
}


static void app_set_cfg_default(AppObj *obj)
{
    snprintf(obj->segTIDLObj.config_file_path,APP_MAX_FILE_PATH, ".");
    snprintf(obj->segTIDLObj.network_file_path,APP_MAX_FILE_PATH, ".");
}

static void app_parse_cfg_file(AppObj *obj, vx_char *cfg_file_name)
{
    FILE *fp = fopen(cfg_file_name, "r");
    vx_char line_str[1024];
    vx_char *token;

    if(fp==NULL)
    {
        printf("# ERROR: Unable to open config file [%s]\n", cfg_file_name);
        exit(0);
    }

    while(fgets(line_str, sizeof(line_str), fp)!=NULL)
    {
        vx_char s[]=" \t";

        if (strchr(line_str, '#'))
        {
            continue;
        }

        /* get the first token */
        token = strtok(line_str, s);
        if(token != NULL)
        {
          if(strcmp(token, "tidl_seg_config")==0)
          {
              token = strtok(NULL, s);
              if(token != NULL)
              {
                token[strlen(token)-1]=0;
                strcpy(obj->segTIDLObj.config_file_path, token);
              }
          }
          else
          if(strcmp(token, "tidl_seg_network")==0)
          {
              token = strtok(NULL, s);
              if(token != NULL)
              {
                token[strlen(token)-1]=0;
                strcpy(obj->segTIDLObj.network_file_path, token);
              }
          }
          else
          if(strcmp(token, "num_classes")==0)
          {
              vx_int32 i;

              for(i = 0; i < TIVX_PIXEL_VIZ_MAX_CLASS; i++)
              {
                token = strtok(NULL, s);
                if(token != NULL)
                {
                  obj->num_classes[i] = atoi(token);
                }
                else
                {
                  break;
                }
              }
              for(;i < TIVX_PIXEL_VIZ_MAX_CLASS; i++)
              {
                obj->num_classes[i] = 0;
              }
          }
          else
          if(strcmp(token, "en_out_img_write")==0)
          {
              token = strtok(NULL, s);
              if(token != NULL)
              {
                obj->en_out_img_write = atoi(token);
              }
          }
          else
          if(strcmp(token, "input_file_path")==0)
          {
              token = strtok(NULL, s);
              if(token != NULL)
              {
                token[strlen(token)-1]=0;
                strcpy(obj->input_file_path, token);
              }
          }
          else
          if(strcmp(token, "output_file_path")==0)
          {
              token = strtok(NULL, s);
              if(token != NULL)
              {
                token[strlen(token)-1]=0;
                strcpy(obj->output_file_path, token);
              }
          }
          else
          if(strcmp(token, "enable_sem_seg")==0)
          {
              token = strtok(NULL, s);
              if(token != NULL)
              {
                obj->enable_sem_seg = atoi(token);
              }
          }
        }
    }

    fclose(fp);

}

static void app_parse_cmd_line_args(AppObj *obj, vx_int32 argc, vx_char *argv[])
{
    vx_int32 i;

    printf("In app_parse_cmd_line_args\n");

    app_set_cfg_default(obj);

    if(argc==1)
    {
        app_show_usage(argc, argv);
        exit(0);
    }

    for(i=0; i<argc; i++)
    {
        if(strcmp(argv[i], "--cfg")==0)
        {
            i++;
            if(i>=argc)
            {
                app_show_usage(argc, argv);
            }
            app_parse_cfg_file(obj, argv[i]);
            printf("After app_parse_cfg_file\n");
            break;
        }
        else
        if(strcmp(argv[i], "--help")==0)
        {
            app_show_usage(argc, argv);
            exit(0);
        }
    }

    return;
}

vx_int32 app_tidl_avp_main(vx_int32 argc, vx_char* argv[])
{
    AppObj *obj = &gAppObj;

    /*Config parameter reading*/
	#ifdef APP_DEBUG
    printf("Read config file\n");
	#endif

    app_parse_cmd_line_args(obj, argc, argv);

	#ifdef APP_DEBUG
    printf("Before app_init\n");
	#endif

    app_init(obj);

    #ifdef APP_DEBUG
    printf("App Init Done! \n");
    #endif

    app_create_graph(obj);

    #ifdef APP_DEBUG
    printf("App Create Graph Done! \n");
    #endif

    app_verify_graph(obj);

    #ifdef APP_DEBUG
    printf("App Verify Graph Done! \n");
    #endif

    readInput(obj);

    vxProcessGraph(obj->graph);

    writeOutput(obj);

    #ifdef APP_DEBUG
    printf("App Run Graph Done! \n");
    #endif

    app_delete_graph(obj);

    #ifdef APP_DEBUG
    printf("App Delete Graph Done! \n");
    #endif

    app_deinit(obj);

    #ifdef APP_DEBUG
    printf("App De-init Done! \n");
    #endif

    return 0;
}


/* changes made start */

static void createOutputTensors(vx_context context, vx_user_data_object config, vx_tensor output_tensors[], vx_size data_type)
{
    vx_size   input_sizes[APP_MAX_TENSOR_DIMS];
    vx_map_id map_id_config;

    vx_uint32 id;

    tivxTIDLJ7Params *tidlParams;
    sTIDL_IOBufDesc_t *ioBufDesc;

    vxMapUserDataObject(config, 0, sizeof(tivxTIDLJ7Params), &map_id_config,
                      (void **)&tidlParams, VX_READ_ONLY, VX_MEMORY_TYPE_HOST, 0);

    ioBufDesc = (sTIDL_IOBufDesc_t *)&tidlParams->ioBufDesc;
    for(id = 0; id < ioBufDesc->numInputBuf; id++) {

        input_sizes[0] = ioBufDesc->inWidth[id]  + ioBufDesc->inPadL[id] + ioBufDesc->inPadR[id];
        input_sizes[1] = ioBufDesc->inHeight[id] + ioBufDesc->inPadT[id] + ioBufDesc->inPadB[id];
        input_sizes[2] = ioBufDesc->inNumChannels[id];
#ifdef APP_DEBUG
        printf("inPadL : %d\n",ioBufDesc->inPadL[id]);
        printf("inPadR : %d\n",ioBufDesc->inPadR[id]);
        printf("inPadT : %d\n",ioBufDesc->inPadT[id]);
        printf("inPadB : %d\n",ioBufDesc->inPadB[id]);

        printf("inWidth : %d\n",ioBufDesc->inWidth[id]);
        printf("inHeight : %d\n",ioBufDesc->inHeight[id]);
        printf("inNumChannels : %d\n",ioBufDesc->inNumChannels[id]);

	      printf("outPadL : %d\n",ioBufDesc->outPadL[id]);
        printf("outPadR : %d\n",ioBufDesc->outPadR[id]);
        printf("outPadT : %d\n",ioBufDesc->outPadT[id]);
        printf("outPadB : %d\n",ioBufDesc->outPadB[id]);
#endif



        output_tensors[id] = NULL;
        if (data_type ==  VX_TYPE_UINT8)
        {
            output_tensors[id] = vxCreateTensor(context, 3, input_sizes, VX_TYPE_UINT8, 0);
        }
        if ( data_type == VX_TYPE_UINT16)
        {
            output_tensors[id] = vxCreateTensor(context, 3, input_sizes, VX_TYPE_UINT16, 0);
        }
        if ( data_type == VX_TYPE_FLOAT32)
        {
            output_tensors[id] = vxCreateTensor(context, 3, input_sizes, VX_TYPE_FLOAT32, 0);
        }
    }

    vxUnmapUserDataObject(config, map_id_config);

    return;
}

vx_status pre_proc_out(vx_context context, PreProcObj *preProcObj, vx_user_data_object config)
{
  vx_status status = VX_SUCCESS;

  vx_map_id map_id_config;
  sTIDL_IOBufDesc_t *ioBufDesc;
  tivxTIDLJ7Params *tidlParams;
  vx_tensor output_tensors[APP_MAX_TENSORS];
  vx_size data_type;

  vxMapUserDataObject(config, 0, sizeof(tivxTIDLJ7Params), &map_id_config,
                    (void **)&tidlParams, VX_READ_ONLY, VX_MEMORY_TYPE_HOST, 0);

  ioBufDesc = (sTIDL_IOBufDesc_t *)&tidlParams->ioBufDesc;
  memcpy(&preProcObj->ioBufDesc, ioBufDesc, sizeof(sTIDL_IOBufDesc_t));

  preProcObj->params.pad_pixel[0] = ioBufDesc->inPadL[0]; //Left
  preProcObj->params.pad_pixel[1] = ioBufDesc->inPadT[0]; //Top
  preProcObj->params.pad_pixel[2] = ioBufDesc->inPadR[0]; //Right
  preProcObj->params.pad_pixel[3] = ioBufDesc->inPadB[0]; //Bottom

  vxUnmapUserDataObject(config, map_id_config);

  preProcObj->num_input_tensors = ioBufDesc->numInputBuf;
  preProcObj->num_output_tensors = ioBufDesc->numOutputBuf;

  data_type = VX_TYPE_UINT8;
  createOutputTensors(context, config, output_tensors, data_type);
  preProcObj->output_tensor_arr = vxCreateObjectArray(context, (vx_reference)output_tensors[0], NUM_CH);
  vxReleaseTensor(&output_tensors[0]);

  return status;
}

void deinit_pre_proc_out(PreProcObj *preProcObj)
{
  vxReleaseObjectArray(&preProcObj->output_tensor_arr);
}


static int app_init(AppObj *obj)
{
    int status = VX_SUCCESS;
    // app_grpx_init_prms_t grpx_prms;

    /* Create OpenVx Context */
    obj->context = vxCreateContext();
    APP_ASSERT_VALID_REF(obj->context);

    tivxHwaLoadKernels(obj->context);
    tivxImgProcLoadKernels(obj->context);
    tivxTIDLLoadKernels(obj->context);



    /* Initialize TIDL first to get tensor I/O information from network */

    printf("in app init\n");

    app_init_tidl_pc(obj->context, &obj->segTIDLObj, "Seg_TIDLObj");

    /* Update pre/post proc modules with tensor I/O information */
    pre_proc_out(obj->context, &obj->segPreProcObj, obj->segTIDLObj.config);


    return status;
}

static void app_deinit(AppObj *obj)
{

    deinit_pre_proc_out(&obj->segPreProcObj);
    app_deinit_tidl_pc(&obj->segTIDLObj);
    tivxTIDLUnLoadKernels(obj->context);
    tivxImgProcUnLoadKernels(obj->context);
    tivxHwaUnLoadKernels(obj->context);
    vxReleaseContext(&obj->context);
}

static void app_delete_graph(AppObj *obj)
{
    app_delete_tidl_pc(&obj->segTIDLObj);
    vxReleaseGraph(&obj->graph);
}

static vx_status app_create_graph(AppObj *obj)
{
    vx_status status = VX_SUCCESS;
    vx_graph_parameter_queue_params_t graph_parameters_queue_params_list[2];
    vx_int32 graph_parameter_index;

    obj->graph = vxCreateGraph(obj->context);
    status = vxGetStatus((vx_reference)obj->graph);

    vxSetReferenceName((vx_reference)obj->graph, "AVPGraph");

    if(obj->enable_sem_seg == 1) {
      if(VX_SUCCESS == status) {
        status = app_create_graph_tidl_pc(obj->context, obj->graph, &obj->segTIDLObj, obj->segPreProcObj.output_tensor_arr);
      }
    }

    return status;
}

static vx_status app_verify_graph(AppObj *obj)
{
    vx_status status = VX_SUCCESS;

    // ScalerObj *scalerObj;
    vx_reference refs[1];

    status = vxVerifyGraph(obj->graph);

    #ifdef APP_DEBUG
    printf("App Verify Graph Done!\n");
    #endif

    #if 1
    if(VX_SUCCESS == status)
    {
      status = tivxExportGraphToDot(obj->graph,".", "vx_app_tidl_avp");
    }
    #endif


    #ifdef APP_DEBUG
    printf("App Send MSC Command Done!\n");
    #endif

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

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

    return status;
}

