/******************************************************************************
* Copyright (c) 2020-2022 Xilinx, Inc.  All rights reserved.
* Copyright (C) 2023-2024 Advanced Micro Devices, Inc. All rights reserved.
*
* SPDX-License-Identifier: MIT
******************************************************************************/


/*****************************************************************************/
/**
*
* @file pdi-parsing.c
* @addtogroup test PDI parsing
* @{
* @cond pdi-parsing
* This is the file which contains general commands.
*
* @note
* @endcond
*
******************************************************************************/

/***************************** Include Files *********************************/
#include <stdio.h>
#include <string.h>
#include "cdo_cmd.h"
#include "load_pdi.h"
#include "pdi-transform.h"
#include "platform-hw-config.h"
#include "pdi-parsing-debug.h"
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>

/************************** Constant Definitions *****************************/

/**************************** Type Definitions *******************************/

/***************** Macros (Inline Functions) Definitions *********************/
/************************** Variable Definitions *****************************/
#ifndef __LX6__
extern const char binary_aie_pdi_start[];
extern const char binary_aie_pdi_end[];
#else
#define DRAM_MGMT_BASE_ADDR 0x18000000U
#endif
extern void SetDebugPdi(uint32_t* Pdi, uint32_t len, uint8_t checkDmaData);
extern uint32_t GetPdiOffset();
int SetChecksum(void *Buffer)
{
  const uint32_t Len = XIH_IHT_LEN / XIH_PRTN_WORD_LEN;
  // int Status;
  uint32_t Checksum = 0U;
  uint32_t Count = 0;
  uint32_t *BufferPtr = (uint32_t *)Buffer;

  /* Len has to be at least equal to 2 */
  if (Len < 2U) {
    return XCDO_INVALID_ARGS;
  }

  /*
   * Checksum = ~(X1 + X2 + X3 + .... + Xn)
   * Calculate the checksum
   */
  for (Count = 0U; Count < (Len - 1U); Count++) {
    /*
     * Read the word from the header
     */
    Checksum += BufferPtr[Count];
  }

  /* Invert checksum */
  Checksum ^= 0xFFFFFFFFU;

  /* Validate the checksum */
  BufferPtr[Len - 1U] = Checksum;
  return XCDO_OK;
}

int SetHeaderChecksum(void *CdoPtr)
{
  uint32_t *CdoHdr = (uint32_t *)CdoPtr;
  uint32_t CheckSum = 0U;
  uint32_t Index = 0;

  for (Index = 0U; Index < (XCDO_CDO_HDR_LEN - 1U); Index++) {
    CheckSum += CdoHdr[Index];
  }

  /* Invert checksum */
  CheckSum ^= 0xFFFFFFFFU;
  CdoHdr[Index] = CheckSum;

  return XCDO_OK;
}

void test_read_pdi(char* pdi, char** data, int* len)
{
  #define BUF_SIZE (1024*1024)
  *data = NULL;
  *len = 0;
  FILE *fp = NULL;
#if defined(_MSC_VER)
  errno_t err = fopen_s(&fp, pdi, "rb");  // open in binary mode
  if (err != 0 || fp == NULL) {
    printf("%s create failed Error Number % d\n", pdi, err);
    return;
  }
#else
  fp = fopen(pdi, "rb");  // open in binary mode
  if (fp == NULL) {
    printf("%s create failed Error Number % d\n", pdi, errno);
    return;
  }
#endif

  *data = (char *)malloc((size_t)BUF_SIZE);
  *len = (int)fread(*data, 1, (size_t)BUF_SIZE, fp);
  fclose(fp); // NOLINT
}


// cdo_common.h
FILE* file_pointer;

// Main entry point for pdi transformation
// Client should call this API
// Within the function, XPdi_Compress_Transform is the most important call
int pdi_transform(char* pdi_file,  char* pdi_file_out, const char* out_file)
{
   if (!out_file || (out_file[0] == '\0')) { 
     file_pointer = stdout;
   } else {
#if defined(_MSC_VER)
     fopen_s(&file_pointer, out_file, "a");
#else
     file_pointer = fopen(out_file, "a");
#endif
   }

   // Set the file stream to line-buffered mode
   (void)setvbuf(file_pointer, NULL, _IOLBF, BUFSIZ);

  int Ret = 0;
  printf("Get pdi file %s, do tranform pdi check and parsing.\n", pdi_file);
  int len = 0;
  char *data = NULL;
  printf("\ntest_read_pdi\n");
  test_read_pdi(pdi_file, &data, &len);
  printf("test_read_pdi done\n");

  XPdiLoad PdiLoad = {0};
  if (len) {
    PdiLoad.PdiLen = len;
    PdiLoad.PdiPtr = data;
  } else {
    printf("Invalid PDI file\n");
    if (data) free(data);
    return -1;
  }
  PdiLoad.BasePtr = 0;

  XCdo_Print("\n--------------------------------------------------\n");
  XCdo_Print("Pdi parsing... file = %s; len = %u\n", pdi_file, PdiLoad.PdiLen);
  #define MAX_DEBUG_PDI_LEN (1024*500)
  const uint8_t cmpDmaData = 1;
  // The default stack size on Windows is 1MB
  // We should not delcare local variables with large size on the stack
  // The following code will cause stack overflow on Windows
  // Instead, we should use malloc to allocate memory on heap
  /*
  char DebugPdi[MAX_DEBUG_PDI_LEN], DebugTransformPdi[MAX_DEBUG_PDI_LEN];
  memset((char*)DebugPdi, 0, (size_t)MAX_DEBUG_PDI_LEN);
  memset((char*)DebugTransformPdi, 0, (size_t)MAX_DEBUG_PDI_LEN);
  */
  char *DebugPdi = (char*)malloc((size_t)MAX_DEBUG_PDI_LEN);
  char *DebugTransformPdi = (char*)malloc((size_t)MAX_DEBUG_PDI_LEN);
  memset(DebugPdi, 0, (size_t)MAX_DEBUG_PDI_LEN);
  memset(DebugTransformPdi, 0, (size_t)MAX_DEBUG_PDI_LEN);
  
  SetDebugPdi((uint32_t *)DebugPdi, MAX_DEBUG_PDI_LEN, cmpDmaData);
  XCdo_Print("\n\nLoad original pdi\n");
  XPdi_Load(&PdiLoad);
  XCdo_Print("Load original pdi done\n");
  
  SetDebugPdi((uint32_t *)DebugTransformPdi, MAX_DEBUG_PDI_LEN, cmpDmaData);
  XPdi_Compress_Transform(&PdiLoad, pdi_file_out);

  //Verify the data
  for (int i = 0; i < MAX_DEBUG_PDI_LEN; i++) {
    if(DebugTransformPdi[i] != DebugPdi[i]) {
      XCdo_Print("num %d value is mismatch\n", i);
      printf("Generating Original PDI log\n");
      errorLog("OriginalError.log",(uint32_t *)DebugPdi, i);
      XCdo_Print("Generating Transformed PDI log\n");
      errorLog("TransformError.log",(uint32_t *)DebugTransformPdi, i);

      assert(DebugTransformPdi[i] == DebugPdi[i]);
    }
  }

  free(DebugPdi);
  free(DebugTransformPdi);

  printf("\nThe transform PDI check pass!!! Transformed PDI is consistent with traditional PDI\n");
  if (data) free(data);
  return Ret;
}

/**
 * @}
 * @endcond
 */

 /** @} */
