/*
    DFT++ is a density functional package developed by the research group
    of Professor Tomas Arias

    Copyright 1996-2003 Sohrab Ismail-Beigi

    This file is part of DFT++.

    DFT++ 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 2 of the License, or
    (at your option) any later version.

    DFT++ 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 DFT++; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

    Please see the file CREDITS for a list of authors.

    For academic users, we request that publications using results obtained with
    this software reference

    "New algebraic formulation of density functional calculation," by Sohrab Ismail-Beigi
    and T.A. Arias, Computer Physics Communications 128:1-2, 1-45 (June 2000).

    and, if using the wavelet basis, further reference

    "Multiresolution analysis of electronic structure: semicardinal and wavelet bases,"
    T.A. Arias, Reviews of Modern Physics 71:1, 267-311 (January 1999).

    and 

    "Robust ab initio calculation of condensed matter: transparent convergence through
    semicardinal multiresolution analysis,'' I.P. Daykov, T.A. Arias, and
    Torkel D. Engeness, Physical Review Letters, 90:21, 216402 (May 2003).

    For your convenience, preprints of the above articles may be obtained from
    http://arXiv.org/abs/cond-mat/9909130, 9805262, and 0204411, respectively.
*/

#include "command.h"

int ion_dep[] = {ION_SPECIES,LATTICE,COORDS_TYPE,0};

extern char processing_error[DFT_LINE_LEN];

int process_ion(char *input_line,Everything &e)
{
  char key[DFT_MSG_LEN];
  if (sscanf(input_line,"%*s %s",key)!=1)
    {
      sprintf(processing_error,
	      "ion command must be followed by a name.  Input line:\n%s\n",
	      input_line);
      return 1;
    }
  int sp;
  for (sp=0; sp < e.ioninfo.nspecies; sp++)
    if (MATCH(key,e.ioninfo.species[sp].name))
      {
	real x,y,z;
	int flag;
	if (sscanf(input_line,"%*s %*s %lg %lg %lg %d",&x,&y,&z,&flag)!=4)
	  {
	    sprintf(processing_error,
		    "ion must have x y z and move flag.  Input was:\n%s\n",
		    input_line);
	    return 1;
	  }
	if (flag != 0 && flag != 1)
	  {
	    sprintf(processing_error,
		    "move_flag for ion must be 1 or 0.  %d was input.\n",
		    flag);
	    return 1;
	  }
	int &nat = e.ioninfo.species[sp].natoms;
	nat++;
	Speciesinfo &spinfo = e.ioninfo.species[sp];
	if (nat==1)
	  {
	    spinfo.atpos = (vector3 *)mymalloc(sizeof(vector3)*nat,
					       "atpos[]","process_ion");
	    spinfo.forces = (vector3 *)mymalloc(sizeof(vector3)*nat,
						"forces[]","process_ion");
	    spinfo.old_forces = (vector3 *)mymalloc(sizeof(vector3)*nat,
						"old_forces[]","process_ion");
	    spinfo.ion_vel = (vector3 *)mymalloc(sizeof(vector3)*nat,
						   "ion_vel[]","process_ion");
	    spinfo.move = (int *)mymalloc(sizeof(int)*nat,
					  "move[]","process_ion");
	  }
	else
	  {
	    spinfo.atpos = (vector3 *)myrealloc(spinfo.atpos,
						sizeof(vector3)*nat,
						"atpos[]","process_ion");
	    spinfo.forces = (vector3 *)myrealloc(spinfo.forces,
						 sizeof(vector3)*nat,
						 "forces[]","process_ion");
	    spinfo.old_forces = (vector3 *)myrealloc(spinfo.old_forces,
						 sizeof(vector3)*nat,
						 "old_forces[]","process_ion");
	    spinfo.ion_vel = (vector3 *)myrealloc(spinfo.ion_vel,
						  sizeof(vector3)*nat,
						  "ion_vel[]","process_ion");
	    spinfo.move = (int *)myrealloc(spinfo.move,
					   sizeof(int)*nat,
					   "move[]","process_ion");
	  }
	if(e.ioninfo.coords_type == LATTICE_COORDS){
	  spinfo.atpos[nat-1].v[0] = x;
	  spinfo.atpos[nat-1].v[1] = y;
	  spinfo.atpos[nat-1].v[2] = z;
	} else if(e.ioninfo.coords_type == CARTESIAN_COORDS){
	  vector3 xx(x,y,z);

	  // HACK WARNING
	  //
	  // this is really stupid, but when the program gets here,
	  // the lattice is read in, but the lattice_scale has not been
	  // applied yet, so we need to apply it here.
	  //
	  // when this idiocy with the lattice_scale is sorted out,
	  // this might need to be changed!!!!!
	  matrix3 scaledR(e.lattice.latvec);
	  for (int i=0; i < 3; i++)
	    for (int j=0; j < 3; j++)
	      scaledR.m[j][i] *= e.cntrl.lattscale[i];


	  spinfo.atpos[nat-1] = inv3(scaledR)*xx;
	} else {
	  die("Invalid CoordsType in process_ion()\n");
	}

	spinfo.move[nat-1] = flag;
	return 0;
      }
  sprintf(processing_error,
	  "Species %s was not found.  Input line:\n%s\n",key,input_line);
  return 1;
}

void ion_print_status(Everything &e,Output *log)
{
  int sp,i;
  vector3 xx;
  for (sp=0; sp < e.ioninfo.nspecies; sp++)
    for (i=0; i < e.ioninfo.species[sp].natoms; i++){
      if(e.ioninfo.coords_type == LATTICE_COORDS){
	xx = e.ioninfo.species[sp].atpos[i];
      } else if(e.ioninfo.coords_type == CARTESIAN_COORDS){
	xx = e.lattice.latvec * e.ioninfo.species[sp].atpos[i];
      }
      log->printf("ion %s %18.14lf %18.14lf %18.14lf %d\n",
		  e.ioninfo.species[sp].name,
		  xx.v[0],
		  xx.v[1],
		  xx.v[2],
		  e.ioninfo.species[sp].move[i]);
    }
}

command* setup_ion(command *next)
{
  command* cmnd = alloc_command();

  cmnd->number = ION;
  cmnd->name = "ion";
  cmnd->format = "ion <id> <x1> <x2> <x3> <move_flag>";
  cmnd->comments = "# Specifies that ion type id at x y z.\n# move_flag is 1 | 0 and specifies whether the ion can be moved";
  cmnd->dependencies = ion_dep;
  cmnd->process_command = process_ion;
  cmnd->print_status = ion_print_status;
  cmnd->allow_multiple = 1;
  cmnd->next_command = next;

  return cmnd;
}
