// -*- C++ -*-
// KimParameterProvider.cpp:  Get parameters from a string (for OpenKIM).
//
// 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 "KimParameterProvider.h"
#include "Asap.h"
#include "Exception.h"
// #define ASAPDEBUG
#include "Debug.h"
#include <iostream>
#include <fstream>
#include <cmath>
#include <set>
#include <stdlib.h>

KimParameterProvider::KimParameterProvider(KIM::ModelDriverCreate *const modelDriverCreate,
					   std::vector<std::string> &parameter_filenames,
					   KIM::LengthUnit const requestedLengthUnit,
					   KIM::EnergyUnit const requestedEnergyUnit,
					   KIM::ChargeUnit const requestedChargeUnit,
					   KIM::TemperatureUnit const requestedTemperatureUnit,
					   KIM::TimeUnit const requestedTimeUnit)
{
  DEBUGPRINT;
  int error;

  // Register units
  error = modelDriverCreate->SetUnits(requestedLengthUnit,
				      requestedEnergyUnit,
				      KIM::CHARGE_UNIT::unused,
				      KIM::TEMPERATURE_UNIT::unused,
				      KIM::TIME_UNIT::unused);
  if (error)
    throw AsapError("Unable to set units to requested values");
  
  // define default base units
  KIM::LengthUnit fromLength = KIM::LENGTH_UNIT::A;
  KIM::EnergyUnit fromEnergy = KIM::ENERGY_UNIT::eV;
  KIM::ChargeUnit fromCharge = KIM::CHARGE_UNIT::e;
  KIM::TemperatureUnit fromTemperature = KIM::TEMPERATURE_UNIT::K;
  KIM::TimeUnit fromTime = KIM::TIME_UNIT::ps;
  
  // Prepare to convert all parameters to other units.
  double energy_conv = 1.0;
  error = modelDriverCreate->ConvertUnit(fromLength,
					 fromEnergy,
					 fromCharge,
					 fromTemperature,
					 fromTime,
					 requestedLengthUnit,
					 requestedEnergyUnit,
					 requestedChargeUnit,
					 requestedTemperatureUnit,
					 requestedTimeUnit,
					 0.0,
					 1.0,
					 0.0,
					 0.0,
					 0.0,
					 &energy_conv);
  if (error)
    throw AsapError("Unable to convert energy unit");

  double len_conv = 1.0;
  error = modelDriverCreate->ConvertUnit(fromLength,
					 fromEnergy,
					 fromCharge,
					 fromTemperature,
					 fromTime,
					 requestedLengthUnit,
					 requestedEnergyUnit,
					 requestedChargeUnit,
					 requestedTemperatureUnit,
					 requestedTimeUnit,
					 1.0,
					 0.0,
					 0.0,
					 0.0,
					 0.0,
					 &len_conv);
  if (error)
    throw AsapError("Unable to convert length unit");

  double inv_len_conv = 1.0;
  error = modelDriverCreate->ConvertUnit(fromLength,
					 fromEnergy,
					 fromCharge,
					 fromTemperature,
					 fromTime,
					 requestedLengthUnit,
					 requestedEnergyUnit,
					 requestedChargeUnit,
					 requestedTemperatureUnit,
					 requestedTimeUnit,
					 -1.0,
					 0.0,
					 0.0,
					 0.0,
					 0.0,
					 &inv_len_conv);
  if (error)
    throw AsapError("Unable to convert inverse length unit");

  double inv_vol_conv = 1.0;
  error = modelDriverCreate->ConvertUnit(fromLength,
					 fromEnergy,
					 fromCharge,
					 fromTemperature,
					 fromTime,
					 requestedLengthUnit,
					 requestedEnergyUnit,
					 requestedChargeUnit,
					 requestedTemperatureUnit,
					 requestedTimeUnit,
					 -3.0,
					 0.0,
					 0.0,
					 0.0,
					 0.0,
					 &inv_vol_conv);
  if (error)
    throw AsapError("Unable to convert inverse length unit");

  // Read the parameter file.
  if (parameter_filenames.size() != 1)
    throw AsapError("Expected a single parameter file, got ") << parameter_filenames.size();
  std::ifstream pstr(parameter_filenames[0].c_str());
  int numread = -1;
  emt_parameters *p = NULL;
  while(true)
    {
      std::string word;
      double value;
      pstr >> word >> value;
      //std::cerr << "read(" << word << ")(" << value << ")" << std::endl;
      numread++;
      if (word == "NEWELEMENT")
        {
          std::string name;
          pstr >> name;
          numread = 0;
          p = new emt_parameters;
          p->Z = (int) value;
          p->gamma1 = 0.0;        // These are calculated later!
          p->gamma2 = 0.0;
          p->name = name;
	  KIM::SpeciesName const specName(name);
	  error = modelDriverCreate->SetSpeciesCode(name, value);
	  if (error)
	    throw AsapError("Cannot set species code ") << name << " to " << value;
        }
      else if (word == "V0")
        p->V0 = value * energy_conv;
      else if (word == "kappa")
        p->kappa = value * inv_len_conv;
      else if (word == "eta2")
        p->eta2 = value * inv_len_conv;
      else if (word == "n0")
        p->neq = value * inv_vol_conv;
      else if (word == "S0")
        p->seq = value * len_conv;
      else if (word == "E0")
        p->e0 = value * energy_conv;
      else if (word == "lambda")
        p->lambda = value * inv_len_conv;
      else if (word == "mass")
        {
          p->mass = value;  // Never really used.
          p->invmass = 1.0/value;
        }
      else if (word == "ENDELEMENT")
        {
          if (numread != 9)
            {
              std::cerr << "Wrong number of parameters for element " << p->name << std::endl;
              throw AsapError("Error in parameterfile");
            }
	  p->index = params.size();  // Count the elements, starting at zero.
	  params.push_back(p);
          p = NULL;
        }
      else if (word == "STOP")
        break;
      else
        {
          std::cerr << "Unknown keyword in parameter file: " << word << std::endl;
          throw AsapError("Error in parameterfile");
        }
    }
}

emt_parameters *KimParameterProvider::GetNewParameters(int element)
{
  throw AsapError("Element not supported (missing from parameter file): number = ") << element;
}

