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

using System;

using foxit;
using foxit.common;
using foxit.common.fxcrt;
using foxit.pdf;
using foxit.pdf.annots;
using foxit.addon;
using foxit.addon.comparison;

namespace pdfcompareCS
{
    class pdfcompare
    {
        static string input_path = "../input_files/pdfcompare/";
        static string output_path = "../output_files/pdfcompare/";

        public static foxit.common.DateTime GetLocalDateTime()
        {
            System.DateTimeOffset rime = System.DateTimeOffset.Now;
            foxit.common.DateTime datetime = new foxit.common.DateTime();
            datetime.year = (UInt16)rime.Year;
            datetime.month = (UInt16)rime.Month;
            datetime.day = (ushort)rime.Day;
            datetime.hour = (UInt16)rime.Hour;
            datetime.minute = (UInt16)rime.Minute;
            datetime.second = (UInt16)rime.Second;

            datetime.utc_hour_offset = (short)rime.Offset.Hours;
            datetime.utc_minute_offset = (ushort)rime.Offset.Minutes;

            return datetime;
        }

        static String RandomUID()
        {
            return System.Guid.NewGuid().ToString();
        }

        static void CreateDeleteText(PDFPage page, RectFArray rects, int color, String csContents, String csType, String csObjType)
        {
			int rectSize = rects.GetSize();
			if (rectSize == 0) return;
            using (Annot annot = page.AddAnnot(Annot.Type.e_StrikeOut, new RectF()))
            {
                StrikeOut strikeout = new StrikeOut(annot);
                strikeout.SetContent(csContents);

                QuadPointsArray quad_points_array = new QuadPointsArray();
                
                for (int i = 0; i < rectSize; i++)
                {
                    RectF item = rects.GetAt(i);
                    QuadPoints quad_points = new QuadPoints();
                    quad_points.first = new foxit.common.fxcrt.PointF(item.left, item.top);
                    quad_points.second = new foxit.common.fxcrt.PointF(item.right, item.top);
                    quad_points.third = new foxit.common.fxcrt.PointF(item.left, item.bottom);
                    quad_points.fourth = new foxit.common.fxcrt.PointF(item.right, item.bottom);
                    quad_points_array.Add(quad_points);
                }
                strikeout.SetQuadPoints(quad_points_array);

                strikeout.SetBorderColor(color);
                strikeout.SetSubject(csObjType);
                strikeout.SetTitle(csType);
                strikeout.SetCreationDateTime(GetLocalDateTime());
                strikeout.SetModifiedDateTime(GetLocalDateTime());
                strikeout.SetUniqueID(RandomUID());

                strikeout.ResetAppearanceStream();
                strikeout.Dispose();
            }
        }

        static void CreateSquigglyRect(PDFPage page, RectFArray rects, int color, String csContents, String csType, String csObjType)
        {
			int rectSize = rects.GetSize();
			if (rectSize == 0) return;
            using (Annot annot = page.AddAnnot(Annot.Type.e_Squiggly, new RectF()))
            {
                Squiggly squiggly = new Squiggly(annot);
                squiggly.SetContent(csContents);

                QuadPointsArray quad_points_array = new QuadPointsArray();
                for (int i = 0; i < rectSize; i++)
                {
                    RectF item = rects.GetAt(i);
                    QuadPoints quad_points = new QuadPoints();
                    quad_points.first = new foxit.common.fxcrt.PointF(item.left, item.top);
                    quad_points.second = new foxit.common.fxcrt.PointF(item.right, item.top);
                    quad_points.third = new foxit.common.fxcrt.PointF(item.left, item.bottom);
                    quad_points.fourth = new foxit.common.fxcrt.PointF(item.right, item.bottom);
                    quad_points_array.Add(quad_points);
                }
                squiggly.SetQuadPoints(quad_points_array);

                squiggly.SetBorderColor(color);
                squiggly.SetSubject(csObjType);
                squiggly.SetTitle(csType);
                squiggly.SetCreationDateTime(GetLocalDateTime());
                squiggly.SetModifiedDateTime(GetLocalDateTime());
                squiggly.SetUniqueID(RandomUID());

                squiggly.ResetAppearanceStream();
                squiggly.Dispose();
            }
        }

        static void CreateInsertStamp(PDFPage page, RectFArray rects, int color, String csContents, String csType, String csObjType)
        {
            RectF rcStamp = new RectF();
            int rectSize = rects.GetSize();
            if (rectSize > 0)
            {
                RectF item = rects.GetAt(0);
                rcStamp.left = item.left;
                rcStamp.top = item.top -4;
                rcStamp.right = rcStamp.left + 4;
                rcStamp.bottom = rcStamp.top - 8;
            }

            Image pImage = new Image(input_path + "insert_stamp.png");

            using (Annot annot = page.AddAnnot(Annot.Type.e_Stamp, rcStamp))
            {
                Stamp stamp = new Stamp(annot);
                stamp.SetContent(csContents);
                stamp.SetBorderColor(color);
                stamp.SetSubject(csObjType);
                stamp.SetTitle(csType);
                stamp.SetCreationDateTime(GetLocalDateTime());
                stamp.SetModifiedDateTime(GetLocalDateTime());
                stamp.SetUniqueID(RandomUID());
                stamp.SetImage(pImage, 0, 0);

                stamp.ResetAppearanceStream();
                stamp.Dispose();
            }
        }
        static void CreateDeleteTextStamp(PDFPage page, RectFArray rects, int color, String csContents, String csType, String csObjType)
        {
            RectF rcStamp = new RectF();
            int rectSize = rects.GetSize();
            if (rectSize > 0)
            {
                RectF item = rects.GetAt(0);
                rcStamp.left = item.left;
                rcStamp.top = item.top + 12;
                rcStamp.right = rcStamp.left + 9;
                rcStamp.bottom = rcStamp.top - 12;
            }

            Image pImage = new Image(input_path + "delete_stamp.png");

            using (Annot annot = page.AddAnnot(Annot.Type.e_Stamp, rcStamp))
            {
                Stamp stamp = new Stamp(annot);
                stamp.SetContent(csContents);
                stamp.SetBorderColor(color);
                stamp.SetSubject(csObjType);
                stamp.SetTitle(csType);
                stamp.SetCreationDateTime(GetLocalDateTime());
                stamp.SetModifiedDateTime(GetLocalDateTime());
                stamp.SetUniqueID(RandomUID());
                stamp.SetImage(pImage, 0, 0);

                stamp.ResetAppearanceStream();
                stamp.Dispose();
            }
        }

        static void Main(string[] args)
        {
            
            System.IO.Directory.CreateDirectory(output_path);

            string sn = "Rew1vB0TqoMIxX444UcpScARWdhgJpsJfzmyMLR32a0+EgrCjp2NaQ==";
            string 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=";
            // Initialize library
            ErrorCode error_code = Library.Initialize(sn, key);
            if (error_code != ErrorCode.e_ErrSuccess)
            {
                Console.WriteLine("Library Initialize Error: {0}\n", error_code);
                return;
            }

            try
            {
                string input_base_file = input_path + "test_base.pdf";
                string input_compared_file = input_path + "test_compared.pdf";
                using (PDFDoc base_doc = new PDFDoc(input_base_file))
                {
                    error_code = base_doc.Load(null);
                    if (error_code != ErrorCode.e_ErrSuccess)
                    {
                        Console.WriteLine("The PDFDoc [{0}] Error: {1}\n", input_base_file, error_code);
                        Library.Release();
                        return;
                    }

                    using (PDFDoc compared_doc = new PDFDoc(input_compared_file))
                    {
                        error_code = compared_doc.Load(null);
                        if (error_code != ErrorCode.e_ErrSuccess)
                        {
                            Console.WriteLine("The PDFDoc [{0}] Error: {1}\n", input_compared_file, error_code);
                            Library.Release();
                            return;
                        }

                        using (Comparison comparison = new Comparison(base_doc, compared_doc))
                        {
                            CompareResults result = comparison.DoCompare(0, 0, (int)Comparison.CompareType.e_CompareTypeText);
                            CompareResultInfoArray oldInfo = result.base_doc_results;
                            CompareResultInfoArray newInfo = result.compared_doc_results;
                            uint oldInfoSize = oldInfo.GetSize();
                            uint newInfoSize = newInfo.GetSize();

                            using (PDFPage page1 = base_doc.GetPage(0))
                            {
                                for (uint i = 0; i < oldInfoSize; i++)
                                {
                                    CompareResultInfo item = oldInfo.GetAt(i);
                                    CompareResultInfo.CompareResultType type = item.type;
                                    if (type == CompareResultInfo.CompareResultType.e_CompareResultTypeDeleteText)
                                    {
                                        String res_string = String.Format("\"{0}\"", item.diff_contents);
                                        CreateDeleteText(page1, item.rect_array, 0xff0000, res_string, "Compare : Delete", "Text");
                                    }
                                    else if (type == CompareResultInfo.CompareResultType.e_CompareResultTypeInsertText)
                                    {
                                        String res_string = String.Format("\"{0}\"", item.diff_contents);
                                        CreateInsertStamp(page1, item.rect_array, 0x0000ff, res_string, "Compare : Insert", "Text");
                                    }
                                    else if (type == CompareResultInfo.CompareResultType.e_CompareResultTypeReplaceText)
                                    {
                                        String res_string = String.Format("[New]: \"{0}\"\r\n[Old]: \"{1}\"", newInfo.GetAt(i).diff_contents, item.diff_contents);
                                        CreateSquigglyRect(page1, item.rect_array, 0xe7651a, res_string, "Compare : Replace", "Text");
                                    }
                                }
                            }
                            using (PDFPage page = compared_doc.GetPage(0))
                            {
                                for (uint i = 0; i < newInfoSize; i++)
                                {
                                    CompareResultInfo item = newInfo.GetAt(i);
                                    CompareResultInfo.CompareResultType type = item.type;
                                    if (type == CompareResultInfo.CompareResultType.e_CompareResultTypeDeleteText)
                                    {
                                        String res_string = String.Format("\"{0}\"", item.diff_contents);
                                        CreateDeleteTextStamp(page, item.rect_array, 0xff0000, res_string, "Compare : Delete", "Text");
                                    }
                                    else if (type == CompareResultInfo.CompareResultType.e_CompareResultTypeInsertText)
                                    {
                                        String res_string = String.Format("\"{0}\"", item.diff_contents);
                                        CreateDeleteText(page, item.rect_array, 0x0000ff, res_string, "Compare : Insert", "Text");
                                    }
                                    else if (type == CompareResultInfo.CompareResultType.e_CompareResultTypeReplaceText)
                                    {
                                        String res_string = String.Format("[Old]: \"{0}\"\r\n[New]: \"{1}\"", oldInfo.GetAt(i).diff_contents, item.diff_contents);
                                        CreateSquigglyRect(page, item.rect_array, 0xe7651a, res_string, "Compare : Replace", "Text");
                                    }
                                }
                            }

                            compared_doc.SaveAs(output_path + "new.pdf", (int)PDFDoc.SaveFlags.e_SaveFlagNormal);
                            base_doc.SaveAs(output_path + "old.pdf", (int)PDFDoc.SaveFlags.e_SaveFlagNormal);

                            using (PDFDoc new_doc = comparison.GenerateComparedDoc((int)Comparison.CompareType.e_CompareTypeAll))
                            {
                                new_doc.SaveAs(output_path + "generate_result.pdf", (int)PDFDoc.SaveFlags.e_SaveFlagNormal);
                            }
                            Console.WriteLine("Pdf compare test.");
                        }
                        
                    }
                }
            }
            catch (foxit.PDFException e)
            {
                Console.WriteLine(e.Message);
            }

            Library.Release();
        }
    }
}
