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

#ifdef MAC_ENV
#include "macUtils.h"
#else
#include <sys/types.h>
#include <sys/stat.h>
#endif

#include "CosCalls.h"
#include "ASCalls.h"
#include "PDCalls.h"
#include "MyPDFLibUtils.h"
#include "Peddler.h"
#include <stdio.h>

#define URI_K ASAtomFromString("URI")

// Constructor, init's the pdfl 
Peddler::Peddler(){
                int  err =  MyPDFLInit();  // initialize the PDFLib 
                if  (err != 0)                                      // check for error after initialization 
               {
                               cerr <<  "Initialization error. See \"AcroErr.h\" for more info.\n"  << endl;
                               cerr <<  "Error system: "  <<  ErrGetSystem(err) << endl;
                               cerr <<  "Error Severity: "  <<  ErrGetSeverity(err) << endl;
                               cerr <<  "Error Code: "  <<  ErrGetCode(err) << endl;
                                return ;  // if we cannot init the pdfl, we terminate the application
               }
               this->theKeyList=NULL;
}

// ctor initializing the internal files to use 
Peddler::Peddler( const   string  &pdfFile,  const   string  &URIFile){
                fPDFFileName  = pdfFile;
                fURIFileName  = URIFile;
                int  err =  MyPDFLInit();  // initialize the PDFLib 
                if  (err != 0)                                      // check for error after initialization 
               {
                               cerr <<  "Initialization error. See \"AcroErr.h\" for more info.\n"  << endl;
                               cerr <<  "Error system: "  <<  ErrGetSystem(err) << endl;
                               cerr <<  "Error Severity: "  <<  ErrGetSeverity(err) << endl;
                               cerr <<  "Error Code: "  <<  ErrGetCode(err) << endl;
                                return ;  // if we cannot init the pdfl we terminate the appliation.
               }
               this->theKeyList=NULL;
}

// dtor, deletes the keyList if required and terminates the pdfl 
Peddler::~Peddler(){
                if  (this->theKeyList  !=  NULL){
                                delete  this->theKeyList;
               }
                MyPDFLTerm();      // terminate the PDFLib
}


// initially, I am just caching the filename locally as a string, and doing all the work in the 
//           makeURI method, I will re-factor this in time to move the file level management to this 
//           method and cache file handles instead...
void
Peddler::SetPDFFile( const   string  &pdfFile){
                fPDFFileName  = pdfFile;
}


// initially, I am just caching the filename locally as a string, and doing all the work in the 
//           makeURI method, I will re-factor this in time to move the file level management to this 
//           method and cache file handles instead... 
void
Peddler::SetURIFile( const   string  &URIFile){
                fURIFileName  = URIFile;
}

// doing the actual work. I should abstract this further... 
void
Peddler::MakeURIs( const   string  &outputPDFFile){

                volatile   PDDoc  pdDoc;
                volatile   PDWordFinder  wordFinder;
                volatile   ASPathName  path;
                volatile   ASInt32  errCode = 0;
// all our actions with the PDFL occur here, wrap it in an exception block
DURING

#if MAC_ENV
{
//OSErr err;
                // Set the working directory where the application was launched from - This must be done after initializing
                // the Toolkit 
//           err = PDFLSetWorkDirToAppDir();
}
#endif
               
                // Open the file that has the key value pairs
               this->theKeyList  =  new   keyList();
               this->theKeyList->importFile(fURIFileName);

                char  *file = ( char  *)fPDFFileName.data();
                if  ((pdDoc =  MyPDDocOpen(file))==NULL) {
                                // If we cannot open the file, we informt he user through cerr, and exit. 
                                // We do this because we understand that we are the outer loop (i.e. there is no exception handler here).
                               cerr <<  "Sample application error: Unable to open input PDF file" ;
                                ASRaise(ASRegisterErrorString(ErrAlways, "Unable to open input PDF file" ));
               }
                // cache the document for the wordfinder callback
               this->theDoc  = pdDoc;

                // get the first page before setting up the word finder
               this->pdPage  =  PDDocAcquirePage  (pdDoc, 0);

                // Create a word finder that will populate tables of words sorted in PDF order
                //   and words sorted in x-y order 
               wordFinder=  PDDocCreateWordFinder(pdDoc,  NULLNULLNULL, 0, (WXE_PDF_ORDER  |  WXE_XY_SORT),  NULL);

                // As the word finder callback is static (for linkage reasons), we need to pass the isntance of the clientData...
                PDWordFinderEnumWords(wordFinder,  PDAllPages,ASCallbackCreateProto(PDWordProc,&(this->TEWordProc)),( void  *) this );


#if MAC_PLATFORM
               path =  GetMacPath(( char  *)outputPDFFile.data());
#else
               path =  ASFileSysCreatePathName(NULLASAtomFromString( "Cstring" ), ( char  *)outputPDFFile.data(), 0);
#endif
                PDDocSave(pdDoc,  PDSaveFull, path,  NULLNULLNULL);

HANDLER
               errCode =  ERRORCODE;
END_HANDLER
               
                if  (pdDoc!=  NULL  && this->pdPage  !=  NULL) {
                                PDPageRelease(this->pdPage);
               }
                if  (pdDoc!=  NULL  && path !=  NULL) {         
                                ASFileSysReleasePath  (NULL, path);
               }
                if  (pdDoc!=  NULL  && wordFinder !=  NULL) {
                                // Destroy the wordfinder
                                PDWordFinderDestroy(wordFinder);               
               }
                if  (pdDoc !=  NULL) {
                                PDDocClose(pdDoc);
               }       
                if  (errCode != 0) {
                                char  buf[256];
                                ASGetErrorString(ERRORCODE, buf,  sizeof (buf));
                               fprintf(stderr,  "\nError code: %d, Error Message: %s\n" ,  ERRORCODE, buf);
                                return ;
               }
}


// The callback function. The PDFL calls into this function for every word it finds in the document.
//           If one of the calls into pdfl throws an exception, I let the outer block catch it, I do not attempt
//           to recover from any of the errors (the exceptions that might be thrown either involve a malformed argument
//           list, or some protection issue in writing annotations to the file, neither would indicate we should attempt to
//           continue with the enumeration).
ACCB1  ASBool   ACCB2  
Peddler::TEWordProc(PDWordFinder  wObj,  PDWord  wInfo,  Int32    pageNum,  void  * clientData)
{
                // get the this pointer and retrieve internal state
                Peddler  *thisData = (Peddler  *)clientData; 
                keyList  * theKL = thisData->theKeyList;
                PDDoc  docP = thisData->theDoc;

                // If we were passed a NULL word, just ignore it 
                if  (wInfo == (PDWordNULL) {
                                return   true ;
               }

                // we do not handle rotated words...
                if (PDWordIsRotated(wInfo)){
                                return   true ;
               }

                // if this is our first time through, or the page changes, update our cached copy
                if  (PDPageGetNumber(thisData->pdPage) != pageNum) {
                               thisData->pdPage  =  PDDocAcquirePage  (docP, pageNum);
               }
               
                // space to hold the word
                char                        str[256];
                // number of characters in word after filter
                ASInt16                  retSize;
                // get the word
                PDWordFilterWord( wInfo, str, 256, &retSize );
                // If you'd rather match up punctuation, use the API call below instead
                //           PDWordGetString(wInfo, str, 256);

        // We create a link for each quad, this allows us to hyperlink words that are hyphenated 
                int  numQ =  PDWordGetNumQuads(wInfo);
                for  ( int  i=0; i<numQ; i++ ) {
                                ASFixedQuad              quad;
                                PDWordGetNthQuad( wInfo, i, &quad );
                                const   string  lookupVal = str;
                                string  value = theKL->lookupElement(lookupVal);
                                if  (!value.empty()){
                                                // ths annotation box.
                                                ASFixedRect  annotBox; 
                                                // define the annotation box
                                               annotBox.left      = quad.tl.h;           
                                               annotBox.top        = quad.tl.v;
                                               annotBox.right    = quad.br.h;
                                               annotBox.bottom  = quad.br.v;
                                               
                                                // create the link annotation
                                                PDAnnot  myannot=  PDPageAddNewAnnot(thisData->pdPage, -2,  ASAtomFromString( "Link" ),&annotBox);
                               
                                                // create the link action (it is of type "URI")
                                                PDAction  myAct =  PDActionNew(docP,  URI_K);           

                                                // bind the link annotation to the action
                                                PDLinkAnnotSetAction  (myannot, myAct);   
                                               
                                                // set the color of the annotation to blue
                                                PDColorValue  color =  new   PDColorValueRec;
                                               color->space=PDDeviceRGB;
                                               color->value[0] = 0;  // no red
                                               color->value[1] = 0;  // no green
                                               color->value[2] = 0xffff;  // blue
                                               color->value[3] = 0;  // unused
                                                PDAnnotSetColor(myannot,color);

                                                // get the action's associated cos object
                                                CosObj  theObj =  PDActionGetCosObj(myAct);
               
                                                // get the cos document object
                                                CosDoc   theDoc  =  PDDocGetCosDoc(docP);
                                               
                                               
                                                // create the cos string that represents the URI
                                                CosObj  theURI =  CosNewString  (theDoc,  false , ( char  *)value.c_str(), value.length());

                                                // populate the cos dictionary
                                                CosDictPut(theObj,  ASAtomFromString( "URI" ), theURI);
                               }
               }
                return   true ;
}