/*-----------------------------tbinit.c------------------------------
  This file contains routines for initializing the cell data
  structure used for the evaluation of the Tersoff-Brenner potential.
  This software was written by Stephen Jordan.
  -------------------------------------------------------------------*/

#include "tbinit.h"
#include "cell.h"
#include "constants.h"
#include "potential.h"
#include "abcio.h"     //for find_neighb_i
#include "defs.h"
#include <stdio.h>
#include <math.h>
#include <malloc.h>


void find_bond(cell *C, int bond_max, int atom, int b) {
  int b2 = 0;
  int found = 0;
  do {
    if(C->bond_array[b2].bond_atom[0] == &C->atom_array[atom] ||
       C->bond_array[b2].bond_atom[1] == &C->atom_array[atom]) {
      C->atom_array[atom].neighb_b[b] = &C->bond_array[b2];
      found = 1;
    }
    b2++;
  }while(b2 <= bond_max && found == 0);
}


void find_neighb_a_i(cell *C, bond *b, int i) {
  int j,k;
  atom *atom1;
  atom *atom2;
  atom *atom3;
  if(i == 1) j = 0;
  if(i == 0) j = 1;
  atom1 = b->bond_atom[i];
  atom2 = b->bond_atom[j];
  b->neighbs[i] = 0;
  for(k = 0; k < atom1->neighbs; k++) {
    atom3 = &C->atom_array[atom1->neighb_i[k]];
    if(atom3 != atom2) {
      b->neighb_a[i][b->neighbs[i]] = atom3;
      b->neighbs[i]++;
    }
  }
}


void merge_atom_list(bond *b) {
  int i, j, k;
  int already;
  k = 0;
  for(i = 0; i < b->neighbs[0]; i++) {
    b->neighb_a[2][k] = b->neighb_a[0][i];
    k++;
  }
  b->neighbs[2] = k;
  for(i = 0; i < b->neighbs[1]; i++) {
    already = 0;
    for(j = 0; j < b->neighbs[2] && already == 0; j++)
      if(b->neighb_a[1][i] == b->neighb_a[2][j]) already = 1;
    if(already == 0) {
      b->neighb_a[2][k] = b->neighb_a[1][i];
      k++;
    }
  }
  b->neighbs[2] = k;
}


void merge_bond_list(bond *b) {
  int i, j, k;
  int already;
  k = 0;
  for(i = 0; i < b->neighbs[0]; i++) {
    b->neighb_b[2][k] = b->neighb_b[0][i];
    k++;
  }
  b->neighbs[2] = k;
  for(i = 0; i < b->neighbs[1]; i++) {
    already = 0;
    for(j = 0; j < b->neighbs[2] && already == 0; j++)
      if(b->neighb_b[1][i] == b->neighb_b[2][j]) already = 1;
    if(already == 0) {
      b->neighb_b[2][k] = b->neighb_b[1][i];
      k++;
    }
  }
  b->neighbs[2] = k;
}


void find_neighb_a(cell *C) {
  int bond_num;
  for(bond_num = 0; bond_num < C->bonds; bond_num++) {
    find_neighb_a_i(C, &C->bond_array[bond_num], 0);
    find_neighb_a_i(C, &C->bond_array[bond_num], 1);
    merge_atom_list(&C->bond_array[bond_num]);
  }
}


void find_bond_atoms(cell *C) {
  int atom1, atom2;
  int b;
  int bond_num = 0;
  for(atom1 = 0; atom1 < C->atoms; atom1++) {
    for(b = 0; b < C->atom_array[atom1].neighbs; b++) {
      atom2 = C->atom_array[atom1].neighb_i[b];
      if(atom1 > atom2) {
        C->bond_array[bond_num].bond_atom[0] = &C->atom_array[atom1];
        C->bond_array[bond_num].bond_atom[1] = &C->atom_array[atom2];
        bond_num++;
      }
    }
  }
}
  

void find_a_neighb_b(cell *C) {
  int i, j, k;
  int success;
  for(i = 0; i < C->atoms; i++) {
    for(j = 0; j < C->atom_array[i].neighbs; j++) {
      success = 0;
      for(k = 0; k < C->bonds && success == 0; k++) {
        if((C->bond_array[k].bond_atom[0] == &C->atom_array[i] &&
	    C->bond_array[k].bond_atom[1] == &C->atom_array[C->atom_array[i].neighb_i[j]]) ||
           (C->bond_array[k].bond_atom[1] == &C->atom_array[i] &&
            C->bond_array[k].bond_atom[0] == &C->atom_array[C->atom_array[i].neighb_i[j]])) {
          C->atom_array[i].neighb_b[j] = &C->bond_array[k];
          success = 1;
        }
      }
    }
  }
}
  

void find_b_neighb_b_e(cell *C, bond *b, int e) {
  int j, k;
  atom *atm;
  k = 0;
  atm = b->bond_atom[e];
  for(j = 0; j < C->bonds; j++) {
    if((C->bond_array[j].bond_atom[0] == atm  ||
        C->bond_array[j].bond_atom[1] == atm) &&
        &C->bond_array[j] != b) {
      b->neighb_b[e][k] = &C->bond_array[j];
      k++;
    }
  }
}

void find_b_neighb_b(cell *C) {
  int bond_num;
  for(bond_num = 0; bond_num < C->bonds; bond_num++) {
    find_b_neighb_b_e(C, &C->bond_array[bond_num], 0);
    find_b_neighb_b_e(C, &C->bond_array[bond_num], 1);
    merge_bond_list(&C->bond_array[bond_num]);
  }
}

void find_2nd_b(cell *C) {
  int i, b1, b2, n;
  int a, b;
  atom *atom1;
  atom *atom2;
  atom *atom3;
  for(i = 0; i < C->atoms; i++) {
    atom1 = &C->atom_array[i];   
    n = 0;
    for(b1 = 0; b1 < atom1->neighbs; b1++) {
      atom2 = &C->atom_array[atom1->neighb_i[b1]];
      atom1->neighb_b2[n] = atom1->neighb_b[b1];
      n++;
      for(b2 = 0; b2 < atom2->neighbs; b2++) {
        atom3 = &C->atom_array[atom2->neighb_i[b2]];
        if(atom3 != atom1) {
          atom1->neighb_b2[n] = atom2->neighb_b[b2];
          n++;
        }
      }
    }
    C->atom_array[i].neighbs2 = n;
    for(b1 = 0; b1 < C->atom_array[i].neighbs2; b1++) {
      a = C->atom_array[i].neighb_b2[b1]->bond_atom[0]-C->atom_array;
      b = C->atom_array[i].neighb_b2[b1]->bond_atom[1]-C->atom_array;
    }
  }
}

//finds the nearest neighbors
//this is an N^2 algorithm to be used sparingly
void find_neighbs(cell *C, int automatic) {
  if(automatic == 1) find_neighb_i(C);
  if(C->allocated == 1) free(C->bond_array);
  C->bond_array = (bond *)malloc(C->bonds*sizeof(bond));
  if(C->bond_array == NULL) printf("Unable to allocate bonds!\n");
  C->allocated = 1;
  find_bond_atoms(C);
  find_neighb_a(C);
  find_a_neighb_b(C);
  find_b_neighb_b(C);
  find_2nd_b(C);
}

void tbinit(cell *C, int automatic) {
  int b;
  find_neighbs(C, automatic);
  //initialize everything for the potential
  constants(C->bond_array, C->bonds);
  for(b = 0; b < C->bonds; b++) geometry(&C->bond_array[b]);
  for(b = 0; b < C->bonds; b++) C->bond_array[b].fc = 0;
  for(b = 0; b < C->bonds; b++) fc(&C->bond_array[b]);
  reconnect(C->bond_array, C->bonds);
}

//for debugging
void disp_i(const cell *C) {
  int atm;
  int bnd;
  for(atm = 0; atm < C->atoms; atm++) {
    printf("C->atom_array[%i].neighbs = %i\n", atm, C->atom_array[atm].neighbs);
    for(bnd = 0; bnd < C->atom_array[atm].neighbs; bnd++) {
      printf("C->atom_array[%i].neighb_i[%i] = %i\n", atm, bnd,
	     C->atom_array[atm].neighb_i[bnd]);
    }
  }
}
