/*-------------------------------deform.c-------------------------------
  This program simulates the behavior of hydrocarbon molecules being
  acted on by moving objects in the quasi-static limit. That is, at each
  stage, the external objects are moved a small amount and the
  hydrocarbon molecule is relaxed to its equilibrium configuration
  relative to the forces exerted by the external objects. The internal
  forces exerted by the atoms of the hydrocarbon molecule on each other
  are calculated using a Tersoff-Brener potential defined in potential.c.
  The external objects are modeled as continuum Lenard-Jones graphene in
  force.c. The relaxation is done using the conjugate gradient method
  in calculate.c. The parameters of the simulation must be given in
  a "definitions" file, which is a plaintext format with a .def
  extension. This software was written by Stephen Jordan.
  ---------------------------------------------------------------------*/

#include "defs.h"                //for loaddefs and logdefs
#include "cell.h"                //for Cell  
#include "abcio.h"               //for loadabc saveabc and destroy_cell
#include "calculate.h"           //for calculate
#include "diags.h"               //for diags
#include "potential.h"           //for energy
#include "force.h"               //for tipforce
#include <stdio.h>               //for sprintf, fopen, FILE
#include <stdlib.h>              //for EXIT_SUCCESS and EXIT_FAILURE
#include <string.h>              //for strlen
#include <time.h>                //for clock


//parses the commandline args and returns 1 on success 0 on failure
int parse_args(int argc, char *argv[], char *defname, defs *d) {
  int parsed, loaded;
  if(argc != 2 && argc != 3) {
    printf("Usage: deform [-v] file.def\n");
    return 0;
  }
  parsed = 0;
  if(argc == 2) { //not verbose
    d->verbose = 0;
    strncpy(defname, argv[1], 127);
  }
  else { //argc == 3, verbose
    if(argv[2][0] == '-') { //the second argument is the switch
      if(argv[2][1] == 'v') d->verbose = 1;
      else {
        printf("Invalid switch %s\n", argv[2]);
        return 0;
      }
      strncpy(defname, argv[1], 127);
      parsed = 1;
    }
    if(argv[1][0] == '-') { //the first argument is the switch
      if(argv[1][1] == 'v') d->verbose = 1;
      else {
        printf("Invalid switch %s\n", argv[1]);
        return 0;
      }
      strncpy(defname, argv[2], 127);
      parsed = 1;
    }
    if(parsed == 0) {
      printf("Usage: deform [-v] file.def\n");
      return 0;
    }
  }
  return 1;
}  



//add random perturbations to the atomic coordinates
//the maximum perturbation is 0.01 Angstrom
void perturb(cell *C) {
  int i;
  double numerator;
  double denominator;
  for(i = 0; i < 3*C->atoms; i++) {
    numerator = (double)rand() - (double)RAND_MAX/2.0;
    denominator = (double)50.0*RAND_MAX;
    C->x[i] += numerator/denominator;
  }
}


void mk_stagename(int stage, char *outfile, char *stagename) {
  char prefix[16];
  char stage_str[16];
  int zeros, i;
  sprintf(stage_str, "%i", stage);
  zeros = 4 - strlen(stage_str); //hence 0000_whatever.abc etc.
  stagename[0] = '\0';
  for(i = 0; i < zeros; i++) strcat(stagename, "0");
  strcat(stagename, stage_str);
  strcat(stagename, "_");
  strcat(stagename, outfile);
}

//strips the path and extension off of a filename
void truncate(char *name) {
  char *temp;
  char *ptr;
  temp = strrchr(name, '/');
  if(temp != NULL) strcpy(name, temp + 1);
  ptr = strchr(name, '.');
  ptr[0] = '\0';
}


int main(int argc, char *argv[]) {
  FILE *deffile, *infile, *stagefile;
  char stagename[128];
  char defname[128];
  char msg[256];
  char header[65536];
  double *init_bnd_nrg;
  char jobname[256];
  cell C;
  int loaded;
  int stage, i;
  defs d;
  diags dia;  
  time_t starttime;
  char timsg[256];
  int size = 0;
  starttime = time(NULL);
  if(starttime == (time_t)-1) printf("Unable to read system clock.\n");
  loaded = parse_args(argc, argv, defname, &d);
  if(loaded == 0) return EXIT_FAILURE;
  deffile = fopen(defname, "r");
  if(deffile == NULL) {
    printf("Definitions file not found.\n");
    return 0;
  }
  strcpy(jobname, defname);
  truncate(jobname);
  loaded = loaddefs(deffile, &d, jobname);
  if(loaded == 0) {
    printf("Error processing %s\n", deffile);
    return 0;
  }
  //this loop is so that multiple jobs can be scripted in a single .def
  while(loaded) {
    logdefs(&d);
    if(strcmp(d.infile, "LAST")) {
      infile = fopen(d.infile, "r");
      if(infile == NULL) {
        sprintf(msg, "ABC file %s not found.\n", d.infile);
        msglog(msg, &d);
        return EXIT_FAILURE;
      }
      loadabc(infile, &C, d.automatic);    //load structure + allocate RAM
      tbinit(&C, d.automatic);             //prepare bond_array
      if(d.color == 2) {
        if(size) free(init_bnd_nrg);
        init_bnd_nrg = malloc(C.bonds*sizeof(double));
        if(init_bnd_nrg == NULL) {
          sprintf(msg, "Unable to allocate init_bnd_nrg\n");
          msglog(msg, &d);
          return EXIT_FAILURE;
        }
        size = C.bonds;
        prepare(C.bond_array, C.bonds);
        for(i = 0; i < C.bonds; i++) init_bnd_nrg[i] = C.bond_array[i].E;
      }
      sprintf(msg, "atoms          %i\n", C.atoms);
      msglog(msg, &d);
      cm(&C, &dia, &d);                    //calculate the center of mass
      center(&C, &dia);                    //set cm x and y to zero
    }
    if(d.randomization) srand(d.randomization);
    for(stage = d.start; stage <= d.stop; stage++) {
      dia.max_f = 0;                                    //initialize
      for(i = 0; i < d.f_num; i++) dia.s_max_f[i] = 0;  //initialize
      if(d.randomization != 0) perturb(&C);
      calculate(&C, &d, &dia, stage+d.offset);    //integrate equations of motion
      dia.tb_energy = energy(C.bond_array, C.bonds); //Tersoff-Brenner energy
      energy_tally(&C, &d, &dia, stage+d.offset); //calculate the other energies
      tipforce(&C, &d, &dia, stage+d.offset);     //calculate the net force of the tip
      cm(&C, &dia, &d);                  //calculate the center of mass
      sprintf(msg, "stage, offset  %i\t%i\n", stage, d.offset);
      msglog(msg, &d);
      logdiags(&dia, &d);                 //log the diagnostics for current stage
      datalog(&dia, &d, stage+d.offset);  //log data from the simulation
      mk_stagename(stage, d.outfile, stagename);
      stagefile = fopen(stagename, "w");
      if(stagefile == NULL) {
        sprintf(msg, "Unable to create %s.\n", stagename);
        msglog(msg, &d);
        return EXIT_FAILURE;
      }
      makeheader(header, &d, &dia, stage);
      //save molecule:
      if(d.color == 2) saverel(stagefile, &C, header, d.digits, init_bnd_nrg);
      else saveabc(stagefile, &C, header, d.digits, d.color);
      fclose(stagefile);
    }
    loaded = loaddefs(deffile, &d, jobname); //if multiple calculations are scripted
    if(strcmp(d.infile, "LAST")) {
      fclose(infile);            
      destroy_cell(&C);                  //de-allocate memory
    }
  }
  if(size) free(init_bnd_nrg);
  sprintf(timsg, "Total execution time: %.0f seconds\n",
    difftime(time(NULL), starttime));
  msglog(timsg, &d);
  fclose(deffile);
  return EXIT_SUCCESS;
}







