#ifndef AMPL_ENVIRONMENT_H
#define AMPL_ENVIRONMENT_H

#include "ampl/ep/environment_ep.h"
#include "ampl/ep/errorinfo_ep.h"
#include "ampl/ep/scopedarray.h" // For getStringFromDLL
#include "ampl/ep/countediterator.h"
#include "ampl/format.h"


namespace ampl {
/**
* This class provides access to the environment variables and provides
* facilities to specify where to load the underlying %AMPL interpreter.
*/
class Environment {
  friend class AMPL;

private:
  internal::Environment* impl_;

public:
  /**
  \rst
  Default constructor
  \endrst
  */
  Environment() { impl_ = internal::AMPL_Environment_Create(internal::ErrorInfo()); }

  /**
  \rst
  Copy constructor
  \endrst
  */
  Environment(const Environment& other) { impl_ = internal::AMPL_Environment_Copy(other.impl_, internal::ErrorInfo()); }

  /**
  Assignment operator
  */
  Environment& operator=(const Environment& other) {
    internal::Environment* newimpl = internal::AMPL_Environment_Copy(other.impl_, internal::ErrorInfo());
    internal::AMPL_Environment_Destroy(impl_);
    impl_ = newimpl;
    return (*this);
  }

  /**
  \rst
  Constructor with ability to select the location of the %AMPL binary.
  Note that if this constructor is used, the automatic lookup for an AMPL
  executable
  will not be executed.
  \param binaryDirectory The directory in which look for the %AMPL Binary
  \endrst
  */
  explicit Environment(fmt::CStringRef binaryDirectory) { impl_ = internal::AMPL_Environment_Create_WithBin(binaryDirectory.c_str(), internal::ErrorInfo()); }

  /**
   * Destructor
   */
  ~Environment() { internal::AMPL_Environment_Destroy(impl_); }

  /**
  * Add an environment variable to the environment, or change its value
  * if already defined
  * \param name name of the environment variable
  * \param value value to be assigned
  */
  void put(fmt::CStringRef name, fmt::CStringRef value) { internal::AMPL_Environment_addEnvironmentVariable(impl_, name.c_str(), value.c_str(), internal::ErrorInfo()); }

  /**
   * Set the location where AMPLAPI will search for the %AMPL executable
   * \param binaryDirectory Directory 
   */
  void setBinDir(fmt::CStringRef binaryDirectory) { internal::AMPL_Environment_setBinaryDirectory(impl_, binaryDirectory.c_str(), internal::ErrorInfo()); }

  /**
  * Get the location where AMPLAPI will search for the %AMPL executable
  */
  std::string getBinDir() const { return std::string(internal::AMPL_Environment_getBinaryDirectory(impl_, internal::ErrorInfo())); }

  /**
  \rst
  Print all variables in the map
  \endrst
  */
  std::string toString() const { return internal::getStringFromDLL(internal::AMPL_Environment_toString(impl_, internal::ErrorInfo())); }
  /**
  Iterator for the map
  */
  typedef internal::CountedIterator<internal::EnvironmentIterator> iterator;

  /**
  Returns an iterator pointing to the first environment variable in the map.
  */
  iterator begin() const { return iterator(internal::AMPL_Environment_begin(impl_, internal::ErrorInfo())); }

  /**
  Returns an iterator pointing to the past-the-end element in the map:
  */
  iterator end() const { return iterator(internal::AMPL_Environment_end(impl_, internal::ErrorInfo())); }

  /**
  Searches the current object for an environment variable called name and 
  returns an iterator to it if found, otherwise it returns an iterator to Environment::end.
  */
  iterator find(fmt::CStringRef name) const { return iterator(internal::AMPL_Environment_find(impl_, name.c_str(), internal::ErrorInfo())); }

  std::size_t size() const {  return internal::AMPL_Environment_size(impl_, internal::ErrorInfo());
 }

};
}

#endif  // AMPL_ENVIRONMENT_H
