/*****************************************************
**
**  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_SHISTATS
#define HAVE_SHISTATS

#include <fstream>
#include <iostream>
#include <stdlib.h>
#include "../../shi/src/shi.h"
#include "../../spres/src/spres.h"


using namespace std;

typedef enum {SHI_STATSOP_LARGEST_NUCLEUS    = 0,  
               SHI_STATSOP_ENERGY             = 1,  
               SHI_STATSOP_NUM_UPSPINS        = 2,  
               SHI_STATSOP_NUM_BURIED         = 3,  
               SHI_STATSOP_CLUSTER_MOMENT     = 4,  
               SHI_STATSOP_LARGEST_CLUSTER    = 5,  
               SHI_STATSOP_STRUCTURE_FACTOR   = 6,  
               SHI_STATSOP_N_HOPS             = 7,  
               SHI_STATSOP_N_ASEP_FLUXES      = 8,  
               SHI_STATSOP_NUM_OPS            = 9} shiOpId;


class shi;

class shiStatsOp{
    
 public:
    string   nameInHeader; //allow them to self-identify
    shiOpId  myOp;
    ofstream logFile;

    //initialisation function
    void setOp(string name, shiOpId id, string logFileName){

	    nameInHeader = name;
	    myOp         = id;

	    if( !logFileName.empty() ) {    
	        logFile.open( logFileName.c_str() );
	        if( !logFile.good() ){
		        cerr << "failed to open log file: " << logFileName << endl; 
		        exit( 8 );
	        }
	    }
    }

 private:
    void    *context; //allow them to store state if needed
};



/*! The class shiStats describes a series of postprocessing calculations to run on a shear-ising trajectory
 *
 */
class shiStats {
   
    public:

    //pack or unpack data from files
    void requestOperation( string opName, shiOpId id, string logFileName );
    void requestOperation( string opName, int    id, string logFileName );
    
/** 
*  Write out the header for a stats data file. The header contents describe the operations performed
*
*/ 
    void printStatsHeader();   
  
/** 
*  Write out the stats metadata for a frame.
*
*/          
    void printStatsSnap();    

/** Write out average values of metadata over the trajectory.
 *
 *  This function writes a summary/average/sysnthesis of the structural data over the course of the simulation.
 */
    void printStatsFooter(void);              

    void   calcFrameStats( shi *shiState ); 
    double calcStat( shiStatsOp *s, shi *state );
    double readOpValue( int opIndex ){
          return(opsValues[ opIndex ]);
    }

    //Actual Stats functions

    /** counts potential nuclei */
    void buildClusters( shi *state );
    int  largestClusterSize( shi *state, ofstream *logFile );
    int  numberBuried( shi *state );
    int  structureFactor( shi *state );
    int  nHops( shi *state );


    //initialiser
    void init(void){
	    opsRequested = 0;
    }

    /** List of operations requested */
    shiStatsOp        opsList[SHI_STATSOP_NUM_OPS];

    /** List of return values from most recent calculation of each operation */
    double         opsValues[SHI_STATSOP_NUM_OPS];
        
    /**how many ops are currently requested? */
    int getOpsRequested(void){
        return( opsRequested );
    }
    int      opsRequested;

private:
    int      pairCorrelate( shi *shiState, int ii, int jj, int *tries );
    bool     haveClusters; //set true if have up-to-date clusters

};

#endif
