// clang-format off
/* ----------------------------------------------------------------------
   LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
   https://www.lammps.org/, Sandia National Laboratories
   Steve Plimpton, sjplimp@sandia.gov

   Copyright (2003) Sandia Corporation.  Under the terms of Contract
   DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
   certain rights in this software.  This software is distributed under
   the GNU General Public License.

   See the README file in the top-level LAMMPS directory.
------------------------------------------------------------------------- */

#include "server_mc.h"
#include "atom.h"
#include "domain.h"
#include "update.h"
#include "integrate.h"
#include "input.h"
#include "output.h"
#include "thermo.h"
#include "error.h"

// CSlib interface

#include "cslib.h"

using namespace LAMMPS_NS;
using namespace CSLIB_NS;

enum{NATOMS=1,EINIT,DISPLACE,ACCEPT,RUN};

/* ---------------------------------------------------------------------- */

ServerMC::ServerMC(LAMMPS *lmp) : Pointers(lmp) {}

/* ---------------------------------------------------------------------- */

void ServerMC::loop()
{
  int m;
  double xold[3];
  tagint atomid;

  CSlib *cs = (CSlib *) lmp->cslib;

  if (domain->box_exist == 0)
    error->all(FLERR,"Server command before simulation box is defined");

  if (atom->map_style == Atom::MAP_NONE) error->all(FLERR,"Server mc requires atom map");
  if (atom->tag_enable == 0) error->all(FLERR,"Server mc requires atom IDs");
  if (sizeof(tagint) != 4) error->all(FLERR,"Server mc requires 32-bit atom IDs");

  // initialize LAMMPS for dynamics

  input->one("run 0");

  // loop on messages
  // receive a message, process it, send return message if necessary

  int msgID,nfield;
  int *fieldID,*fieldtype,*fieldlen;

  while (1) {
    msgID = cs->recv(nfield,fieldID,fieldtype,fieldlen);
    if (msgID < 0) break;

    if (msgID == NATOMS) {

      cs->send(msgID,1);
      cs->pack_int(1,atom->natoms);

    } else if (msgID == EINIT) {

      double dval;
      output->thermo->evaluate_keyword((char *) "pe",&dval);

      cs->send(msgID,2);
      cs->pack_double(1,dval);
      double *coords = nullptr;
      if (atom->nlocal) coords = &atom->x[0][0];
      cs->pack_parallel(2,4,atom->nlocal,atom->tag,3,coords);

    } else if (msgID == DISPLACE) {

      atomid = cs->unpack_int(1);
      double *xnew = (double *) cs->unpack(2);
      double **x = atom->x;

      m = atom->map(atomid);
      if (m >= 0 && m < atom->nlocal) {
        xold[0] = x[m][0];
        xold[1] = x[m][1];
        xold[2] = x[m][2];
        x[m][0] = xnew[0];
        x[m][1] = xnew[1];
        x[m][2] = xnew[2];
      }

      input->one("run 0");
      double dval;
      output->thermo->evaluate_keyword((char *) "pe",&dval);

      cs->send(msgID,1);
      cs->pack_double(1,dval);

    } else if (msgID == ACCEPT) {

      int accept = cs->unpack_int(1);
      double **x = atom->x;

      if (!accept) {
        m = atom->map(atomid);
        if (m >= 0 && m < atom->nlocal) {
          x[m][0] = xold[0];
          x[m][1] = xold[1];
          x[m][2] = xold[2];
        }
      }

      cs->send(msgID,0);

    } else if (msgID == RUN) {

      int nsteps = cs->unpack_int(1);

      update->nsteps = nsteps;
      update->firststep = update->ntimestep;
      update->laststep = update->ntimestep + nsteps;

      update->integrate->setup(1);
      update->integrate->run(nsteps);

      cs->send(msgID,0);

    } else error->all(FLERR,"Server received unrecognized message");
  }

  // final reply to client

  cs->send(0,0);

  // clean up

  delete cs;
  lmp->cslib = nullptr;
}
