// Copyright (C) 2010, Guy Barrand. All rights reserved.
// See the file tools.license for terms.

#ifndef tools_system
#define tools_system

#include <cstdlib>

#include "cstr"
#include "sout"
#include "num2s"
#include "sto"
#include "words"
#include "sep"
#include "vmanip"
#include "srep"

#if defined(__APPLE__)
#include <TargetConditionals.h>
#endif

namespace tools {

inline bool is_env(const std::string& a_string){
  const char* env = ::getenv(a_string.c_str());
  return (env?true:false);
}

inline bool isenv(const std::string& a_string){return is_env(a_string);} //deprecated.

inline bool get_env(const std::string& a_string,std::string& a_value){
  const char* env = ::getenv(a_string.c_str());
  if(env) {
    a_value = std::string(env?env:"");
    return true;
  } else {
    a_value.clear();
    return false;
  }
}
inline bool getenv(const std::string& a_string,std::string& a_value){return get_env(a_string,a_value);} //deprecated.

template <class T>
inline bool get_env(const std::string& a_string,T& a_v,const T& a_def = T()){
  std::string s;
  if(!getenv(a_string,s)) {a_v = a_def;return false;}
  return to<T>(s,a_v,a_def);
}

inline bool get_env_bool(const std::string& a_string,bool& a_v){
  std::string s;
  if(!getenv(a_string,s)) return false;
  return to(s,a_v);
}

inline bool put_env(const std::string& a_env,const std::string& a_value){
  std::string value = a_env+"="+a_value;
#ifdef TOOLS_MEM
  if(::putenv(str_dup(value.c_str(),false))) return false;
#else
  if(::putenv(str_dup(value.c_str()))) return false;
#endif
  //check:
  std::string s;
  if(!getenv(a_env,s)) return false;
  if(s!=a_value) return false;
  return true;
}
inline bool putenv(const std::string& a_env,const std::string& a_value){return put_env(a_env,a_value);} //deprecated.

inline bool rm_env(const std::string& a_env){
#ifdef _MSC_VER
  std::string value = a_env+"=";
#ifdef TOOLS_MEM
  if(::putenv(str_dup(value.c_str(),false))) return false;
#else
  if(::putenv(str_dup(value.c_str()))) return false;
#endif
#else
  ::unsetenv(a_env.c_str());
#endif
  return true;
}

inline bool rmenv(const std::string& a_env) {return rm_env(a_env);} //deprecated. Still used in OnX.

inline bool check_getenv(std::ostream& a_out,
                         const std::string& a_new,const std::string& a_old,
                         std::string& a_env){
  if(get_env(a_new,a_env)) return true;
  if(get_env(a_old,a_env)) {
    a_out << "Environment variable " << sout(a_old) << " is deprecated. Use " << sout(a_new) << " instead." << std::endl;
    return true;
  }
  return false;
}

inline int std_system(std::ostream& a_out,const std::string& a_string) {
#if TARGET_OS_IPHONE
  a_out << "tools::std_system : system() not available on iOS. Can't execute " << sout(a_string) << "." << std::endl;
  return EXIT_FAILURE;
#else
  return ::system(a_string.c_str());
  (void)a_out;
#endif
}

inline bool env_append(const std::string& a_env,const std::string& a_sep,const std::vector<std::string>& a_vals){
  std::string env_value;
  if(is_env(a_env)) {
    if(!get_env(a_env,env_value)) return false;
  }

 {std::vector<std::string>::const_iterator it;
  for(it=a_vals.begin();it!=a_vals.end();++it) {
    const std::string& value = *it;
    if(value.empty()) continue;

    if(env_value.size()) {
      std::vector<std::string> ws;
      words(env_value,a_sep,false,ws);
      // Remove existing one, so that value be put at end.
      remove(ws,value);
      if(!nums2s<std::string>(ws,env_value,a_sep)) {}
    }

    if(env_value.size()) env_value += a_sep;
    env_value += value;
  }}

  if(!putenv(a_env,env_value)) return false;

  return true;
}

inline bool env_path_append(const std::string& a_env,const std::vector<std::string>& a_paths){
  return env_append(a_env,psep(),a_paths);
}

inline bool env_append_path(const std::string& a_env,const std::string& a_path){
  std::vector<std::string> v;
  v.push_back(a_path);
  return env_append(a_env,psep(),v);
}

// OpenPAW, Panoramix :
inline bool rep_env(std::string& a_string) {
  char spec_char = '\n';
  std::string::size_type dollar;
  while((dollar=a_string.find('$'))!=std::string::npos){
    std::string::size_type slash = a_string.find('/',dollar+1);
    std::string::size_type back_slash = a_string.find('\\',dollar+1);
    std::string::size_type pos = std::string::npos; 
    if(slash!=std::string::npos) {
      if(back_slash!=std::string::npos) {
        pos = slash<back_slash?slash:back_slash;
      } else {
        pos = slash;
      }
    } else {
      if(back_slash!=std::string::npos) {
        pos = back_slash;
      } else {
        pos = std::string::npos; 
      }
    }
    std::string env;
    if(pos==std::string::npos) {
      env = a_string.substr(dollar+1,a_string.length()-(dollar+1));
    } else {
      //     abc$xxx/ef
      //     0  3   7 9
      env = a_string.substr(dollar+1,pos-(dollar+1));
    }
    char* val = ::getenv(env.c_str());
    if(!val) {
      // We may have $ in a_string not attached to an env variable.
      // For example at LAL, some Windows account name have a leading $
      // that is found back in the home directory path. To bypass this
      // problem we change temporarily the $ by spec_char and continue
      // the loop. We change back all the spec_chars to $ at end.
      a_string[dollar] = spec_char;
    } else {
      std::string value = a_string.substr(0,dollar);
      value += val;
      if(pos!=std::string::npos) value += a_string.substr(pos,a_string.length()-pos);
      a_string = value;
    }
  }
  replace(a_string,spec_char,'$');
  return true;
}

inline bool repenv(std::string& a_string) {return rep_env(a_string);} //deprecated.

inline bool file_name(const std::string& a_path,std::string& a_name) {
  //used in osc packages and a tools::xml::parser::load_file() compatible for OnX.
  a_name = a_path;
  if(!rep_env(a_name)) {a_name.clear();return false;}
#ifdef _WIN32
  replace(a_name,"/","\\");
  replace(a_name,"\"","");
#endif
  return true;
}

}

#endif
