/*----------------------------abcio.c---------------------------
  loadabc() loads an abc file into the "cell" memory structure.
  saveabc() saves the "cell" memory structure to an abc file.
  This software was written by Stephen Jordan.
  --------------------------------------------------------------*/

#include "cell.h"
#include "abcio.h"
#include "constants.h" //for Dmax and constants()
#include <malloc.h>
#include <stdio.h>
#include <math.h>
#include <string.h>

//it is assumed that no atom is bonded to more than 5 others
void loadabc(FILE *fp, cell *C, int automatic) {
  char line[128];
  char linetype[32];
  char type[3];
  char *returnptr;
  int success;
  int num;
  int src, dst;
  int b;
  double x, y, z;
  C->atoms = 0;
  C->bonds = 0;
  C->allocated = 0;
  do {
    returnptr = fgets(line, sizeof(line), fp);
    success = sscanf(line, "%31s", linetype);
    if(strcmp(linetype, "ATOM") == 0) C->atoms++;
  }while(success == 1 && returnptr != NULL);
  C->atom_array = (atom *)malloc(C->atoms*sizeof(atom));
  if(C->atom_array == NULL) printf("Unable to allocate atom array!\n");
  C->x    = (double *)malloc(3*C->atoms*sizeof(double));
  if(C->x == NULL) printf("Unable to allocate coordinates!\n");
  C->f_tb = (double *)malloc(3*C->atoms*sizeof(double));
  if(C->f_tb == NULL) printf("Unable to allocate Tersoff-Brenner forces!\n");
  C->f_x  = (double *)malloc(3*C->atoms*sizeof(double));
  if(C->f_x == NULL) printf("Unable to allocate external forces!\n");
  for(num = 0; num < C->atoms; num++) {
    C->atom_array[num].x = C->x + 3*num;
    C->atom_array[num].f_x = C->f_x + 3*num;
    C->atom_array[num].f_tb = C->f_tb + 3*num;
    C->atom_array[num].neighbs = 0;
  }
  fseek(fp, 0L, SEEK_SET);                     //go to the beginning of the file
  do {
    returnptr = fgets(line, sizeof(line), fp);
    success = sscanf(line, "%31s", linetype);
    if(strcmp(linetype, "ATOM") == 0 && returnptr) {
      sscanf(line, "%*s %i %s %lf %lf %lf", &num, type, &x, &y, &z);
      C->atom_array[num].x[0] = x;
      C->atom_array[num].x[1] = y;
      C->atom_array[num].x[2] = z;
      if(type[0] == 'H') C->atom_array[num].type = 2; //hydrogen
      if(type[0] == 'C') C->atom_array[num].type = 1; //carbon
    }
    if(strcmp(linetype, "BOND") == 0 && automatic == 0 && returnptr) {
      sscanf(line,"%*s %i %i", &src, &dst);
      C->atom_array[src].neighb_i[C->atom_array[src].neighbs] = dst;
      C->atom_array[src].neighbs++;
      C->atom_array[dst].neighb_i[C->atom_array[dst].neighbs] = src;
      C->atom_array[dst].neighbs++;
      C->bonds++;
    }
  }while(success && returnptr);
}


void destroy_cell(cell *C) {
  free(C->atom_array);
  free(C->x);
  free(C->f_tb);
  free(C->f_x);
  if(C->allocated == 1) free(C->bond_array);
  C->bonds = 0;
  C->atoms = 0;
  C->allocated = 0;
}


char *element(int num) {
  if(num == 2) return "H";
  if(num == 1) return "C";
  return "?";
}


void formatstring(int digits, int color, char *fstring) {
  char digitstring[3];
  int i;
  sprintf(digitstring, "%i", digits);
  strcpy(fstring, "ATOM\t%i\t%s");
  for(i = 0; i < 3; i++) {
    strcat(fstring, "\t%");
    strcat(fstring, digitstring);
    strcat(fstring, "f");
  }
  if(color == 1) strcat(fstring, "\t%e");
  strcat(fstring, "\n");
}


void saveabc(FILE *fp, const cell *C, char *header, int digits, int color) {
  int atom_num;
  int neighb_num;
  int other;
  char format[256];
  fprintf(fp, header);
  if(color == 1) {
    formatstring(digits, 1, format);
    for(atom_num = 0; atom_num < C->atoms; atom_num++) {
      fprintf(fp, format, atom_num, element(C->atom_array[atom_num].type),
              C->atom_array[atom_num].x[0],
              C->atom_array[atom_num].x[1],
              C->atom_array[atom_num].x[2],
              sqrt(C->atom_array[atom_num].f_x[0]*
                   C->atom_array[atom_num].f_x[0]+
                   C->atom_array[atom_num].f_x[1]*
                   C->atom_array[atom_num].f_x[1]+
                   C->atom_array[atom_num].f_x[2]*
                   C->atom_array[atom_num].f_x[2]));
    }
  }
  else {
    formatstring(digits, 0, format);
    for(atom_num = 0; atom_num < C->atoms; atom_num++) {
      fprintf(fp, format, atom_num, element(C->atom_array[atom_num].type),
              C->atom_array[atom_num].x[0],
              C->atom_array[atom_num].x[1],
	      C->atom_array[atom_num].x[2]);
    }
  }
  for(atom_num = 0; atom_num < C->atoms; atom_num++) {
    for(neighb_num = 0; neighb_num < C->atom_array[atom_num].neighbs; neighb_num++) {
      other = C->atom_array[atom_num].neighb_i[neighb_num]; 
      if(other > atom_num) {
        fprintf(fp, "BOND\t%i\t%i", atom_num, other);
        if(color == 1) fprintf(fp, "\t%e",
		       C->atom_array[atom_num].neighb_b[neighb_num]->E);
        fprintf(fp, "\n");
      }
    } 
  }    
}


void saverel(FILE *fp, const cell *C, char *header, int digits, double *init_bnd_nrg) {
  int atom_num;
  int neighb_num;
  int other;
  char format[256];
  fprintf(fp, header);
  formatstring(digits, 1, format);
  for(atom_num = 0; atom_num < C->atoms; atom_num++) {
    fprintf(fp, format, atom_num, element(C->atom_array[atom_num].type),
            C->atom_array[atom_num].x[0],
            C->atom_array[atom_num].x[1],
            C->atom_array[atom_num].x[2],
            sqrt(C->atom_array[atom_num].f_x[0]*
                 C->atom_array[atom_num].f_x[0]+
                 C->atom_array[atom_num].f_x[1]*
                 C->atom_array[atom_num].f_x[1]+
                 C->atom_array[atom_num].f_x[2]*
                 C->atom_array[atom_num].f_x[2]));
  }
  for(atom_num = 0; atom_num < C->atoms; atom_num++) {
    for(neighb_num = 0; neighb_num < C->atom_array[atom_num].neighbs; neighb_num++) {
      other = C->atom_array[atom_num].neighb_i[neighb_num]; 
      if(other > atom_num) {
        fprintf(fp, "BOND\t%i\t%i", atom_num, other);
        fprintf(fp, "\t%e", C->atom_array[atom_num].neighb_b[neighb_num]->E -
               init_bnd_nrg[C->atom_array[atom_num].neighb_b[neighb_num]-C->bond_array]);
        fprintf(fp, "\n");
      }
    } 
  }    
}

double dist(cell *C, int i, int j) {
  double sum, dx;
  int q;
  sum = 0;
  for(q = 0; q < 3; q++) {
    dx = C->atom_array[i].x[q]-C->atom_array[j].x[q];
    sum += dx*dx;
  }
  return sqrt(sum);
}

void find_neighb_i(cell *C) {
  int atom1, atom2;
  double sum, dmax;
  C->bonds = 0;
  for(atom1 = 0; atom1 < C->atoms; atom1++) {
    C->atom_array[atom1].neighbs = 0;
    for(atom2 = 0; atom2 < C->atoms; atom2++) {
      dmax = Dmax(C->atom_array[atom1].type, C->atom_array[atom2].type);
      if(dist(C, atom1, atom2) < dmax) {
        if(atom1 < atom2) C->bonds++; 
        if(atom1 != atom2) {
          C->atom_array[atom1].neighb_i[C->atom_array[atom1].neighbs] = atom2;
          C->atom_array[atom1].neighbs++;
        }
      }
    }
  }
}

