// -*- C++ -*-
//
// asap_emt_driver.cpp: OpenKIM Model Driver interface for EMT.
//
// Copyright (C) 2012-2013 Jakob Schiotz and the Department of Physics,
// Technical University of Denmark.  Email: schiotz@fysik.dtu.dk
//
// This file is part of Asap version 3.
// Asap is released under the GNU Lesser Public License (LGPL) version 3.
// However, the parts of Asap distributed within the OpenKIM project
// (including this file) are also released under the Common Development
// and Distribution License (CDDL) version 1.0.
//
// This program is free software: you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public License
// version 3 as published by the Free Software Foundation.  Permission
// to use other versions of the GNU Lesser General Public License may
// granted by Jakob Schiotz or the head of department of the
// Department of Physics, Technical University of Denmark, as
// described in section 14 of the GNU General Public License.
//
// This program 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
// and the GNU Lesser Public License along with this program.  If not,
// see <http://www.gnu.org/licenses/>.

#include "KIM_ModelDriverHeaders.hpp"
#include "KIM_LogMacros.hpp"
#include "Asap.h"
#include "Debug.h"
#include "asap_kim_api.h"
#include "asap_emt_driver.h"
#include "KimParameterProvider.h"

#include <stdlib.h>

static int asap_emt_driver_initmodel(AsapKimPotential *model);

KimEMT::KimEMT(AsapKimPotential *owner, EMTParameterProvider *provider) : EMT(NULL, NULL, 0)
{
  CONSTRUCTOR;
  this->owner = owner;
  nblist = NULL;
  nblist_obj = NULL;
  provider_obj = NULL;  // Bypass EMT's Python-based memory management.
  this->provider = provider;
  always_fullnblist = true;
  modelWillNotRequestNeighborsOfNoncontributingParticles = 1;
}

KimEMT::~KimEMT()
{
  DESTRUCTOR;
  assert(provider_obj == NULL);
  delete provider;
  if (nblist != NULL)
    delete nblist;  // Not deleted in the real potential.
}

void KimEMT::CreateNeighborList()
{
  PyAsap_NeighborLocatorObject *nbl = owner->CreateNeighborList(atoms, rNbCut, driftfactor);
  nblist = nbl->cobj;
  nblist_obj = (PyObject *) nbl;
  nblist->UpdateNeighborList();
}

int KimEMT::ComputeArgumentsCreate(KIM::ModelComputeArgumentsCreate * const modelComputeArgumentsCreate) const
{
  int error =
    modelComputeArgumentsCreate->SetArgumentSupportStatus(
      KIM::COMPUTE_ARGUMENT_NAME::partialEnergy,
      KIM::SUPPORT_STATUS::optional)
    || modelComputeArgumentsCreate->SetArgumentSupportStatus(
      KIM::COMPUTE_ARGUMENT_NAME::partialForces,
      KIM::SUPPORT_STATUS::optional)
    || modelComputeArgumentsCreate->SetArgumentSupportStatus(
      KIM::COMPUTE_ARGUMENT_NAME::partialParticleEnergy,
      KIM::SUPPORT_STATUS::optional)
    || modelComputeArgumentsCreate->SetArgumentSupportStatus(
      KIM::COMPUTE_ARGUMENT_NAME::partialVirial,
      KIM::SUPPORT_STATUS::optional)
    || modelComputeArgumentsCreate->SetArgumentSupportStatus(
      KIM::COMPUTE_ARGUMENT_NAME::partialParticleVirial,
      KIM::SUPPORT_STATUS::optional);

  // No callbacks are set up for this potential.

#if 0
  // Debugging info
  std::cerr << "*** COMPUTE ARGUMENTS ***" << std::endl;
  std::cerr << modelComputeArgumentsCreate->ToString() << std::endl;
#endif
  return error;
}


// /* Reinit function */

// static int asap_emt_reinit(void *km)
// {
//   intptr_t* pkim = *((intptr_t**) km);
//   int ier;
//   // Remove the old model
//   AsapKimPotential *model = (AsapKimPotential *) KIM_API_get_model_buffer(pkim, &ier);
//   if (KIM_STATUS_OK > ier)
//     {
//       KIM_API_report_error(__LINE__, __FILE__, "KIM_API_get_model_buffer", ier);
//       return ier;
//     }

//   // Add the new model
//   AsapKimPotential *newmodel = new AsapKimPotential(pkim, model->paramfile_names, model->nmstrlen,
//       model->numparamfiles, true);
//   delete model;
//   return asap_emt_driver_initmodel(newmodel);
// }

/* Initialization function */
#define KIM_LOGGER_OBJECT_NAME modelDriverCreate
extern "C" int model_driver_create(KIM::ModelDriverCreate * const modelDriverCreate,
                        KIM::LengthUnit const requestedLengthUnit,
                        KIM::EnergyUnit const requestedEnergyUnit,
                        KIM::ChargeUnit const requestedChargeUnit,
                        KIM::TemperatureUnit const requestedTemperatureUnit,
                        KIM::TimeUnit const requestedTimeUnit)
{

  // Create the model
  AsapKimPotential *model = NULL;
  KimParameterProvider *provider = NULL;
  try
  {
    model = new AsapKimPotential(modelDriverCreate, true);
    provider = new KimParameterProvider(modelDriverCreate,
					model->paramfile_names,
					requestedLengthUnit,
					requestedEnergyUnit,
					requestedChargeUnit,
					requestedTemperatureUnit,
					requestedTimeUnit);
  }
  catch (AsapError &e)
  {
      std::cerr << e.GetMessage() << std::endl;
      LOG_ERROR(e.GetMessage().c_str());
      return true;
  }

  KimEMT *kimpotential = new KimEMT(model, provider); 
  model->SetPotential(kimpotential);


  modelDriverCreate->SetModelBufferPointer(static_cast<void *>(model));
  
  provider->CalcGammaEtc();
  kimpotential->influenceDistance = provider->GetListCutoffDistance();
  modelDriverCreate->SetInfluenceDistancePointer(&(kimpotential->influenceDistance));
  modelDriverCreate->SetNeighborListPointers(
      1,
      &(kimpotential->influenceDistance),
      &(kimpotential->modelWillNotRequestNeighborsOfNoncontributingParticles));

#if 0
  std::cerr << "*** MODEL CREATION PRINTOUT ***" << std::endl;
  std::cerr << modelDriverCreate->ToString()<< std::endl;
  std::cerr << "*** MODEL CREATION PRINTOUT DONE ***" << std::endl;
#endif
  return 0;  // No error
}

