// 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.

#include "FSPDFObjC.h"

NSString *input_path = @"../input_files/";
NSString *output_path = @"../output_files/";

NSString *sn = @"bPxThFlBAYifO3yiW0C51bKbjPap1zHaf5aG5sX378TjFvB7Q0802A==";
NSString *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";

FSDateTime* getLocalDateTime() {
    time_t t = time(NULL);
    struct tm* rime = localtime(&t);
    tzset();
    int timezone_hour = (int)timezone / 3600 * -1;
    int timezone_minute = (abs((int)timezone) % 3600) / 60;

    FSDateTime* datetime = [[FSDateTime alloc] initWithYear:(rime->tm_year + 1900) month:(rime->tm_mon + 1) day:(rime->tm_mday) hour:(rime->tm_hour) minute:(rime->tm_min) second:(rime->tm_sec) milliseconds:0 utc_hour_offset:timezone_hour utc_minute_offset:timezone_minute];
    
    return datetime;
}

NSString* randomUID() {
    NSString* uuid = @"";
    const char* c = "0123456789qwertyuiopasdfghjklzxcvbnm";
    
    for (int n = 0; n < 16; n++) {
        NSString* uuid_temp = @"";
        int b = rand() % 255;
        switch (n) {
            case 6:
                uuid_temp = [NSString stringWithFormat:@"4%x", b % 15];
                break;
            case 8:
                uuid_temp = [NSString stringWithFormat:@"%c%x", c[rand() % strlen(c)], b % 15];
                break;
            default:
                uuid_temp = [NSString stringWithFormat:@"%02x", b];
                break;
        }
        uuid  = [uuid stringByAppendingString:uuid_temp];
        
        switch (n) {
            case 3:
            case 5:
            case 7:
            case 9:
                uuid = [uuid stringByAppendingString:@"-"];
                break;
        }
    }
    return uuid;
}

void createSquigglyRect(FSPDFPage* page, FSRectFArray* rects, int color, NSString *csContents, NSString *csType, NSString *csObjType) {
	if (rects == NULL) return;
	int rectSize = [rects getSize];
	if (rectSize == 0) return;		
    FSRectF* annot_rect = [[FSRectF alloc] init];
    FSSquiggly* squiggly = [[FSSquiggly alloc] initWithAnnot:[page addAnnot:FSAnnotSquiggly rect:annot_rect]];
    [squiggly setContent:csContents];

    FSQuadPointsArray* quad_points_array = [FSQuadPointsArray new];

    for (int i=0; i<rectSize; i++)
    {
        FSRectF* item = [rects getAt:i];
        FSQuadPoints* quad_points = [FSQuadPoints new];
        FSPointF* point = [FSPointF new];
        [point set:item.left y:item.top];
        quad_points.first = point;
		[point set:item.right y:item.top];
        quad_points.second = point;
		[point set:item.left y:item.bottom];
        quad_points.third = point;
		[point set:item.right y:item.bottom];
        quad_points.fourth = point;

		[quad_points_array add:quad_points];
    }
    [squiggly setQuadPoints:quad_points_array];

	[squiggly setBorderColor:color];
    [squiggly setSubject:csObjType];
    [squiggly setTitle:csType];
    FSDateTime* local_time = getLocalDateTime();
    [squiggly setCreationDateTime:local_time];
    [squiggly setModifiedDateTime:local_time];
    [squiggly setUniqueID:randomUID()];

    [squiggly resetAppearanceStream];
}
void CreateDeleteText(FSPDFPage* page, FSRectFArray* rects, int color, NSString *csContents, NSString *csType, NSString *csObjType) {
	if (rects == NULL) return;
	int rectSize = [rects getSize];
	if (rectSize == 0) return;	
    FSRectF* annot_rect = [[FSRectF alloc] init];
    FSStrikeOut* strikeout = [[FSStrikeOut alloc] initWithAnnot:[page addAnnot:FSAnnotStrikeOut rect:annot_rect]];
    [strikeout setContent:csContents];

    FSQuadPointsArray* quad_points_array = [FSQuadPointsArray new];

    for (int i=0; i<rectSize; i++)
    {
        FSRectF* item = [rects getAt:i];
        FSQuadPoints* quad_points = [FSQuadPoints new];
        FSPointF* point = [FSPointF new];
        [point set:item.left y:item.top];
        quad_points.first = point;
		[point set:item.right y:item.top];
        quad_points.second = point;
		[point set:item.left y:item.bottom];
        quad_points.third = point;
		[point set:item.right y:item.bottom];
        quad_points.fourth = point;

		[quad_points_array add:quad_points];
    }
    [strikeout setQuadPoints:quad_points_array];

	[strikeout setBorderColor:color];
    [strikeout setSubject:csObjType];
    [strikeout setTitle:csType];
    FSDateTime* local_time = getLocalDateTime();
    [strikeout setCreationDateTime:local_time];
    [strikeout setModifiedDateTime:local_time];
    [strikeout setUniqueID:randomUID()];

    [strikeout resetAppearanceStream];
}

void createDeleteTextStamp(FSPDFPage* page, FSRectFArray* rects, int color, NSString *csContents, NSString *csType, NSString *csObjType)
{
    FSRectF* rcStamp = [[FSRectF alloc] init];
    int rectSize = [rects getSize];
    if (rectSize > 0)
    {
        FSRectF* item = [rects getAt:0];
        rcStamp.left = item.left;
        rcStamp.top = item.top + 12;
        rcStamp.right = rcStamp.left + 9;
        rcStamp.bottom = rcStamp.top - 12;
    }

	FSImage* pImage = [[FSImage alloc] initWithPath:[input_path stringByAppendingString:@"pdfcompare/delete_stamp.png"]];
	FSStamp* stamp = [[FSStamp alloc] initWithAnnot:[page addAnnot:FSAnnotStamp rect:rcStamp]];

	[stamp setContent:csContents];
	[stamp setBorderColor:color];
	[stamp setSubject:csObjType];
    [stamp setTitle:csType];
    FSDateTime* local_time = getLocalDateTime();
    [stamp setCreationDateTime:local_time];
    [stamp setModifiedDateTime:local_time];
    [stamp setUniqueID:randomUID()];
	[stamp setImage:pImage frame_index:0 compress:0];

    [stamp resetAppearanceStream];
}

void createInsertTextStamp(FSPDFPage* page, FSRectFArray* rects, int color, NSString *csContents, NSString *csType, NSString *csObjType)
{
    FSRectF* rcStamp = [[FSRectF alloc] init];
    int rectSize = [rects getSize];
    if (rectSize > 0)
    {
        FSRectF* item = [rects getAt:0];
        rcStamp.left = item.left;
        rcStamp.top = item.top - 4;
        rcStamp.right = rcStamp.left + 4;
        rcStamp.bottom = rcStamp.top - 8;
    }

	FSImage* pImage = [[FSImage alloc] initWithPath:[input_path stringByAppendingString:@"pdfcompare/insert_stamp.png"]];
	FSStamp* stamp = [[FSStamp alloc] initWithAnnot:[page addAnnot:FSAnnotStamp rect:rcStamp]];

	[stamp setContent:csContents];
	[stamp setBorderColor:color];
	[stamp setSubject:csObjType];
    [stamp setTitle:csType];
    FSDateTime* local_time = getLocalDateTime();
    [stamp setCreationDateTime:local_time];
    [stamp setModifiedDateTime:local_time];
    [stamp setUniqueID:randomUID()];
	[stamp setImage:pImage frame_index:0 compress:0];

    [stamp resetAppearanceStream];
}

int main(int argc, const char *argv[]) {
    @autoreleasepool {
        NSString *output_directory = [output_path stringByAppendingString:@"pdfcompare/"];
        NSString *input_base_file = [input_path stringByAppendingString:@"pdfcompare/test_base.pdf"];
        NSString *input_compared_file = [input_path stringByAppendingString:@"pdfcompare/test_compared.pdf"];
        NSFileManager *fileManager = [NSFileManager defaultManager];
        BOOL isDir = NO;
        if ([fileManager fileExistsAtPath:output_directory isDirectory:&isDir]) {
            if (!isDir) {
                NSError *error = nil;
                if (![fileManager removeItemAtPath:output_directory error:&error] || error) {
                    return -1;
                }
                if (![fileManager createDirectoryAtPath:output_directory withIntermediateDirectories:YES attributes:nil error:&error] || error) {
                    return -1;
                }
            }
        } else {
            NSError *error = nil;
            if (![fileManager createDirectoryAtPath:output_directory withIntermediateDirectories:YES attributes:nil error:&error] || error) {
                return -1;
            }
        }

        // Initialize library
        FSErrorCode errorCode = [FSLibrary initialize:sn key:key];
        if (errorCode != FSErrSuccess) {
            NSLog(@"Library Initialize Error: %ld", errorCode);
            return -1;
        }

        @try {
            FSPDFDoc *base_doc = [[FSPDFDoc alloc] initWithPath:input_base_file];
            errorCode = [base_doc load:@""];
            if (errorCode != FSErrSuccess) {
                NSLog(@"The Doc [%@] Error: %ld\n", input_base_file, errorCode);
                return -1;
            }

			FSPDFDoc *compared_doc = [[FSPDFDoc alloc] initWithPath:input_compared_file];
            errorCode = [compared_doc load:@""];
            if (errorCode != FSErrSuccess) {
                NSLog(@"The Doc [%@] Error: %ld\n", input_compared_file, errorCode);
                return -1;
            }

			FSComparison* comparison = [[FSComparison alloc] initWithBase_doc:base_doc compared_doc:compared_doc];
            FSCompareResults* result = [comparison doCompare:0 compared_page_index:0 compare_flags:FSComparisonCompareTypeText];
            int oldInfoSize = [result.base_doc_results getSize];
            int newInfoSize = [result.compared_doc_results getSize];
            FSPDFPage* page = [compared_doc getPage:0];
            FSPDFPage* page1 = [base_doc getPage:0];
            for (int i=0; i<oldInfoSize; i++)
            {
                FSCompareResultInfo* item = [result.compared_doc_results getAt:i];
                FSCompareResultInfoCompareResultType type = item.type;
                if (type == FSCompareResultInfoCompareResultTypeDeleteText)
                {
                    NSString* res_string = [NSString stringWithFormat:@"\"%@\"", item.diff_contents];
                    CreateDeleteText(page1, item.rect_array, 0xff0000, res_string, @"Compare : Delete", @"Text");
                }
                else if (type == FSCompareResultInfoCompareResultTypeInsertText)
                {
                    NSString* res_string = [NSString stringWithFormat:@"\"%@\"", item.diff_contents];
                    createInsertTextStamp(page1, item.rect_array, 0x0000ff, res_string, @"Compare : Insert", @"Text");
                }
                else if (type == FSCompareResultInfoCompareResultTypeReplaceText)
                {
                    NSString* res_string = [NSString stringWithFormat:@"[Old]: \"%@\"\r\n[New]: \"%@\"",[result.base_doc_results getAt:i].diff_contents,item.diff_contents];
                    createSquigglyRect(page1, item.rect_array, 0xe7651a, res_string, @"Compare : Replace", @"Text");
                }
            }
            for (int i=0; i<newInfoSize; i++)
            {
                FSCompareResultInfo* item = [result.compared_doc_results getAt:i];
                FSCompareResultInfoCompareResultType type = item.type;
                if (type == FSCompareResultInfoCompareResultTypeDeleteText)
                {
                    NSString* res_string = [NSString stringWithFormat:@"\"%@\"", item.diff_contents];
                    createDeleteTextStamp(page, item.rect_array, 0xff0000, res_string, @"Compare : Delete", @"Text");
                }
                else if (type == FSCompareResultInfoCompareResultTypeInsertText)
                {
                    NSString* res_string = [NSString stringWithFormat:@"\"%@\"", item.diff_contents];
                    CreateDeleteText(page, item.rect_array, 0x0000ff, res_string, @"Compare : Insert", @"Text");
                }
                else if (type == FSCompareResultInfoCompareResultTypeReplaceText)
                {
                    NSString* res_string = [NSString stringWithFormat:@"[Old]: \"%@\"\r\n[New]: \"%@\"",[result.base_doc_results getAt:i].diff_contents,item.diff_contents];
                    createSquigglyRect(page, item.rect_array, 0xe7651a, res_string, @"Compare : Replace", @"Text");
                }
            }
           
            [compared_doc saveAs:[output_directory stringByAppendingString:@"new.pdf"] save_flags:FSPDFDocSaveFlagNormal];
            [base_doc saveAs:[output_directory stringByAppendingString:@"old.pdf"] save_flags:FSPDFDocSaveFlagNormal];

			FSPDFDoc* new_doc = [comparison generateComparedDoc:FSComparisonCompareTypeAll];
			[new_doc saveAs:[output_directory stringByAppendingString:@"generate_result.pdf"] save_flags:FSPDFDocSaveFlagNormal];
            NSLog(@"Pdf compare test.");
        }
        @catch (NSException *e) {
            NSLog(@"Unknown Exception, %@", e);
        }
    }
    [FSLibrary destroy];
    return 0;
}
