/*
//
//   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.
//
*/
#include "MyPDFLibUtils.h"
#include "WatchFolder.h"
#include "stdio.h"

// sleep in windows is in ms, on unix it is in seconds.
#ifdef WINDOWS
#define base 1000
#else
#define base 1
#endif

#ifdef UNIX_ENV
#include "stdlib.h"
#include "string.h"
#endif
WatchFolder::WatchFolder(ASPathName  folderToWatch,  ASInt32  numFiles) : numToReturn(numFiles){
                // initialise the mutex used to protect our vectors
                InitCS(this->watchFolderMutex);
               
               this->folder =  ASFileSysCopyPath(NULL, folderToWatch);
               
                ASFileSysItemPropsRec  folderProps;
               folderProps.size  =  sizeof (ASFileSysItemPropsRec);
                ASErrorCode  err =  ASFileSysGetItemProps(NULL,this->folder,&folderProps);
               memcpy(&(this->folderModDate),&folderProps.modDate, sizeof (ASTimeRec));
                ASFileSysItemPropsRec  fileProps;
               fileProps.size  =  sizeof (ASFileSysItemPropsRec);

                // we allocate an ASPathName for each path found. We always allocate before 
                // using the folder iterator, so we have to free the last one created.
                // Our vector takes ownership of the ASPathNames
                ASPathName  *newPath =  static_cast< ASPathName  * > (ASmalloc( sizeof (ASPathName)));
                // We have the folder, we build up the list of files.
                ASFolderIterator  folderIter =  NULL;
               folderIter =  ASFileSysFirstFolderItem(NULL,this->folder, &fileProps, newPath);
                ASBool  iterate =  true ;
                if  (folderIter ==  NULL){
                               iterate =  false ;
               }
                while  (iterate){
                                // we only add the file if it is a pdf
                                char * diPath =  ASFileSysDIPathFromPath(NULL,*newPath,  NULL);
                                string  pathToTest(diPath);
                                ASfree(diPath);
                                if  (pathToTest.rfind( ".pdf" ) == pathToTest.length()-4){
                                               this->addFile(*newPath);
                               }             
                                else
                                                ASFileSysReleasePath(NULL, *newPath);
                               iterate =  ASFileSysNextFolderItem(NULL,folderIter, &fileProps, newPath);
               }
                if  (folderIter !=  NULL)
                                ASFileSysDestroyFolderIterator(NULL,folderIter);
                ASfree(newPath);

}

WatchFolder::~WatchFolder(){
                // clean out the vectors

                while (dirContents.size()>0){
                                char * tmpStr =  dirContents.back();
                                dirContents.pop_back();
                                ASfree(tmpStr);
               }
                while (doneFiles.size()>0){
                                char * tmpStr =  doneFiles.back();
                                doneFiles.pop_back();
                                ASfree(tmpStr);
               }             
                ASFileSysReleasePathNULL,  folder  );
                DestroyCS(this->watchFolderMutex);           
}             


void  
WatchFolder::watchFolder(){
                ASFileSysItemPropsRec  folderProps;
               folderProps.size  =  sizeof (ASFileSysItemPropsRec);
                EnterCS(this->watchFolderMutex);
                ASInt32  oldNum = this->numToReturn;
                LeaveCS(this->watchFolderMutex);
                while  (1){
                               printf( "Scanning for input files...\n" );
                                ASErrorCode  err =  ASFileSysGetItemProps(NULL,this->folder,&folderProps);
                                while  (memcmp(&folderProps.modDate,&(this->folderModDate), sizeof (ASTimeRec))==0){
                                                // this while loop represents the main idle loop of the primary thread.
                                                // we can test to see if enough files have been converted, if so we can 
                                                // simply exit the method.
                                                EnterCS(this->watchFolderMutex);
                                                if  (this->numToReturn  == 0){
                                                                LeaveCS(this->watchFolderMutex);
                                                               printf( "File limit reached, terminating\n" );
                                                                return ;
                                               }
                                                LeaveCS(this->watchFolderMutex);

                                                Sleep(10*base);
                                               err =  ASFileSysGetItemProps(NULL,this->folder,&folderProps);
                                                if  ((err != 0) || folderProps.isThere  ==  false ){
                                                               fprintf(stderr, "Error getting the folder properties : WatchFolder.cpp:103\n" );
                                               }
                                                EnterCS(this->watchFolderMutex);
                                                if  (this->numToReturn  == 0) {
                                                                LeaveCS(this->watchFolderMutex);
                                                                return ;
                                               }
                                                ASInt32  newNum = this->numToReturn;
                                                LeaveCS(this->watchFolderMutex);
                                                if  (newNum!=oldNum){
                                                               printf( "%d files to go\n" ,newNum);
                                                               oldNum=newNum;
                                               }
                               }
                                // there must have been an update on the folder...
                                // look at each file, if we haven't seen it before, add it to our list.
                                ASFileSysItemPropsRec  fileProps;
                               fileProps.size  =  sizeof (ASFileSysItemPropsRec);
                                size_t  size =  sizeof (ASPathName);
                                ASPathName  *newPath =  static_cast< ASPathName  * > (ASmalloc(size));
                                // We have the folder, we build up the list of files.
                                ASFolderIterator  folderIter =  ASFileSysFirstFolderItem(NULL,this->folder, &fileProps, newPath);
                                char * buff =  ASFileSysDisplayStringFromPath(NULL,*newPath);
                                ASBool  iterate =  true ;
                                if  (folderIter ==  NULL){
                                               iterate =  false ;
                               }

                                while  (iterate){
                                                // we only add the file if it is a pdf
                                char * diPath =  ASFileSysDIPathFromPath(NULL,*newPath,  NULL);
                                string  pathToTest(diPath);
                                ASfree(diPath);
                                                if  (pathToTest.rfind( ".pdf" ) == pathToTest.length()-4) {
                                                               this->addFile(*newPath);
                                               }
                                                else
                                                                ASFileSysReleasePath(NULL, *newPath);
                                               iterate =  ASFileSysNextFolderItem(NULL,folderIter, &fileProps, newPath);
                               }
                               memcpy(&(this->folderModDate),&folderProps.modDate, sizeof (ASTimeRec));
                                ASFileSysDestroyFolderIterator(NULL,folderIter);
                                // Similar to the constructor, we only need to free the unused instance of newPath
                                ASfree(newPath);
               }
}

void  
WatchFolder::addFile(ASPathName  newPath){
                // A robust implementation would make sure the file is not being written to here
                // This sample leaves this as an exercise for the reader
                char  * pathToTest =  ASFileSysDIPathFromPath(NULL,newPath,  NULL);
                ASBool  fileExists =  false ;
                EnterCS(this->watchFolderMutex);
                for  ( unsigned   int  tmp=0;tmp<doneFiles.size();tmp++){
                                char * tmpStr =  doneFiles[tmp];
                               fileExists = (strcmp(pathToTest,tmpStr) == 0);
                                if  (fileExists){
                                                break ;
                               }
               }
                LeaveCS(this->watchFolderMutex);
                if  (fileExists ==  false ){
                                // we need to test the todo list, we have to do this within the 
                                // critical section in case it is removed as we test it.
                                // A better implementation might allow for a shrinking todo list.
                                EnterCS(this->watchFolderMutex);
                                // make sure it isn't on the todo list either.
                                for  ( unsigned   int  tmp2=0;tmp2<dirContents.size();tmp2++){
                                                char * tmpStr =  dirContents[tmp2];
                                               fileExists = (strcmp(pathToTest,tmpStr) == 0);
                                                if  (fileExists){
                                                                break ;
                                               }
                               }
                                LeaveCS(this->watchFolderMutex);
                                if  (fileExists ==  false ){
                                                // debug:printf("Adding new file %s\n",ASFileSysDIPathFromPath(NULL,newPath,NULL));
                                                EnterCS(this->watchFolderMutex);
                                                dirContents.push_back(pathToTest);
                                                LeaveCS(this->watchFolderMutex);
                               }
               }
                ASFileSysReleasePath(NULL, newPath);
}

const   char  * 
WatchFolder::getFile(){
                // the newPath == NULL indicating one of our worker threads is looking for work
                EnterCS(this->watchFolderMutex);
                if  (this->numToReturn  == 0){
                                // we do not need to convert any more files, returning NULL to the worker thread
                                // indicates it should terminate.
                                LeaveCS(this->watchFolderMutex);
                                return   NULL;
               }
                while  (dirContents.size() == 0){
                                LeaveCS(this->watchFolderMutex);
                                Sleep(1*base);
                                EnterCS(this->watchFolderMutex);
                                if  (this->numToReturn  == 0){
                                                LeaveCS(this->watchFolderMutex);
                                                return   NULL;
                               }
               }
                // we get here, we know we have wokrk to be done and we are inthe mutex.
                // we need to test that as well as having files to be processed, we still
                // need to process them (i.e. some other thread might have taken the number
                // of files we have to process down to 0 while we were waiting for work.
                if  (this->numToReturn  == 0){
                                // leave the critical section and return null, this tells the worker 
                                // thread to terminate.
                                LeaveCS(this->watchFolderMutex);
                                return   NULL;
               }
                char  * tmp =  dirContents.back();
                dirContents.pop_back();
               
                // done files needs to be updated
                doneFiles.push_back(tmp);
               
                // update the number of files still to be handled
               this->numToReturn--;
                LeaveCS(this->watchFolderMutex);
                return  tmp;
}