/*****************************************************
**
**  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 <iostream>
#include <string>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <cstring>

#include "shi.h"


shi* shi::copyShi(shi *old){
   shiStats *copyStats = new shiStats;
   shi      *copy      = new shi;

   
   copy->parameterFileName = old->parameterFileName;
   copy->configFileName    = old->configFileName;
   copy->restartFileName   = old->restartFileName;
   copy->logFileName       = old->logFileName;
   copy->stats             = copyStats;
   
   copy->setupNew();
   
  return( copy );
  
}

void shi::readParameterFile(){

    ifstream infile;
    char     line[2*SHI_MAX_X_SIZE];
    char     quantity[2*SHI_MAX_X_SIZE];
    char     value[2*SHI_MAX_X_SIZE];
    int      check;

    infile.open( parameterFileName.c_str() );
    if ( !infile.good() ) { //open and test parameter file
        cerr << "Error parameter file: \"" << parameterFileName << "\" not found\n";
        exit(8);
    }

    /* set defaults */
    fieldStrength    = 0.05; /*Allen & Frenkel 2008 version */
    couplingConstant = 0.65; /*Allen & Frenkel 2008 version */
    temperature      = 1.0; /*Allen & Frenkel 2008 version */
    spresNumMatrices   = 0;
    spresNumCompleteMatrices = 0;
    spresNumBins       = 0;
    dynamicsType     = SHI_DYNAMICS_NONCONSERVED;
    magnetisation    = 0.0;
    xSize            = 1;
    ySize            = 1;

    //read parameter file
    while (infile.peek() != EOF) {
        infile.getline(line,sizeof(line));

        //allow whitespace or #-delimited comments
        check = sscanf(line, "%s", quantity );
        if( check <= 0 ) {
            continue;
        }
        if( quantity[0] == '#' ){
            continue;
        }
        if( quantity[0] == '\n' ){
            continue;
        }

        
        //meaningful entries are in key-value pairs followed by comments if desired
        check = sscanf(line, "%s%s", quantity, value);
        if (check < 2) {
            cerr << "Warning: wrong line format in " << parameterFileName << endl;
            cerr << "Line was: _" << line << "_\n";
        }

        if (strcmp(quantity,"xSize")==0)                   { xSize = atoi( value ); }
        else if (strcmp(quantity,"ySize")==0)             { ySize = atoi( value ) ;}
        else if (strcmp(quantity,"seed")==0)              { seed  = atoi( value ) ;}
        else if (strcmp(quantity,"fieldStrength")==0)     { fieldStrength = atof( value ) ;}
        else if (strcmp(quantity,"couplingConstant")==0)  { couplingConstant = atof( value ) ;}
        else if (strcmp(quantity,"dynamicsType")==0)      { dynamicsType  = shiDynamics( atoi( value ) );}
        else if (strcmp(quantity,"magnetisation")==0)     { magnetisation = atof( value ) ;}
        else if (strcmp(quantity,"temperature")==0)       { temperature = atof( value ) ;}
        else if (strcmp(quantity,"shearRate")==0)         { shearRate = atof( value ) ;}
        else if (strcmp(quantity,"shearDecayRate")==0)    { shearDecayRate = atof( value ) ;}
        else if (strcmp(quantity,"mcSteps")==0)           {mcSteps = atol( value );}
        else if (strcmp(quantity,"mcSnapEvery")==0)       {mcSnapEvery = atol( value );}
        else if (strcmp(quantity,"asepInsertP")==0)       { asepInsertP = atof( value ) ;}
        else if (strcmp(quantity,"asepRightHopP")==0)     { asepRightHopP = atof( value ) ;}
        else if (strcmp(quantity,"asepEscapeP")==0)       { asepEscapeP = atof( value ) ;}
        else if (strcmp(quantity,"spresSteps")==0)          {spresSteps=atoi( value );}
        else if (strcmp(quantity,"spresNumCompleteMatrices")==0)    {spresNumCompleteMatrices=atoi( value );}
        else if (strcmp(quantity,"spresNumMatrices")==0)    {spresNumMatrices=atoi( value );}
        else if (strcmp(quantity,"spresPathsPerBin")==0)    {spresPathsPerBin=atoi( value );}
        else if (strcmp(quantity,"spresNumBins")==0)        {spresNumBins=atoi( value );}
        else if (strcmp(quantity,"spresCriterion")==0)      {spresCriterion=shiOpId( atoi( value ) );}
        else if (strcmp(quantity,"spresCriterionMin")==0)   {spresCriterionMin=atof( value );}
        else if (strcmp(quantity,"spresCriterionMax")==0)   {spresCriterionMax=atof( value );}
        else if (strcmp(quantity,"spresLogFileName")==0)    {spresLogFileName.assign( value );}
        else if (strcmp(quantity,"spresRatesFileName")==0)  {spresRatesFileName.assign( value );}
        else{
            cerr << "Error: Parameter line not recognised: " << line << endl;
            exit( 8 ); 
        }
    }

    negInvT = -1.0 / temperature;

    infile.close();
}

void shi::setupNew(){

    int    i;
 
    readParameterFile();
    
    iran = &rng.idum;
    rng.init(seed);
    
    //sanity check
    if( xSize * ySize <= 0 ) {
        cerr << "System x/y size not set in parameter file: " << parameterFileName << "endl";
        exit( 8 ) ;
    }

    //init the cluster list
    clust    = 0;
    haveClusters = false;

    //allocate & init 2D array for spins.
    spins = new SPIN_T*[xSize];
    for( i = 0; i < xSize; i++ ){
        spins[i] = new SPIN_T[ySize];
    }

    if( dynamicsType ==  SHI_DYNAMICS_KAWASAKI || dynamicsType ==  SHI_DYNAMICS_ASEP ) {
       buildSpinList();
    }else{
        nUpSpins = 0;
    }
    reset();

    

}
void shi::reset(){

    int     i, j, runningM, targetM;
    double flip; 

     
    runningM  = 0;
    pbcShear  = 0;
    haveClusters = false;

    if( dynamicsType == SHI_DYNAMICS_NONCONSERVED || dynamicsType == SHI_DYNAMICS_ASEP ) { 
        for( i = 0; i < xSize; i++ ){
            for( j = 0; j < ySize; j++ ) {
                spins[i][j] = SPIN_DOWN;
            }
        }
#ifdef SHI_GLAUBER_EQUIL_ON_RESET
        if( dynamicsType == SHI_DYNAMICS_NONCONSERVED ){ //allow an equil
            for( int i = 0; i < 1000; i++ ){
                saveFrameToFile( (char *)restartFileName.c_str() );
                stepTime();
            }
        }
#endif
    }
    else{//conserved dynamics - initialise random spin gas.
 
       //set each spin
       for( i = 0; i < xSize; i++ ){
            for( j = 0; j < ySize; j++ ) {
                flip=rng.ran2();
                if( flip > magnetisation ) {   
                    spins[i][j] = SPIN_DOWN;
                }
                else{
                    spins[i][j] = SPIN_UP;
                    runningM   += 1;
                }
            }
       }

       //make sure that magnetisation is exactly right
       targetM = int( magnetisation * xSize * ySize );
       while( runningM != targetM ){
              
          do {
              flip=rng.ran2();   
              i = int( flip * xSize );
          }while( i >= xSize );

          do {
              flip=rng.ran2();   
              j = int( flip * ySize );
          }while( j >= ySize );
        
          if ( spins[i][j] == SPIN_DOWN && runningM < targetM ){
                runningM++;
                spins[i][j] = SPIN_UP;
          }      
          else if ( spins[i][j] == SPIN_UP && runningM > targetM ){
                runningM--;
                spins[i][j] = SPIN_DOWN;
          }
          
       }
       fillSpinList();
    }
}

void shi::buildSpinList(){

       if( dynamicsType == SHI_DYNAMICS_KAWASAKI ){
          nUpSpins = int( magnetisation * xSize * ySize );

          upSpinXpos  = new int[nUpSpins];
          upSpinYpos  = new int[nUpSpins];
          upSpinListP = new int[nUpSpins];

          fillSpinList();

       }
       else{ //dynamicsType == SHI_DYNAMICS_ASEP
          nUpSpins    = 0;
          upSpinXpos  = new int[xSize];
          upSpinYpos  = new int[ySize];
          upSpinListP = new int[xSize];
       }


       

}

void shi::fillSpinList(){

       int c = 0;
       for( int i = 0; i < xSize; i++ ){
            for( int j = 0; j < ySize; j++ ) { 
                if( spins[i][j] == SPIN_UP ) {
                    if( c >= nUpSpins ) {
                        c++;
                        break;
                    }
                    upSpinXpos[c] = i;
                    upSpinYpos[c] = j;
                    c++;
                }
            }
       }
}


void  shi::setupFromFile( char *configFileName ){

    ifstream infile;
    char     line[2*SHI_MAX_X_SIZE];
    char     quantity[2*SHI_MAX_X_SIZE];
    char     value[2*SHI_MAX_X_SIZE];
    int      headerCount = 0;
    int      dataLine = 0, i, j;
    int      check;

    iran = &rng.idum;
    readParameterFile();

    //init the cluster list
    clust        = 0;
    haveClusters = false;

    infile.open( configFileName );
    if ( !infile.good() ) { //open and test parameter file
        cerr << "Error startup config file\"" << configFileName << "\" not found\n";
        exit(8);
    }

    //read config file
    while (infile.peek() != EOF) {

        infile.getline(line,sizeof(line));

        //allow whitespace or #-delimited comments
        check = sscanf(line, "%s%s", quantity, value );
        if( check == 0 ) {
            continue;
        }
        if( quantity[0] == '#' ){

            //possible header information 
            switch( headerCount ){
                case SHI_FILE_ENTRY_XSIZE :
                    xSize  = atoi( value );
                    break;
                case SHI_FILE_ENTRY_YSIZE :
                    ySize  = atoi( value );
                    break;
                case SHI_FILE_ENTRY_PBCSHEAR :
                    pbcShear = atoi( value );
                    break;
            }            
            headerCount++;

            //allocate spins
            if( headerCount == SHI_FILE_ENTRY_NUM_ENTRIES ){
                spins = new SPIN_T*[xSize];
                for( i = 0; i < xSize; i++ ){
                    spins[i] = new SPIN_T[ySize];
                }   
            }

            continue;
        }
        if( quantity[0] == '\n' ){
            continue;
        }

        //have data-line read it in so that x-axis reads along the line
        {
            char *pos;

            pos = line;
            j   = 0;
            while( *pos != '\n' && *pos != '\0' ){

                if( *pos == '0' || *pos == '1'){
                    spins[j++][dataLine] = *pos;
                }
                pos++;

            }

            dataLine++;

        }       
    }

    if( dynamicsType ==  SHI_DYNAMICS_KAWASAKI || dynamicsType ==  SHI_DYNAMICS_ASEP ) {
       buildSpinList();
    }

    infile.close();

}
















