// Copyright (C) 2003-2026, Foxit Software Inc..
// All Rights Reserved.
//
// http://www.foxitsoftware.com
//
// The following code is copyrighted and contains proprietary information and trade secrets of Foxit Software Inc..
// You cannot distribute any part of Foxit PDF SDK to any third party or general public,
// unless there is a separate license agreement with Foxit Software Inc. which explicitly grants you such rights.
//
// This file contains an example to demonstrate how to use Foxit PDF SDK to get preflight keys and analyze or fixup PDF file.

#include <time.h>
#include <direct.h>
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>

#include "../../../include/fs_basictypes_c.h"
#include "../../../include/fs_common_c.h"
#include "../../../include/fs_compliancecommon_c.h"
#include "../../../include/fs_pdfa_c.h"
#include "../../../include/fs_pdfcompliance_c.h"

#ifndef TRUE
#define TRUE 1
#endif

#ifndef FALSE
#define FALSE 0
#endif

#ifndef NULL
#define NULL (void*)0
#endif

#define MAX_FILE_PATH 260

static const char* sn = "bPxThFlBAYifO3yiW0C51bKbjPap1zHaf5aG5sX378TjFvB7Q0802A==";
static const char* key = "8f3g1sGNtR8NAgfSNv+BvaJWy2fnrw36NZvzh8GUxYXvs+/YczAk4e34yqlOnmL4asgxGMO7+rlOcSHUSSKagx5g4G7VEXfkYLXnDDx77FOOedZPrFFcOCu1vT4+wOKzy1zkORq9cKtiFf44byH5sfRqf+OvELjep2teT9eF9yPCsSw2vRKljXlPy40DMP9XX1O5OTe+mSG3RvwKUMb0+RpG3YobM87j+/ynTDlJMMXw4vgwuTb1ZuAtlvkTdFz7HDLV81C6TYnn73ddHeGD3qYWBVsD4uJkDwyyH/SWr7QX0HSgVg8HSZqnab1VGdAUYxKY2vGWeOTJdu6qDGb5ZEb9O3kZZg+9LYAmUKzDfCYwfIsYZ8Y9LPC4wMx70kIq7JpNnzPvigrdshV8/ew1F0+qP/P5Z1EB53vDVrp2LVSuL873Wv4g3PzjZlNH+1qgT87hC+Buto4qsSUfP5Y2z0uApu+c/TfttCXbeMLSsw9TY1HQD3mkrZExMaQqRrAuXAcZWdvBnVuG3DCZmm8q0DLxJiCfYfViAU/AkbeKMIGaTJdrvIXBcssfde4nmU6qr0M4x4+Hs2ESddtY2QRtLAUBq/7h7GJlJHkEZr8ueaUy/6D/2vvAWz6wMLgmjlPbDTLqzfnCJylHCPtcJmu1SPPT9v1VreBX3+zNWcIT8HueyHEiUniqzwWkRKaotrnNb/zhnHLPpgaBSwKjyi8rOzzVwu/nY84G9ye4jsVjmkIRItKDrW7z22iS+QQuTwfqLVuMB0OD4jMkPCC2slzPfEeZnuh4+Rewln2qLRJVgI72zJ20OBWAelGSpO1Ii2Y3gOvojdmtUwx6R3Z1qFNWTTB25o9ytcux8BLOtTCkA8T8xaT4N339OTCsu/TmfahIDixKvr8cDa6F2WkBT8SOfv64W9PgiNv5B/BMOD2VWYaAl0N7yD0NkYYg/cDWRC4dfpdeip1jrr0k3mND0oSpIkxC+FicZbaYYwRdtJ9OSVWJ5OE0voLv8HgLze8mw6s4TrN3z+a2NQCveMa2VRN7YcEF7agPrvb/qU+otMwHae/wM7+f+HFAaOW+WTfzfxIVFB8/A1DN0NiEX+em4G7nO5F5ZcvfYPpaaioe8Nn33BbVLtMjXqsOBeM8AXiVfpD6YO75j7BqnBUPnivvD8BxJaaIQZvVSuQmvGm0ynt03IYwE8Rq+jYsunvsZ86jh5BiUR+Nm7sK0Qe9BdZn/7jdqohMv9czfMGUAscX+xBMunFbDpcRbWVpUADcm209L8Ndek2XY7YlCMxPgPVNvfWRF2jxehib9Tpd7zB5ZIDfAEH9zrpLfp29DLvCh2Yx8mH7xxdKG1O9Q0GdHphYHhej6smAcPm68R0GUQaTOLfXqNpueGJC1GAOI3Y4swjyt7jMTWcibnKLzwukWE5rnEDhn5n64JnyUM0ZFkRmtOiV";

static const wchar_t* output_path = L"../output_files/";
static const wchar_t* input_path = L"../input_files/";
static wchar_t output_directory[MAX_FILE_PATH];

void WriteWStr(FILE* file, FS_WSTR* text_content) {
  FS_BSTR dst_bstr;
  FSDK_WStr_UTF16LEEncode(text_content, &dst_bstr);

  if (dst_bstr.len == 0) return;
  fwrite((const char*)dst_bstr.str, sizeof(char), dst_bstr.len, file);
}

void WriteUnicode(FILE* file, const wchar_t* text_content) {
  FS_WSTR str;
  FS_BSTR dst_bstr;

  str.str = (wchar_t*)text_content;
  str.len = wcslen(text_content);
  FSDK_WStr_UTF16LEEncode(&str, &dst_bstr);

  if (dst_bstr.len == 0) return;
  fwrite((const char*)dst_bstr.str, sizeof(char), dst_bstr.len, file);
}

FILE* progressfile = NULL;

void gRelease(void* user_data) {
  if (user_data) free(user_data);
}

void gUpdateCurrentStateData(void* user_data, int current_rate, const wchar_t* current_state_string) {
  wchar_t temp_string[32];
  printf("Current rate: %d\n", current_rate);

  swprintf_s(temp_string, 32, L"Current rate:%d, state str:", current_rate);
  WriteUnicode(progressfile, temp_string);
  WriteUnicode(progressfile, current_state_string);
  WriteUnicode(progressfile, L"\r\n");
}

void ListPreflightKeys(FS_PREFLIGHT_HANDLE preflight, FSLibraryType library_type, FSOperateType operate_type) {
  FS_BSTR* group_arr = NULL;
  FS_UINT32 group_count = 0;
  FSDK_Preflight_GetGroupNamesArray(preflight, library_type, operate_type, NULL, &group_count);
  group_arr = (FS_BSTR*)malloc(sizeof(FS_BSTR) * group_count);
  for (int i = 0; i < group_count; i++)
    FSDK_BStr_Init(&group_arr[i]);
  FSDK_Preflight_GetGroupNamesArray(preflight, library_type, operate_type, group_arr, &group_count);
  for (int i = 0; i < group_count; i++) {
    FS_BSTR* item_array = NULL;
    FS_UINT32 item_count = 0;
    printf("Group name: %s\n", group_arr[i].str );
    FSDK_Preflight_GetItemKeysArray(preflight, group_arr[i].str, NULL, &item_count);
    item_array = (FS_BSTR*)malloc(sizeof(FS_BSTR) * item_count);
    for (int j = 0; j < item_count; j++)
      FSDK_BStr_Init(&item_array[j]);
    FSDK_Preflight_GetItemKeysArray(preflight, group_arr[i].str, item_array, &item_count);
    printf("    Item count: %d\n", item_count);
    for (int j = 0; j < item_count; j++) {
      printf("    Item name: %s\n", item_array[j].str);
    }
    for (int i = 0; i < item_count; i++)
      FSDK_BStr_Clear(&item_array[i]);
  }
  for (int i = 0; i < group_count; i++)
    FSDK_BStr_Clear(&group_arr[i]);
}

void AnalyzeAndFixup(FS_PREFLIGHT_HANDLE preflight, FSOperateType operate_type, FS_WSTR preflight_key, FS_BOOL is_fixed) {
  FSPreflightSettingData setting_data;
  setting_data.operate_type = operate_type;
  setting_data.preflight_key = preflight_key;
  setting_data.first_page_index = 0;
  setting_data.last_page_index = -1;
  wchar_t srcpath[MAX_FILE_PATH];
  swprintf_s(srcpath, MAX_FILE_PATH, L"%lsAboutFoxit.pdf", input_path);
  setting_data.src_pdf_path.str = srcpath;
  setting_data.src_pdf_path.len = wcslen(srcpath);
  wchar_t fixpath[MAX_FILE_PATH];
  swprintf_s(fixpath, MAX_FILE_PATH, L"%lsAboutFoxit_%ls_fixed.pdf", output_directory, preflight_key.str);
  setting_data.saved_pdf_path.str = fixpath;
  setting_data.saved_pdf_path.len = wcslen(fixpath);
  FSProgressCallback* convert_progress_callback = NULL;
  convert_progress_callback = (FSProgressCallback*)malloc(sizeof(FSProgressCallback));
  convert_progress_callback->user_data = convert_progress_callback;
  convert_progress_callback->Release = gRelease;
  convert_progress_callback->UpdateCurrentStateData = gUpdateCurrentStateData;

  wchar_t output_progress_file[MAX_FILE_PATH];
  swprintf_s(output_progress_file, MAX_FILE_PATH, L"%lsAboutFoxit_%ls_progress.txt", output_directory, preflight_key.str);
  _wfopen_s(&progressfile, output_progress_file, L"w+b");
  if (is_fixed)
    FSDK_Preflight_AnalyzeAndFixup(preflight, setting_data, convert_progress_callback);
  else
    FSDK_Preflight_Analyze(preflight, setting_data, convert_progress_callback);
  fclose(progressfile);
  progressfile = NULL;
}

void GenerateReport(FS_PREFLIGHT_HANDLE preflight, FSReportFileFormatType format_type, FS_WSTR preflight_key, const wchar_t* output_path) {
  FSPreflightReportSettingData report_setting_data;
  report_setting_data.report_file_format_type = format_type;
  wchar_t path[MAX_FILE_PATH];
  swprintf_s(path, MAX_FILE_PATH, L"%lsAboutFoxit_%ls_report.pdf", output_directory, preflight_key.str);
  report_setting_data.report_file_path.str = path;
  report_setting_data.report_file_path.len = wcslen(path);
  report_setting_data.to_generate_overview = TRUE;
  report_setting_data.to_highlight_problems = TRUE;
  report_setting_data.problems_highlight_method = e_FSProblemsHighlightMethodTransparentMasks;
  FS_BOOL return_value = FALSE;
  FSDK_Preflight_GenerateReport(preflight, report_setting_data, NULL, &return_value);
}

int main(int argc, char *argv[]) {
  FSErrorCode error_code = e_FSErrSuccess;
  wchar_t input_file[MAX_FILE_PATH];

  swprintf_s(output_directory, MAX_FILE_PATH, L"%lspreflight/", output_path);
  _wmkdir(output_directory);

  error_code = FSDK_Library_Initialize(sn, key);
  if (error_code != e_FSErrSuccess) {
    printf("Library Initialize Error: %d\n", error_code);
    return 1;
  }

  swprintf_s(input_file, MAX_FILE_PATH, L"%lsAboutFoxit.pdf", input_path);

  {
    // "compliance_resource_folder_path" is the path of compliance resource folder. Please refer to Developer Guide for more details.
    const wchar_t* compliance_resource_folder_path = L"";
    // If you use an authorization key for Foxit PDF SDK, please set a valid unlock code string to compliance_engine_unlockcode for ComplianceEngine.
    // If you use an trial key for Foxit PDF SDK, just keep compliance_engine_unlockcode as an empty string.
    const char* compliance_engine_unlockcode = "";

    wchar_t save_pdf_path[MAX_FILE_PATH];

    if (wcslen(compliance_resource_folder_path) == 0) {
      printf("compliance_resource_folder_path is still empty. Please set it with a valid path to compliance resource folder path.\n");
      return 1;
    }
    // Initialize compliance engine.
    error_code = FSDK_ComplianceEngine_Initialize(compliance_resource_folder_path, compliance_engine_unlockcode);
    if (error_code != e_FSErrSuccess) {
      switch (error_code) {
      case e_FSErrInvalidLicense:
        printf("[Failed] Compliance module is not contained in current Foxit PDF SDK keys.\n");
        break;
      default:
        printf("[Failed] Fail to initialize compliance engine. Error: %d\n", error_code);
        break;
      }
      return 1;
    }
    printf("ComplianceEngine is initialized.\n");

    // Set custom temp folder path for ComplianceEngine. 
    //ComplianceEngine::SetTempFolderPath(L"");
    // Set languages. If not set language to ComplianceEngine, "English" will be used as default.
    //ComplianceEngine::SetLanguage("Chinese-Simplified");

    {
      FS_PREFLIGHT_HANDLE preflight;
      error_code = FSDK_Preflight_Create(&preflight);
      // List all Foxit preflight keys.
      printf("Library Type: Foxit, Operate Type: Profiles \n");
      ListPreflightKeys(preflight, e_FSLibraryTypeFoxit, e_FSOperateTypeProfiles);
      printf("Library Type: Foxit, Operate Type: Checks \n");
      ListPreflightKeys(preflight, e_FSLibraryTypeFoxit, e_FSOperateTypeSingleChecks);
      printf("Library Type: Foxit, Operate Type: Fixups \n");;
      ListPreflightKeys(preflight, e_FSLibraryTypeFoxit, e_FSOperateTypeSingleFixups);

      FS_BOOL is_fixup = FALSE;
      FS_BOOL return_value = FALSE;
      // Analyze and fixup a PDF file.
      printf("== Profiles. ==\n");
      FSDK_Preflight_CanFixup(preflight, "pppp_ConverttoPDFA1a", &is_fixup);
      FS_WSTR str;
      str.str = L"pppp_ConverttoPDFA1a";
      str.len = wcslen(L"pppp_ConverttoPDFA1a");
      AnalyzeAndFixup(preflight, e_FSOperateTypeProfiles, str, is_fixup);
      GenerateReport(preflight, e_FSReportFileFormatTypePdf, str, output_directory);

      // Analyze a PDF file.
      printf("== Single Checks. == \n");
      str.str = L"R_6_Annotationinsidepagearea";
      str.len = wcslen(L"R_6_Annotationinsidepagearea");
      AnalyzeAndFixup(preflight, e_FSOperateTypeSingleChecks, str, FALSE);
      GenerateReport(preflight, e_FSReportFileFormatTypePdf, str, output_directory);

      //Fixup a PDF file.
      printf("== Single Fixups. == \n");
      str.str = L"F_9_RemoveXMPMetadataifnotcompliantwithPDFA1";
      str.len = wcslen(L"F_9_RemoveXMPMetadataifnotcompliantwithPDFA1");
      AnalyzeAndFixup(preflight, e_FSOperateTypeSingleFixups, str, TRUE);
      GenerateReport(preflight, e_FSReportFileFormatTypePdf, str, output_directory);
    }
    // Release compliance engine.
    FSDK_ComplianceEngine_Release();
    FSDK_Library_Release();
    printf("== End: Preflight demo. ==\n");
  }

  return 0;
}