/*****************************************************
**
**  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.
**
******************************************************/
#ifndef HAVE_SPRES
#define HAVE_SPRES

/************************************************************************/
/*! \mainpage  spres: 
 *
 *    
 *                           
 * \section  usage_sec  Usage  
 *     Settings:
 *                                     
 * \section test_sec  Testing                                           
 *                    To test this module, run the test.bash script     
 *                    located in the directory spres/test   
 *                 
 *************************************************************************/
#include <sys/types.h>
#include <dirent.h>
#include <fstream>
#include <iostream>
#include <sstream>
#include <stdio.h>
#include <string.h>

#include "simulation.h"

using namespace std;

//classes for S-PRES calculation
class spres;
class transitionMatrix;


/** each minirun has a record of the final state, plus a pointer to the parent */ 
class shotRecord{
public:
        shotRecord  *next;        /* next in list of shots that go somewhere*/
        shotRecord  *fromNext;    /* next in list of shots that came from somewhere */
        shotRecord  *parent;      /* parent, not in list of all shots but in Markov chain */ 
        double       startRc;
        double       endRc;
        int          start, duration;
        void        *state;
        int          myId;
        string       myFileName;
        unsigned int myMemSize;
        int          numAttempts;
        bool         inMemory;
        stringstream   stateString; 



        shotRecord( double inRc ){
            startRc           = inRc;
            endRc             = inRc; /*true for now...*/
            next              = 0;
            fromNext          = 0;
            parent            = 0;
            myId              = 0;
            numAttempts       = 0;
            myMemSize         = 0;
#ifdef STRING_MEM
            inMemory          = false;
            stateString.str().assign("");
#endif
        }

#ifdef STRING_MEM
        //! destructor: also delete associated file if any 
        void destructAndDeleteFile(){
            if( inMemory == false ) {
                if( remove( myFileName.c_str() ) != 0 ) {
    cerr <<  "Error deleting file: \"" << myFileName.c_str() << "\"\n";
                }
            }
            delete this;
        } 
#endif

};

/** shots can be collected to make a path from the start to final state */
class pathRecord{
public:
        shotRecord *firstShot;    /* next in list */
        int         myId;
        long int    permaSteps;
        double      criterionBest;
        bool        converged;
};

/** each timepoint has a matrix of transfer probabilities from bin i to bin j over delta t */
class transitionMatrix{
public:
    int             nBins, targetShotsForward;
    int             age;
    int            *sampleRate;
    double       **transferRate;
    double        *shotsFrom;
    shotRecord  ***shotsBetween; 
    spres         *mySpres;


    void addShot( shotRecord *s, int startRcBin, int endRcBin );
    int  testDistribution( int row, int highestHitBin );
    bool testRateNonZero();
    void advanceState( double *stateVector );
    long int deleteRow( int row );

    void print( ofstream *logFile );
    void print( ofstream *logFile, int maxBin );

    void printSampleRate( ofstream *logFile );

    /** constructor */
    void init( int n, int numPathsPerBin ){
      int i, j;
     
      nBins = n;
      transferRate   = new double*[n];
      sampleRate     = new int[n];
      shotsFrom      = new double[n];
      shotsBetween   = new shotRecord**[n];
      targetShotsForward = numPathsPerBin;
      
      for( i = 0; i < n; i++ ) {
          transferRate[i]  = new double[n];
          shotsFrom[i]     = 0;
          sampleRate[i]    = numPathsPerBin;
          shotsBetween[i]  = new shotRecord*[n];
          for( j = 0; j < n; j++ ){
              shotsBetween[i][j] = 0;
          }
      }
    }

    /** clear saved data, for reuse */
    unsigned long int reset();

    /** destructor */
    ~transitionMatrix();

    private:
        

};


class spres{

public:
    
    /* details of RC bins */
    int      nRcBins, workingBin;
    int      pathsPerBin;
    double   rcBinMin;
    double   rcBinMax;
    double   rcBinSize;

    /* track this for benchmarking */
    long unsigned int totalDynamicsSweepCount;

    /* useful tracker of average state size, for optimising strstream usage */
    unsigned int      shotMemSize;

    /* current estimated probability density in each state */
    double           *stateVector;	
    double           *dataVector;  //vector of derived data
    int               dvCount;     //size of derived data vector
    transitionMatrix *transMat, *transMatPrev;
    int               timeStep;

    ofstream          spresLog;
    ofstream          dataLog;

    simulation       *pt_state;

    /* track the space currently taken up by shot records in RAM */
    unsigned long int memBytes;

    void sanitiseInputs(simulation *state, 
                  int    numPathsPerBin, 
                  long   numSpresBins);

    void spresRun( simulation *state, 
                  int    numPathsPerBin, 
                  long   numSpresBins);
                  
    bool        spresRunComplete( int matricesSoFar, int firstFullMatrix, int maxMatrices, int minCompleteMatrices);
    shotRecord* selectParentShot( simulation *state, transitionMatrix *m, int row );
    shotRecord* seekParentShot( simulation *state, transitionMatrix *m, int row, long int shotId );
    double      getStartFlux( simulation *state, transitionMatrix *transMat );
    
    void printRates( ofstream *logFile, int numPaths );

    void initStateData();
    void collectStateData( double beliefWeight );
    void writeDataFile();

    void printShotRecords( ofstream *logFile, int numPaths ){
        shotRecord *s;
        int         i;
        
       *logFile << "#PathId ShotId startRC endRC startTime duration parentShot\n";

        for( i = 0; i < numPaths; i++ ) {
             while( s != 0 ) {

                  if( s->duration > 0 ) {
                    *logFile << s->myId     << " "
                           << s->startRc  << " "
                           << s->endRc    << " "
                           << s->start    << " "
                           << s->duration;

                       if( s->parent != 0 ) {
                          *logFile << " " << s->parent->myId  << endl;           
                       }
                       else{
                          *logFile << " -1" << endl;  
                       }
                  }
                  s = s->next;
             }
        }
    }

/** create a unique & descriptive id for a given shot
*/
/** update the shot name if temp storage has moved
*/
string redefineShotFilename( string oldName, string newStem ){

    string        newName;
    unsigned int  splitAt;

    splitAt = oldName.find_first_of( "_" );
    newName = newStem + oldName.substr( splitAt );

    cerr << "Updated old shot name: " << oldName
         << "\nto: "                  << newName << endl;

    return( newName );

}



/* functions for managing shot files */
string    defineShotFilename( simulation *state, shotRecord *shot, shotRecord *parent );
int       testShotFileName( string fileName );
int       getFileShotId( string fileName, double *rc );
int       getFileEndRcRow( string fileName, double *rc );
int       getFileStartRcRow( string fileName, double *rc );
long int  getFileAge( string fileName );
int       deleteFilesAge( string dirName, long int age );
long int  getFileShotId( string fileName );
long int  getFileParentId( string fileName );
string    extractShotnameField( string fileName, int index );

long int  getOldestCompleteRow( simulation    *state,
                                     string    dirName, 
                                     int      *binIndex, 
                                     long int *secondOldestState,
                                     int      *highestShotId  );
int        getHighestShotId( string dirName );

private:

    int  spresCriterion;
    int  criterionOpIndex;   

    /** bin reaction coordinates */
    int assignRcBin( double rc ); // in spres_2.c   just finds out the right bin 
    


};


#endif
