/*****************************************************
**
**  S-PRES Copyright (c) 2010
**
**  Joshua T Berryman
**
**  This file is part of S-PRES
**
**  S-PRES is Free software: you can redistribute it and/or modify
**  it under the terms of the GNU General Public License as published by
**  the Free Software Foundation, either version 3 of the License, or
**  (at your option) any later version.
**
**  S-PRES is distributed in the hope that it will be useful,
**  but WITHOUT ANY WARRANTY; without even the implied warranty of
**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
**  GNU General Public License for more details.
**
**  You should have received a copy of the GNU General Public License
**  along with this program.  If not, see <http://www.gnu.org/licenses/>.
**
**  If you make use of this program for research 
**  resulting in a publication, please cite:
**
**  Joshua T Berryman and Tanja Schilling, 
**  Sampling rare events in nonequilibrium and nonstationary systems. 
**  
**  J Chem Phys, 133(24):244101, Dec 2010.
**
******************************************************/
#include <sys/types.h>
#include <dirent.h>
#include <iostream>
#include <string>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <cstring>
#include <limits.h>

#include "../../spres/src/spres.h"

using namespace std;


///define the ID for a path segment, or "shot".  If we are saving to file, this is the filename.
///If we are working entirely in memory, then this serves as a data structure describing the shot.
string spres::defineShotFilename( simulation *state, shotRecord *shot, shotRecord *parent ){

    stringstream buf;

    buf.str().assign("");

    buf << "rest_" 
        << (shot->start + shot->duration) << "_"
        << parent->myId                   << "_"
        << shot->myId                     << "_"
        << shot->startRc                  << "_"
        << shot->endRc;
      
    shot->myFileName.assign( buf.str() );

    return( shot->myFileName );

}





/** delete files of a given age..used if working on the filesystem */
int spres::deleteFilesAge( string dirName, long int age ){

   DIR            *dp;
   struct dirent *fp;
   long int       fileAge, count;
   string          fileWithPath;

   count = 0;

   /* open dir & test that it exists */
   if((dp  = opendir(dirName.c_str())) == NULL) {
      cout << "Error opening " << dirName << endl;
      exit( EXIT_FAILURE );
   }

   while( ( fp = readdir( dp ) ) ) {
       fileAge =  getFileAge( fp->d_name );       
       if( fileAge <= age ){
            fileWithPath = dirName;
            fileWithPath.append( fp->d_name );
            if( remove( fileWithPath.c_str() ) != 0 ) {
              cerr <<  "Error deleting file: \"" << fileWithPath.c_str() << "\"\n";
            }
            count++;
       }
   }

   /* tidy up */
   closedir( dp );

   return( count );
}



/** identify the second oldest set of states which are currently sourcing shots */
int spres::getHighestShotId( string dirName ){

   DIR            *dp;
   struct dirent *fp;
   int             fileId, highestId;

   highestId = -1;

   /* open dir & test that it exists */
   if((dp  = opendir(dirName.c_str())) == NULL) {
        cerr << "Error opening " << dirName << endl;
        exit( EXIT_FAILURE );
   }

   while( ( fp = readdir( dp ) ) ) {            
        
        fileId = getFileShotId( fp->d_name );
        if( fileId > highestId ){
            highestId = fileId;
        }        
   }

   /* tidy up */
   closedir( dp );

   return( highestId );

}

/** identify the most recently written (oldest state) complete set of shots */
long int spres::getOldestCompleteRow(simulation *state,
                                     string    dirName, 
                                     int      *binIndex, 
                                     long int *secondOldestState, 
                                     int      *shotId ){

   DIR            *dp;
   struct dirent *fp;
   long int       countAtAge, countAtRow;
   long int       fileAge, oldestFile, secondOldest, ageCeiling;
   int             fileFromRcRow;
   int             highestRow, fileId, highestId;

   highestId = 0;
   highestId = 0;

   /* read filenames and find the oldest with enough for a complete matrix row */
   ageCeiling = LONG_MAX;
   do {
   
      /* open dir & test that it exists */
      if((dp  = opendir(dirName.c_str())) == NULL) {
          cout << "Error opening " << dirName << endl;
          exit( EXIT_FAILURE );
      }

      oldestFile   = 0;
      while( ( fp = readdir( dp ) ) ) {

         fileAge =  getFileAge( fp->d_name );       
         if( fileAge > oldestFile && fileAge < ageCeiling ){
               oldestFile = fileAge;
         }
      }

      /* refresh */
      closedir( dp );
      dp  = opendir(dirName.c_str());
   
      /* check that there are enough for one complete row */ 
      countAtAge = 0;
      while( ( fp = readdir( dp ) ) ) {
           fileAge =  getFileAge( fp->d_name );       
	       if( fileAge == oldestFile ){
                countAtAge++;
           }
      }

      closedir( dp );
      ageCeiling = oldestFile;

      cerr << "Found " << countAtAge << " files at age " << oldestFile << endl;
      cerr << "Require a minimum of " << pathsPerBin << " to be working *from* this timepoint." << endl;

   } while( countAtAge < pathsPerBin && oldestFile > 0 );

   /* identify highest complete row at this age */
   highestRow = nRcBins;
   do {
      /* open dir */
      dp  = opendir(dirName.c_str());
      countAtRow = 0;
      highestRow--;   

      while( ( fp = readdir( dp ) ) ){

           double Rc;

           fileAge =  getFileAge( fp->d_name );       
           if( fileAge == oldestFile ){
               fileFromRcRow = getFileStartRcRow( fp->d_name, &Rc );
               if( fileFromRcRow == highestRow ) {
                  countAtRow ++;
               }
           }
      }
      closedir( dp );
     
    } while( countAtRow <  pathsPerBin && highestRow > 0 ); 
   
    /* identify second oldest state & highest ID number */ 
    dp           = opendir(dirName.c_str());
    secondOldest = 0;
    highestId    = 0;
    while( ( fp = readdir( dp ) ) ) {            
        
        fileAge =  getFileAge( fp->d_name );       
        if( fileAge > secondOldest && fileAge < oldestFile ){
              secondOldest++;
        }

        fileId = getFileShotId( fp->d_name );
        if( fileId > highestId ){
            highestId = fileId;
        }        
    }
    closedir( dp );  

   /* record the index of the highest complete row and return its age */
   *binIndex          = highestRow;
   *secondOldestState = secondOldest;
   *shotId            = (highestId + 1);
   return( oldestFile );

}


