//
// meam.cpp
//
// LGPL Version 2.1 HEADER START
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
//
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library 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
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
// MA 02110-1301  USA
//
// LGPL Version 2.1 HEADER END
//

//
// Copyright (c) 2020--2021, Regents of the University of Minnesota.
// All rights reserved.
//
// Contributors:
//    Yaser Afshar
//

#include "meam.hpp"

#include <string>

#include "meam_implementation.hpp"

// The standard interface to KIM Model Drivers

extern "C" {
int ModelDriverCreateRoutine(
    KIM::ModelDriverCreate *const model_driver_create,
    KIM::LengthUnit const requested_length_unit,
    KIM::EnergyUnit const requested_energy_unit,
    KIM::ChargeUnit const requested_charge_unit,
    KIM::TemperatureUnit const requested_temperature_unit,
    KIM::TimeUnit const requested_time_unit) {
  if (!model_driver_create) {
    HELPER_LOG_ERROR("The ModelDriverCreate pointer is not assigned");
    return true;
  }

  int ier;

  // read input files, convert units if needed, compute
  // interpolation coefficients, set cutoff, and publish parameters
  MEAM *const model_object =
      new MEAM(model_driver_create, requested_length_unit,
               requested_energy_unit, requested_charge_unit,
               requested_temperature_unit, requested_time_unit, &ier);
  if (ier) {
    // constructor has already reported the error
    delete model_object;
    return true;
  }

  // register pointer to MEAM object in the KIM object
  model_driver_create->SetModelBufferPointer(static_cast<void *>(model_object));

  // everything is good
  return false;
}
}

// Implementation of MEAM public wrapper functions

MEAM::MEAM(KIM::ModelDriverCreate *const model_driver_create,
           KIM::LengthUnit const requested_length_unit,
           KIM::EnergyUnit const requested_energy_unit,
           KIM::ChargeUnit const requested_charge_unit,
           KIM::TemperatureUnit const requested_temperature_unit,
           KIM::TimeUnit const requested_time_unit, int *const ier)
    : meam_implementation_(new MEAMImplementation(
          model_driver_create, requested_length_unit, requested_energy_unit,
          requested_charge_unit, requested_temperature_unit,
          requested_time_unit, ier)) {}

// static member function
int MEAM::Destroy(KIM::ModelDestroy *const model_destroy) {
  if (!model_destroy) {
    HELPER_LOG_ERROR("The model_destroy pointer is not assigned.\n");
    return true;
  }

  MEAM *model_object = nullptr;

  model_destroy->GetModelBufferPointer(
      reinterpret_cast<void **>(&model_object));

  if (model_object) {
    // delete the object itself
    delete model_object;
  }

  // everything is good
  return false;
}

// static member function
int MEAM::Refresh(KIM::ModelRefresh *const model_refresh) {
  if (!model_refresh) {
    HELPER_LOG_ERROR("The model_refresh pointer is not assigned.\n");
    return true;
  }

  MEAM *model_object = nullptr;

  model_refresh->GetModelBufferPointer(
      reinterpret_cast<void **>(&model_object));

  if (model_object) {
    return model_object->meam_implementation_->Refresh(model_refresh);
  } else {
    std::string msg = "The model_object pointer returned from ";
    msg += "GetModelBufferPointer is not assigned.\n";
    HELPER_LOG_ERROR(msg);
    return true;
  }
}

int MEAM::WriteParameterizedModel(KIM::ModelWriteParameterizedModel const
                                      *const model_write_parameterized_model) {
  if (!model_write_parameterized_model) {
    std::string msg = "The model_write_parameterized_model pointer ";
    msg += "is not assigned.\n";
    HELPER_LOG_ERROR(msg);
    return true;
  }

  MEAM *model_object = nullptr;

  model_write_parameterized_model->GetModelBufferPointer(
      reinterpret_cast<void **>(&model_object));

  if (model_object) {
    return model_object->meam_implementation_->WriteParameterizedModel(
        model_write_parameterized_model);
  } else {
    std::string msg = "The model_object pointer returned from ";
    msg += "GetModelBufferPointer is not assigned.\n";
    HELPER_LOG_ERROR(msg);
    return true;
  }
}

// static member function
int MEAM::Compute(
    KIM::ModelCompute const *const model_compute,
    KIM::ModelComputeArguments const *const model_compute_arguments) {
  if (!model_compute || !model_compute_arguments) {
    if (!model_compute) {
      HELPER_LOG_ERROR("The model_compute pointer is not assigned.\n");
    }
    if (!model_compute_arguments) {
      std::string msg = "The model_compute_arguments pointer ";
      msg += "is not assigned.\n";
      HELPER_LOG_ERROR(msg);
    }
    return true;
  }

  MEAM *model_object = nullptr;

  model_compute->GetModelBufferPointer(
      reinterpret_cast<void **>(&model_object));

  if (model_object) {
    return model_object->meam_implementation_->Compute(model_compute,
                                                       model_compute_arguments);
  } else {
    std::string msg = "The model_object pointer returned from ";
    msg += "GetModelBufferPointer is not assigned.\n";
    HELPER_LOG_ERROR(msg);
    return true;
  }
}

// static member function
int MEAM::ComputeArgumentsCreate(
    KIM::ModelCompute const *const model_compute,
    KIM::ModelComputeArgumentsCreate *const model_compute_arguments_create) {
  if (!model_compute || !model_compute_arguments_create) {
    if (!model_compute) {
      HELPER_LOG_ERROR("The model_compute pointer is not assigned.\n");
    }
    if (!model_compute_arguments_create) {
      std::string msg = "The model_compute_arguments_create pointer ";
      msg += "is not assigned.\n";
      HELPER_LOG_ERROR(msg);
    }
    return true;
  }

  MEAM *model_object = nullptr;

  model_compute->GetModelBufferPointer(
      reinterpret_cast<void **>(&model_object));

  if (model_object) {
    return model_object->meam_implementation_->ComputeArgumentsCreate(
        model_compute_arguments_create);
  } else {
    std::string msg = "The model_object pointer returned from ";
    msg += "GetModelBufferPointer is not assigned.\n";
    HELPER_LOG_ERROR(msg);
    return true;
  }
}

// static member function
int MEAM::ComputeArgumentsDestroy(
    KIM::ModelCompute const *const model_compute,
    KIM::ModelComputeArgumentsDestroy *const model_compute_arguments_destroy) {
  if (!model_compute || !model_compute_arguments_destroy) {
    if (!model_compute) {
      HELPER_LOG_ERROR("The model_compute pointer is not assigned.\n");
    }
    if (!model_compute_arguments_destroy) {
      std::string msg = "The model_compute_arguments_destroy pointer ";
      msg += "is not assigned.\n";
      HELPER_LOG_ERROR(msg);
      return true;
    }
  }

  MEAM *model_object = nullptr;

  model_compute->GetModelBufferPointer(
      reinterpret_cast<void **>(&model_object));

  if (model_object) {
    return model_object->meam_implementation_->ComputeArgumentsDestroy(
        model_compute_arguments_destroy);
  } else {
    std::string msg = "The model_object pointer returned from ";
    msg += "GetModelBufferPointer is not assigned.\n";
    HELPER_LOG_ERROR(msg);
    return true;
  }
}

#undef HELPER_LOG_ERROR
