// 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 compare pdf page with the other.


const FSDK = require("@fuxinsoft/foxit-pdf-sdk-node");
const fs = require('fs');
const os = require('os');
const path = require('path');
const util = require('util');

var sn = "Rew1vB0TqoMIxX444UcpScARWdhgJpsJfzmyMLR32a0+EgrCjp2NaQ==";
var key = "8f3g1sFttW8NAgfSNv+BvaJWy4Pp8flcm7ncNDkdvCCO4JwUFbGLyZdujReMaiplbYJwwxam3lr7uHtQaEf0xTSu8j2ZBdrZ8FS4OQkEUjOI9UEGb3Ic0GqmlP9T9OnSItOYqoZZA/OnYg5EaVP+c/ha32nUquOk+5riEdhYgINAZDdgJkPc+YSWsrfixOIROf6t34j3eRcyFnOY0MS7tSXi1AEPE0VZdyu+5bfmBSGvLI+WR5lQ+oOQR5hRGwqf990WEdBelNO7dxhZELVhEZ+/L60PpDf5y9A8qmrY6PiEU8nhqIyLpks0BxeIsYyZ3Cr82jjfbItPGyEARsIVzluBSgVMD4oGjojz8T6wGDEXUbmZH/pra94XAs8wZ28mzbmIEppWnhBqX0c6Ov8iXCfFfUZr4KNuPxMEgk4VOYTMOd70GEjGnylivw18hbn7ZpkWh2Zql+uU2HejvOgn+WHvT0HF9NhK0Xb5vH8aFkgBP92PJx8obIbxBVeeExczxvsqWufuPdXS2sPNIgMYZ2TtvqVVsiUToeGB2FQo09fzZOSH5Vi18u/siyvC3FmjeMf4+OYWPuDPNCcrb21NgcHam7XI8AAoGLTGtLLWR+fxynbBBcg2d2ZPcZ5CMFIfOBHRWCkcA0sDT3cl9nsBppHNJze/jVxHy+lREeQEA/etSIgohTGAWW3ZlpibRXqMxlmVWEPifCubbWTzQ6U54mRMpW2UghH3gvCNFNgdday3Av7EtlIJOdUuiMq3zmmd5J4Jt4C82CKnVewA0DtZ4EEtwRG9IYuLrfpuZlnjBAtoeXRV1lqUZ6BcCe0E3m9jO28sGkALuIeJQ74XG7X8lMC7lMWWQYiYD7D6G9QT6X3qV4vXJwNKRKpYc/rI+amEawTSnDYAMEaYPwtZ0rb/7BL+atsZZaI+ncebdfVfjDJZbCZAc+JnkuNlUn2xrNRuObw8h/G0lm2pNOEJ10S8R4+yplo5B1QmC2dCDhaTaW67QJPqnJPKbVmjw7i6KGBi+CDP3JnAKlOX/YUu8uk56JfeQUuGcQg/KaBMGctnchH2n1i3+oGbibhurQj22+OO66Q6mijKoCRP/QJm5WTGcO4c0iVX0B8dIlai/uv/pGU8Wps9MxysH5ViVIj56LWKuNfy1FNO9+wx4iHYnUxfW0Iccq3YJYAlOBkMv8VZIJl9aTEEFJ626LJEw/UbEBdR9CdlwnylPf4RuNW/lcGYMgq3xNVpp/bzYKwZivvQOFgMdX1tv8D33SHvAm3zrRCU+v94BJXnBi9k25fnKsc705IMwv+I6bueXA+mDnagj1oTqsWXZ0WnPAYI3WUO7sBf7XUH230wfYXnTSVhujJxiEP/2oWqi2813gtQkKwz9N2p6yT8Vsh3+BaAwwDvtOZMJKUFcabTBSL6NoouzmzwItJ4mIboT6KXaef66rs8jCU=";

const input_path = "../input_files/";
const output_path = "../output_files/";


// get current date time
function GetLocalDateTime() {
  const date = new Date();
  let timezone_hour = date.getTimezoneOffset() / 60 * -1;
  let timezone_minute = Math.abs(date.getTimezoneOffset() % 60);
  let datetime = new FSDK.DateTime(date.getFullYear(), date.getMonth() + 1, date.getDate(), date.getHours(), date.getMinutes(), date.getSeconds(), date.getMilliseconds(), timezone_hour, timezone_minute);
  return datetime;
}

function RandomUID() {
  const characters = "0123456789qwertyuiopasdfghjklzxcvbnm";
  let uuid = "";

  for (let n = 0; n < 16; n++) {
    let uuidTemp = "";
    const b = Math.floor(Math.random() * 255);

    switch (n) {
      case 6:
        uuidTemp = '4' + (b % 15).toString(16);
        break;
      case 8:
        const randomCharIndex = Math.floor(Math.random() * characters.length);
        uuidTemp = characters[randomCharIndex] + (b % 15).toString(16);
        break;
      default:
        let hex = b.toString(16);
        if (hex.length < 2) {
          hex = '0' + hex;
        }
        uuidTemp = hex;
        break;
    }

    uuid += uuidTemp;
    if (n === 3 || n === 5 || n === 7 || n === 9) {
      uuid += '-';
    }
  }
  return uuid;
}

function CreateInsertStamp(page, rects, color, ws_contents, ws_type, ws_obj_type) {
  let rect_stamp = new FSDK.RectF();
  let rect_size = rects.GetSize();
  if (rect_size > 0) {
    let item = rects.GetAt(0);
    rect_stamp.left = item.left;
    rect_stamp.top = item.top-4 ;
    rect_stamp.right = rect_stamp.left + 4;
    rect_stamp.bottom = rect_stamp.top - 8;
  }

  let image = new FSDK.Image(input_path + "pdfcompare/insert_stamp.png");
  let stamp = new FSDK.Stamp(page.AddAnnot(FSDK.Annot.e_Stamp, rect_stamp));
  stamp.SetContent(ws_contents);
  stamp.SetBorderColor(color);
  stamp.SetSubject(ws_obj_type);
  stamp.SetTitle(ws_type);
  stamp.SetCreationDateTime(GetLocalDateTime());
  stamp.SetModifiedDateTime(GetLocalDateTime());
  stamp.SetUniqueID(RandomUID());
  stamp.SetImage(image, 0, 0);

  stamp.ResetAppearanceStream();
}

function CreateSquigglyRect(page, rects, color, ws_contents, ws_type, ws_obj_type) {
  let squiggly = new FSDK.Squiggly(page.AddAnnot(FSDK.Annot.e_Squiggly, new FSDK.RectF()));
  squiggly.SetContent(ws_contents);

  let quad_points_array = new FSDK.QuadPointsArray();
  let rect_size = rects.GetSize();
  for (let i=0; i<rect_size; i++) {
    let item = rects.GetAt(i);
    let quad_points = new FSDK.QuadPoints();
    quad_points.first = new FSDK.PointF(item.left, item.top);
    quad_points.second = new FSDK.PointF(item.right, item.top);
    quad_points.third = new FSDK.PointF(item.left, item.bottom);
    quad_points.fourth = new FSDK.PointF(item.right, item.bottom);
    quad_points_array.Add(quad_points);
  }
  squiggly.SetQuadPoints(quad_points_array);

  squiggly.SetBorderColor(color);
  squiggly.SetSubject(ws_obj_type);
  squiggly.SetTitle(ws_type);
  squiggly.SetCreationDateTime(GetLocalDateTime());
  squiggly.SetModifiedDateTime(GetLocalDateTime());
  squiggly.SetUniqueID(RandomUID());

  squiggly.ResetAppearanceStream();
}

function CreateDeleteTextStamp(page, rects, color, ws_contents, ws_type, ws_obj_type) {
  let rect_stamp = new FSDK.RectF();
  let rect_size = rects.GetSize();
  if (rect_size > 0) {
    let item = rects.GetAt(0);
    rect_stamp.left = item.left;
    rect_stamp.top = item.top + 12;
    rect_stamp.right = rect_stamp.left + 9;
    rect_stamp.bottom = rect_stamp.top - 12;
  }

  let image = new FSDK.Image(input_path + "pdfcompare/delete_stamp.png");

  let stamp = new FSDK.Stamp(page.AddAnnot(FSDK.Annot.e_Stamp, rect_stamp));
  stamp.SetContent(ws_contents);
  stamp.SetBorderColor(color);
  stamp.SetSubject(ws_obj_type);
  stamp.SetTitle(ws_type);
  stamp.SetCreationDateTime(GetLocalDateTime());
  stamp.SetModifiedDateTime(GetLocalDateTime());
  stamp.SetUniqueID(RandomUID());
  stamp.SetImage(image, 0, 0);

  stamp.ResetAppearanceStream();
}

function CreateDeleteText(page, rects, color, ws_contents, ws_type, ws_obj_type) {
  let strikeout = new FSDK.StrikeOut(page.AddAnnot(FSDK.Annot.e_StrikeOut, new FSDK.RectF()));
  strikeout.SetContent(ws_contents);

  let quad_points_array = new FSDK.QuadPointsArray();
  let rect_size = rects.GetSize();
  for (let i = 0; i<rect_size; i++) {
    let item = rects.GetAt(i);
    let quad_points = new FSDK.QuadPoints();
    quad_points.first = new FSDK.PointF(item.left, item.top);
    quad_points.second = new FSDK.PointF(item.right, item.top);
    quad_points.third = new FSDK.PointF(item.left, item.bottom);
    quad_points.fourth = new FSDK.PointF(item.right, item.bottom);
    quad_points_array.Add(quad_points);
  }
  strikeout.SetQuadPoints(quad_points_array);

  strikeout.SetBorderColor(color);
  strikeout.SetSubject(ws_obj_type);
  strikeout.SetTitle(ws_type);
  strikeout.SetCreationDateTime(GetLocalDateTime());
  strikeout.SetModifiedDateTime(GetLocalDateTime());
  strikeout.SetUniqueID(RandomUID());

  strikeout.ResetAppearanceStream();
}

let error_code = FSDK.Library.Initialize(sn, key);
if (FSDK.e_ErrSuccess != error_code) {
  if (FSDK.e_ErrInvalidLicense == error_code)
    console.log("[Failed] Current used Foxit PDF SDK key information is invalid.");
  else
    console.log("Library Initialize Error: %d", FSDK.e_ErrSuccess);
  return;
}

let output_directory = output_path + "pdfcompare/";
if (!fs.existsSync(output_directory))  {
  fs.mkdirSync(output_directory);
}




let input_base_file = input_path + "pdfcompare/test_base.pdf";
let input_compared_file = input_path + "pdfcompare/test_compared.pdf";

try {
  let base_doc = new FSDK.PDFDoc(input_base_file);
  let error_code = base_doc.Load("");
  if (error_code != FSDK.e_ErrSuccess) {
    console.log("The Doc [%s] Error: %d\n", input_base_file, error_code);
    return 1;
  }

  let compared_doc = new FSDK.PDFDoc(input_compared_file);
  error_code = compared_doc.Load("");
  if (error_code != FSDK.e_ErrSuccess) {
    console.log("The Doc [%s] Error: %d\n", input_compared_file, error_code);
    return 1;
  }

  let comparison = new FSDK.Comparison(base_doc, compared_doc);
  let result = comparison.DoCompare(0, 0, FSDK.Comparison.e_CompareTypeText);
  let old_info = result.base_doc_results;
  let new_info = result.compared_doc_results;
  let = result.compared_doc_results;
  let old_info_size = old_info.GetSize();
  let new_info_size = new_info.GetSize();
  let page_base = base_doc.GetPage(0);
  let page = compared_doc.GetPage(0);
  for (let i = 0; i < old_info_size; i++) {
    let item = old_info.GetAt(i);
    let type = item.type;
    if (type == FSDK.CompareResultInfo.e_CompareResultTypeDeleteText) {
      let res_string = `\"${item.diff_contents}\"`;
      CreateDeleteText(page_base, item.rect_array, 0xff0000, res_string, "Compare : Delete", "Text");
    } else if (type == FSDK.CompareResultInfo.e_CompareResultTypeInsertText) {
      let res_string = `\"${item.diff_contents}\"`;
      CreateInsertStamp(page_base, item.rect_array, 0x0000ff, res_string, "Compare : Insert", "Text");
    } else if (type == FSDK.CompareResultInfo.e_CompareResultTypeReplaceText) {
      let res_string = `[New]: \"${new_info.GetAt(i).diff_contents}\"\r\n[Old]: \"${item.diff_contents}\"`;
      CreateSquigglyRect(page_base, item.rect_array, 0xe7651a, res_string, "Compare : Replace",  "Text");
    }
  }
  for (let i=0; i<new_info_size; i++) {
    let item = new_info.GetAt(i);
    let type = item.type;
    if (type == FSDK.CompareResultInfo.e_CompareResultTypeDeleteText) {
      let res_string = `\"${item.diff_contents}\"`;      
      CreateDeleteTextStamp(page, item.rect_array, 0xff0000, res_string, "Compare : Delete", "Text");
    } else if (type == FSDK.CompareResultInfo.e_CompareResultTypeInsertText) {
      let res_string = `\"${item.diff_contents}\"`;
      CreateDeleteText(page, item.rect_array, 0x0000ff, res_string, "Compare : Insert", "Text");
    } else if (type == FSDK.CompareResultInfo.e_CompareResultTypeReplaceText) {      
      let res_string = `[New]: \"${new_info.GetAt(i).diff_contents}\"\r\n[Old]: \"${item.diff_contents}\"`;
      CreateSquigglyRect(page, item.rect_array, 0xe7651a, res_string, "Compare : Replace", "Text");
    }
  }
  base_doc.SaveAs(output_directory + "old.pdf", FSDK.PDFDoc.e_SaveFlagNormal);
  compared_doc.SaveAs(output_directory + "new.pdf", FSDK.PDFDoc.e_SaveFlagNormal);

  let new_doc = comparison.GenerateComparedDoc(FSDK.Comparison.e_CompareTypeAll);
  new_doc.SaveAs(output_directory + "generate_result.pdf", FSDK.PDFDoc.e_SaveFlagNormal);

  console.log("Pdf compare test.");
} catch (e) {
  console.log(e);
}
FSDK.Library.Release();