// 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 create or open a portfolio PDF file.

#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";

BOOL CreatPortfolioPDF(NSString* saved_portfolio_pdf_path) {
  // Create a new blank portfolio PDF.
  FSPortfolio* new_portfolio = [FSPortfolio createPortfolio];
  if (YES == [new_portfolio isEmpty]) {
    NSLog(@"[FAILED] Fail to create a new portfolio PDF.\n");
    return NO;
  }

  // Get the root node.
  FSPortfolioNode* root_node = [new_portfolio getRootNode];
  if (YES == [root_node isEmpty]) {
    NSLog(@"[FAILED] Fail to get the root node.\n");
    return NO;
  }
  // The root node should be a folder node, so transfer to use FSPortfolioFolderNode.
  FSPortfolioFolderNode* root_folder = [[FSPortfolioFolderNode alloc] initWithOther:root_node];

  // Pre-load the PDF file which is to be added to new sub folder in portfolio PDF.
  // ATTENTION: please keep the PDF document object valid until portfolio PDF is saved or ends its life-cycle.
  NSString* input_pdf_filename = @"AboutFoxit.pdf";
  NSString* input_pdf_file_path = [input_path stringByAppendingPathComponent:input_pdf_filename];
  FSPDFDoc* pdf_doc = [[FSPDFDoc alloc] initWithPath:input_pdf_file_path];
  FSErrorCode error_code = [pdf_doc load:nil];
  if (error_code != FSErrSuccess) {
    NSLog(@"[FAILED] Fail to load PDF file %@. Error: %ld", input_pdf_file_path, error_code);
  }

  // Add a sub folder to root folder node.
  FSPortfolioFolderNode* new_sub_foldernode = [root_folder addSubFolder:@"Sub Folder-1"];
  if (YES == [new_sub_foldernode isEmpty]) {
    NSLog(@"[FAILED] Fail to add sub folder.\n");
  } else {
    [new_sub_foldernode setDescription:@"This is a sub folder added to portfolio PDF file."];

    if (NO == [pdf_doc isEmpty]) {
      // Add a valid PDF document object to current folder node.
      // ATTENTION: please keep the PDF document object valid until portfolio PDF is saved or ends its life-cycle.
      FSPortfolioFileNode* new_filenode = [new_sub_foldernode addPDFDoc:pdf_doc file_name:input_pdf_filename];
      if (YES == [new_filenode isEmpty]) {
        NSLog(@"[FAILED] Fail to add PDF file %@.\n", input_pdf_file_path);
      } else {
        FSFileSpec* file_spec = [new_filenode getFileSpec];
        [file_spec setDescription:@"This is a common PDF file added to portfolio PDF file"];
      }
    }
  }

  // Add a non-PDF file to root folder node.
  NSString* input_file_path = [input_path stringByAppendingPathComponent:@"FoxitLogo.jpg"];
  FSPortfolioFileNode* new_sub_filenode = [root_folder addFile:input_file_path];
  if (YES == [new_sub_filenode isEmpty]) {
    NSLog(@"[FAILED] Fail to add file %@.\n", input_file_path);
  } else {
    FSFileSpec* file_spec = [new_sub_filenode getFileSpec];
    [file_spec setDescription:@"This is a non-PDF file added to portfolio PDF file."];
  }
  
  // User can update schema field and other properties by class Portfolio, if necessary.
  
  // Save the new portfolio PDF file.
  FSPDFDoc*portfolio_pdf_doc = [new_portfolio getPortfolioPDFDoc];
  if (YES == [portfolio_pdf_doc isEmpty]) {
    NSLog(@"[FAILED] Fail to get portfolio PDF document object.\n");
    return NO;
  } else {
    return [portfolio_pdf_doc saveAs:saved_portfolio_pdf_path save_flags:FSPDFDocSaveFlagNormal];
  }
}

void OutputTab(NSMutableString* output_txt_info, int tab_count) {
  for (int i = 0; i<tab_count; i++)
    [output_txt_info appendString:@"\t"];
}

void OutputFileNodeInfo(NSMutableString* output_txt_info, FSPortfolioFileNode* node, int tab_count) {
  if (tab_count>0)
    OutputTab(output_txt_info, tab_count);
  [output_txt_info appendString:@"Type:File\n"];

  NSString* key_name = [node getKeyName];
  if (tab_count>0)
    OutputTab(output_txt_info, tab_count);
  [output_txt_info appendString:[NSString stringWithFormat:@"Key Name:%@\n", key_name]];

  FSFileSpec* file_spec = [node getFileSpec];
  NSString* file_name = [file_spec getFileName];
  if (tab_count>0)
    OutputTab(output_txt_info, tab_count);
  [output_txt_info appendString:[NSString stringWithFormat:@"File Name:%@\n", file_name]];

  NSString* description = [file_spec getDescription];
  if (tab_count>0)
    OutputTab(output_txt_info, tab_count);
  [output_txt_info appendString:[NSString stringWithFormat:@"Description:%@\n", description]];
}

void OutputSubNodesInfo(NSMutableString* output_txt_info, FSPortfolioNodeArray* sub_nodes, int tab_count);

void OutputFolderNodeInfo(NSMutableString* output_txt_info, FSPortfolioFolderNode* node, int tab_count) {
  if (tab_count>0)
    OutputTab(output_txt_info, tab_count);
  [output_txt_info appendString:@"Type:Folder\n"];

  NSString* name = [node getName];
  if (tab_count>0)
    OutputTab(output_txt_info, tab_count);
  [output_txt_info appendString:[NSString stringWithFormat:@"Name:%@\n", name]];

  NSString* description = [node getDescription];
  if (tab_count>0)
    OutputTab(output_txt_info, tab_count);
  [output_txt_info appendString:[NSString stringWithFormat:@"Description:%@\n", description]];

  FSPortfolioNodeArray* sub_nodes = [node getSortedSubNodes];
  OutputSubNodesInfo(output_txt_info, sub_nodes, tab_count + 1);
}

void OutputNodeInfo(NSMutableString* output_txt_info, FSPortfolioNode* node, int tab_count) {
  switch ([node getNodeType]) {
    case FSPortfolioNodeTypeFolder:
      OutputFolderNodeInfo(output_txt_info, [[FSPortfolioFolderNode alloc] initWithOther:node], tab_count);
      break;
    case FSPortfolioNodeTypeFile:
      OutputFileNodeInfo(output_txt_info, [[FSPortfolioFileNode alloc] initWithOther:node], tab_count);
      break;
    default:
      break;
  }
}

void OutputSubNodesInfo(NSMutableString* output_txt_info, FSPortfolioNodeArray* sub_nodes, int tab_count) {
  for (unsigned long index = 0; index < [sub_nodes getSize]; index++) {
    if (tab_count > 0)
      OutputTab(output_txt_info, tab_count);
    [output_txt_info appendString:[NSString stringWithFormat:@"Sorted Index (under current folder):%ld\n", index]];
    OutputNodeInfo(output_txt_info, [sub_nodes getAt:index], tab_count);
    [output_txt_info appendString:@"========\n"];
  }
}

void OutputSchemaFields(NSMutableString* output_txt_info, FSSchemaFieldArray* field_array) {
  if ([field_array getSize] <= 0) return;
  [output_txt_info appendString:@"==== Schema Fields ====\n"];

  for (unsigned long i = 0; i < [field_array getSize]; i++) {
    [output_txt_info appendString:[NSString stringWithFormat:@"Field index:%ld\n", i]];
    FSSchemaField* field = [field_array getAt:i];
    if (YES == [field isEmpty]) continue;
    NSString* key_name = [field getKeyName];
    [output_txt_info appendString:[NSString stringWithFormat:@"Key name: %@\n", key_name]];

    NSString* subtype_name = [field getSubtypeName];
    [output_txt_info appendString:[NSString stringWithFormat:@"Subtype name: %@\n", subtype_name]];

    NSString* display_name = [field getDisplayName];
    [output_txt_info appendString:[NSString stringWithFormat:@"Display name: %@\n", display_name]];

    BOOL is_visible = [field isVisible];
    [output_txt_info appendString:[NSString stringWithFormat:@"Visibility: %@\n", is_visible ? @"YES" : @"NO"]];

    [output_txt_info appendString:@"========\n"];
  }
}

void OutputPortfolioProperties(NSMutableString* output_txt_info, FSPortfolio* portfolio) {
  [output_txt_info appendString:@"==== Portfolio Properties ====\n"];
  NSString* initial_filespec_keyname = [portfolio getInitialFileSpecKeyName];
  [output_txt_info appendString:[NSString stringWithFormat:@"Initial FSFileSpec* Key Name:%@\n", initial_filespec_keyname]];

  FSPortfolioInitialViewMode view_mode = [portfolio getInitialViewMode];
  NSString* view_mode_str;
  switch (view_mode) {
    case FSPortfolioInitialViewUnknownMode:
      view_mode_str = @"Unknown";
      break;
    case FSPortfolioInitialViewDetailMode:
      view_mode_str = @"Detail";
      break;
    case FSPortfolioInitialViewTileMode:
      view_mode_str = @"Tile";
      break;
    case FSPortfolioInitialViewHidden:
      view_mode_str = @"Hidden";
      break;
  }
  [output_txt_info appendString:[NSString stringWithFormat:@"Initial View Mode:%@\n", view_mode_str]];


  BOOL is_ascending = [portfolio isSortedInAscending];
  [output_txt_info appendString:[NSString stringWithFormat:@"Sorting Order:%@\n", is_ascending? @"Ascending" : @"Descending" ]];

  NSString* sorting_field_name = [portfolio getSortingFieldKeyName];
  [output_txt_info appendString:[NSString stringWithFormat:@"Sorting Field Key Name:%@\n", sorting_field_name]];

  FSSchemaFieldArray* field_array = [portfolio getSchemaFields];
  OutputSchemaFields(output_txt_info, field_array);
}

void OutputPortfolioPDFInfo(NSString* portfolio_file_path, NSString* output_txt_path) {
  FSPDFDoc* pdf_doc = [[FSPDFDoc alloc] initWithPath:portfolio_file_path];
  FSErrorCode error_code = [pdf_doc load:nil];
  if (error_code != FSErrSuccess) {
    NSLog(@"[FAILED] Fail to load Portfolio PDF file %@. Error: %ld\n", portfolio_file_path, error_code);
    return;
  }

  if (NO == [pdf_doc isPortfolio]) {
    NSLog(@"[FAILED] Fail to output portfolio information for PDF file %@, because it is not a portfolio PDF file.\n", portfolio_file_path);
    return;
  }

  FSPortfolio* exist_portfolio = [FSPortfolio createPortfolioWithPDFDoc:pdf_doc];
  if (YES == [exist_portfolio isEmpty]) {
    NSLog(@"[FAILED] Fail to create a portfolio object based an existed portfolio PDF document.\n");
    return;
  }

  NSMutableString* output_txt_info = [NSMutableString new];
  // Output portfolio properties.
  OutputPortfolioProperties(output_txt_info, exist_portfolio);
  [output_txt_info appendString:@"======================================================================\n"];

  // Output all nodes.
  [output_txt_info appendString:@"==== Nodes Information ====\n"];
  FSPortfolioNode* root_node = [exist_portfolio getRootNode];
  FSPortfolioFolderNode* root_folder = [[FSPortfolioFolderNode alloc] initWithOther:root_node];
  FSPortfolioNodeArray* sub_nodes = [root_folder getSortedSubNodes];
  OutputSubNodesInfo(output_txt_info, sub_nodes, 0);

  [[output_txt_info dataUsingEncoding:NSUTF8StringEncoding] writeToFile:output_txt_path atomically:YES];
}

int main(int argc, const char * argv[]) {
	
	@autoreleasepool {
		NSString *output_directory = [output_path stringByAppendingString:@"portfolio/"];
		
		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 {
			NSString* saved_portfolio_pdf_path = [output_directory stringByAppendingPathComponent:@"new_portfolio.pdf"];
			// Create a new portfolio PDF file
			if (CreatPortfolioPDF(saved_portfolio_pdf_path)) {
				NSString* output_info_file_path = [output_directory stringByAppendingPathComponent:@"new_portfolio_info.txt"];
				// Show information of the new portfolio PDF file.
				OutputPortfolioPDFInfo(saved_portfolio_pdf_path, output_info_file_path);
			}

			NSLog(@"END: Portfolio demo.");
		}
		@catch (NSException *e) {
			NSLog(@"Unknown Exception, %@", e);
			return -1;
		}
	}
	[FSLibrary destroy];
	return 0;
}
