/*
//
//   ADOBE SYSTEMS INCORPORATED
//   Copyright (C) 2000-2003 Adobe Systems Incorporated
//   All rights reserved.
//
//   NOTICE: Adobe permits you to use, modify, and distribute this file
//   in accordance with the terms of the Adobe license agreement
//   accompanying it. If you have received this file from a source other
//   than Adobe, then your use, modification, or distribution of it
//   requires the prior written permission of Adobe.
//
*/
// MTSerialNums.cpp : Defines the entry point for the console application.
// This application demonstrates how to use the multi-threaded version of
// PDFL. Each thread opens a base document and adds a serial number to it
// to create a serialized version of the base document.
//

#include "MyPDFLibUtils.h"
#include "SDKThreads.h"
#include "Watermark.h"
#include "stdlib.h"
#include "stdio.h"
#include "string.h"

typedef   struct  ThreadArgs  {
                int   id;
                const   char  *filename;
                const   char  *outdir;
                const   char  *basename;
               }  ThreadArgs;

// A CSMutex is the local (within this process only) semaphore for
// protecting a critical section of code that modifies an important
// data object shared among threads (or with exception handlers)
CSMutex   serialNumberCS;

// serialNumber is the counter shared among the threads.
// maxSerialNumber is the limit of work to be performed.
static   volatile   int   serialNumber;
static   int   maxSerialNumber;

// getSerialNumber is the "get work" function. Here we just
// return an int, but the key here is the technique. Several threads
// could be wanting work at the same time.
int   getSerialNumber()
{
                int  retval = -1;

                // Here's the key crtical section. Only 1 thread at a time in here...
                EnterCS(  serialNumberCS  );

                // Is there any more work to do? There are really 3 answers to
                // this question, but only 2 are represented here. Here we test
                // for the "no, never" case. If there will never be more work,
                // return a -1, which is a signal to the caller that we're done.
                // If there is more work, we return the serial number to use.
                // The third case is "no, not now, but maybe later". For that case,
                // we should either wait for a signal or an appropriate amount of
                // time to check again, being careful not to hog the critical
                // section. But this is a simple sample...
                if  (serialNumber  != -1) {
                                // Get the next bit of work to do.
                               retval =  serialNumber++;

                                // For this example, we run out of work when we run out of serial
                                // numbers. So test for that and set the flag for all the other
                                // callers of this function.
                                if  (retval >  maxSerialNumber)
                                               retval =  serialNumber  = -1;
                               }

                // Done modifying serialNumber, leave the critical section.
                LeaveCS(  serialNumberCS  );

                // and return the serial number to generate.
                return  retval;
               }

// This is the thread worker function.
ThreadFuncReturnType   threadFunc(  ThreadArgs  *pArgs )
{
                INIT_AUTO_POOL(autoReleasePool);                /* Required only on MAC platform */
               
                int   serialNumber, err;
                char  *ofn;
                char  watermark[100];

                ASSize_t  buffLen = strlen( pArgs->outdir  ) + strlen( pArgs->basename  ) + 20;
               ofn = ( char  *)malloc( sizeof ( char ) * buffLen);
                if  (ofn ==  NULL)  return  0;

                // A loop that does work until there's no more work to do.
                while  ((serialNumber =  getSerialNumber()) != -1) {
                                // Do the work itself. Here we just print the serial number...
                               printf(  "Thread %d got serial number %d\n" , pArgs->id, serialNumber );

                               err =  MyPDFLInit();          // Initialize the PDFLibrary
                                if  (err != 0) {
                                               fprintf(stderr,  "Initialization error. See \"AcroErr.h\" for more info.\n" );
                                               fprintf(stderr,  "Error system: %d\nError Severity: %d\nError Code: %d\n" ,
                                                                                                                ErrGetSystem(err),  ErrGetSeverity(err),  ErrGetCode(err));
                                               free( ofn );
                                                return  0;
                               }

                                sprintf_safe( ofn, buffLen,  "%s/%s%06d.pdf" , pArgs->outdir, pArgs->basename, serialNumber );
                                sprintf_safe( watermark,  sizeof (watermark),  "Copy Serial Number %06d" , serialNumber );

                                Serialize( pArgs->filename, ofn, watermark );

                                MyPDFLTerm();
               }

               free( ofn );

                RELEASE_AUTO_POOL(autoReleasePool);  /* Required only on MAC platform */
               
                return  0;
}

int   main( int  argc,  char * argv[])
{
                INIT_AUTO_POOL(autoReleasePool);                /* Required only on MAC platform */
               
                int  numThreads = 3;          // Default number of threads
                int  i;
               FILE *fp;
                ThreadInfo  *threads;
                ThreadArgs  *threadArgs;

               {  // Deal with command line argument processing...
                                // Print out some usage info if we don't get it right...
                                // Could have pre-programmed some defaults instead, of course.
                                if  (6 > argc || argc > 7) {
                                               printf(  "Usage: %s InputDocument OutputDirectory OutputFileBaseName StartingSerialNumber NumberOfCopies [NumberOfThreads]\n" , argv[0] );
                                               printf(  "Use absolute paths for InputDocument and OutputDirectory.\n" );
                                                return  0;
                                               }

                                // Validate the input file name argument
                                if  ((fp = fopen(argv[1],  "r" )) ==  NULL) {
                                               printf(  "Cannot open input document \"%s\"\n" , argv[1] );
                                                return  0;
                                               }
                                else
                                               fclose( fp );

                               serialNumber = atoi( argv[4] );                  // Starting serial number
                                maxSerialNumber  = atoi( argv[5] ) + serialNumber -1;        // Maximum serial number
                                if  (maxSerialNumber  < serialNumber || serialNumber < 0) {
                                               printf(  "Serial numbers have to be positive and max must be greater than starting.\n"  );
                                                return  0;
                                               }

                                // Get the number of threads
                                if  (argc == 7)
                                               numThreads = atoi( argv[6] );      // Number of threads to use
                                if  (numThreads <= 0) {
                                               printf(  "NumberOfThreads must be 1 or more\n"  );
                                                return  0;
                                               }

                                // Validate the output directory argument
                                // Because this may be destructive (if a file with the target name already
                                // exists) we do this last, using the first file name.
                                ASSize_t  buffLen = strlen( argv[2] ) + strlen( argv[3] ) + 20;
                                char  *fn = ( char  *)malloc(  sizeof ( char ) * buffLen );
                                sprintf_safe( fn, buffLen,  "%s/%s%06d.pdf" , argv[2], argv[3], serialNumber );
                                if  ((fp = fopen( fn,  "w"  )) ==  NULL) {
                                               printf(  "Cannot create file in output directory \"%s\"\n" , argv[2] );
                                               free( fn );
                                                return  0;
                                               }
                               fclose( fp );
                                remove ( fn );
                               free( fn );
                               }  // End of command line argument processing

                // Initialize the PDF Library
                int  err =  MyPDFLInit();
                if  (err != 0) {
                               fprintf(stderr,  "Initialization error. See \"AcroErr.h\" for more info.\n" );
                               fprintf(stderr,  "Error system: %d\nError Severity: %d\nError Code: %d\n" ,
                                                                                                ErrGetSystem(err),  ErrGetSeverity(err),  ErrGetCode(err));
                                return  0;
                               }
               
                InitCS(  serialNumberCS  );                              // Initialize the CSMutex

                // Allocate a ThreadInfo object for each thread
               threads = (ThreadInfo  *)malloc(  sizeof (  ThreadInfo  ) * numThreads );

                // Allocate a ThreadArgs object for each thread
               threadArgs = (ThreadArgs  *)malloc(  sizeof (  ThreadArgs  ) * numThreads );

                // Start the threads, passing their thread number to each
                for  (i = 0; i < numThreads; i++) {
                               threadArgs[i].id  = i;
                               threadArgs[i].filename  = argv[1];
                               threadArgs[i].outdir  = argv[2];
                               threadArgs[i].basename  = argv[3];

                                if  (!createThread(  threadFunc, &threadArgs[i], threads[i] ))
                                               printf(  "Thread creation %d failed\n" , i );
                               }

                // Clean up the threads, after waiting for each to exit.
                for  (i = 0; i < numThreads; i++) {
                                waitThread( threads[i] );
                                destroyThread( threads[i] );
                               }

                // Free up threadInfo and threadArgs
               free( threads );
               free( threadArgs );

                // Now destroy the CSMutex
                DestroyCS(  serialNumberCS  );

                // Shut down the PDF Library
                MyPDFLTerm();

                RELEASE_AUTO_POOL(autoReleasePool);  /* Required only on MAC platform */
               
                // Exit
                return  0;
               }