//<<<<<<<<<<<<<<<<<<<<<<<<<<< ArgVS <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
class ArgVS { // argv[0] e' armazenado
 public:
  vector<string> vs;
  bool expandeRun=true;
  void arg2vs(int argc, char** argv); // converte todos os argv para vs.
  ArgVS() {}
  ArgVS(int argc, char** argv, bool _expandeRun=true) { expandeRun=_expandeRun; arg2vs(argc,argv); }
  void imp();
};

//<<<<<<<<<<<<<<<<<<<< Leitura de ArgStr <<<<<<<<<<<<<<<<<<<<<<<<<<<
// Nota: Devolve minuscula e maiuscula, sem fazer conversao de caso
class Comando { public: string first; string second; bool chequei; };

//<<<<<<<<<<<<<<<<<<<<<<<<<<< ArgVS <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
void ArgVS::arg2vs(int argc, char** argv) {
  for (int i=0; i<argc; i++) {
    string st=argv[i];
    vs.push_back(st);
  }
  if (expandeRun) {
    for (int i=1; i<argc; i++) {
      string run;
      if ( vs[i].find("-run=")==0 ) run=vs[i].substr(5);
      if ( vs[i][0]=='`') {
        if (vs[i][ vs[i].length()-1 ]!='`')
          erro("Error: Enclose argument \"`...`\"");
        run=vs[i].substr(1, vs[i].length()-2);
      }
      if (run!="") {
        //cout << "run=" << run << endl;
        run += " > _util_edvar.txt";
        system(run.c_str());
        string st = leArq("_util_edvar.txt");
        vs[i]=st;
      }
    }
  }
}

void ArgVS::imp() {
  for (unsigned i=0; i<vs.size(); i++) {
    cout << i << " " << vs[i] << endl;
  }
  cout << endl;
}
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

class ArgComando { // argv[0] nao e' armazenado
 public:
  bool exclamacao2porcentagem=true; // Converte ! para %
  bool apostrofe2aspas=true; // Converte ' para "
  bool expandeRun=true;

  ArgComando() {}
  ~ArgComando() { leuTodos(); }
  void arg2comando(int argc, char** argv); // converte todos os argv para vs.
  ArgComando(int argc, char** argv, bool _expandeRun=true) { expandeRun=_expandeRun; arg2comando(argc,argv); }
  // converte todos os argv para vs. Ex: ArgStr(argc-1,&argv[1]).
  void vs2comando(const ArgVS& arg);
  ArgComando(const ArgVS& arg, bool _expandeRun=true) { expandeRun=_expandeRun; vs2comando(arg); }
  void imp();

  bool getBool(const string& nome1, const string& nome2="", bool def=false);
  // Sem argumento ou seguido de true/false. So presenca indica true.
  int getInt(const string& nome1, const string& nome2="", int def=INT_MIN);
  double getDouble(const string& nome1, const string& nome2="", double def=-DBL_MAX);
  float getFloat(const string& nome1, const string& nome2="", float def=-FLT_MAX);
  string getString(const string& nome1, const string& nome2="", string def="");
  string getCommand(int indice);
  // Indice comeca do zero. Devolve comando sem -nome
  void leuTodos();
  // Gera erro se algum comando nao tiver sido checado. Deve chamar apos todos os gets

  int nameless=0; //numero de comandos sem "-"
 private:
  const string separador="=";
  const string indicadorComando="-";
  vector< Comando > vs; //nome do comando + argumentos. Ex: -f=2.4 => -f 2.4;  nome.cpp => -0 nome.cpp
  void converteSt(string& st);
  Comando separaComando(const string& st);
  int procura(const string& nome1, const string& nome2=""); // devolve -1 se nao acha
};

//<<<<<<<<<<<<<<<<<<<< Leitura de ArgStr <<<<<<<<<<<<<<<<<<<<<<<<<<<
void ArgComando::converteSt(string& st) {
  for (unsigned i=0; i<st.length(); i++) {
    char& ch=st[i];
    if (exclamacao2porcentagem && ch=='!') ch='%';
    if (apostrofe2aspas && ch=='\'') ch='"';
  }
}

Comando ArgComando::separaComando(const string& st) {
  Comando comando{"","",false};
  if (st!="") {
    if (st.find_first_of(indicadorComando)==0) { // aparece '-' na posicao zero
      size_t i=st.find_first_of(separador);
      if (i==string::npos) { // nao ha separador
        comando.first=st;
      } else { // separador na posicao i
        comando.first=st.substr(0,i);
        comando.second=st.substr(i+1);
      }
    } else { // nao aparece '-' na posicao zero. Usa 2 numeros: 00, 01, etc
      char vet[8];
      sprintf(vet,"%02d",nameless++);
      comando.first=string(vet);
      comando.second=st;
    }
  }
  return comando;
}

void ArgComando::arg2comando(int argc, char** argv) {
  if (expandeRun) {
    ArgVS vs(argc,argv,true);
    vs2comando(vs);
  } else {
    for (int i=1; i<argc; i++) {
      string st=argv[i];
      converteSt(st);
      Comando comando=separaComando(st);
      vs.push_back(comando);
    }
  }
}

void ArgComando::vs2comando(const ArgVS& arg) {
  for (int i=1; i<int(arg.vs.size()); i++) {
    string st=arg.vs[i];
    converteSt(st);
    Comando comando=separaComando(st);
    vs.push_back(comando);
  }
}

void ArgComando::imp() {
  for (unsigned i=0; i<vs.size(); i++) {
    cout << vs[i].first << " = " << vs[i].second << endl;
  }
  cout << endl;
}

int ArgComando::procura(const string& nome1, const string& nome2) {
  int i;
  for (i=0; i<int(vs.size()); i++)
    if (vs[i].first==nome1 || vs[i].first==nome2) {
      break;
    }
  if (i==int(vs.size())) i=-1;
  else vs[i].chequei=true;
  return i;
}

bool ArgComando::getBool(const string& nome1, const string& nome2, bool def) {
  int pos=procura(nome1,nome2);
  if (pos<0) return def;

  bool b=true;
  string st=maiuscula(vs[pos].second);
  //if (st=="FALSE") b=false;
  //else if (st=="TRUE") b=true;
  //else b=true;
  if (st=="FALSE") b=false;
  else if (st=="TRUE") b=true;
  else if (st=="") b=true;
  else erro("Erro: Booleano "+vs[pos].first+" deve ser true ou false");
  return b;
}

int ArgComando::getInt(const string& nome1, const string& nome2, int def) {
  int pos=procura(nome1,nome2);
  if (pos<0) return def;
  int sai;
  if (sscanf(vs[pos].second.c_str(),"%d",&sai)!=1)
    erro("Error: Integer conversion "+vs[pos].first);
  return sai;
}

double ArgComando::getDouble(const string& nome1, const string& nome2, double def) {
  int pos=procura(nome1,nome2);
  if (pos<0) return def;
  double sai;
  if (sscanf(vs[pos].second.c_str(),"%lf",&sai)!=1)
    erro("Error: Double conversion "+vs[pos].first);
  return sai;
}

float ArgComando::getFloat(const string& nome1, const string& nome2, float def) {
  int pos=procura(nome1,nome2);
  if (pos<0) return def;
  float sai;
  if (sscanf(vs[pos].second.c_str(),"%f",&sai)!=1)
    erro("Error: Float conversion "+vs[pos].first);
  return sai;
}

string ArgComando::getString(const string& nome1, const string& nome2, string def) {
  int pos=procura(nome1,nome2);
  if (pos<0) return def;
  return vs[pos].second;
}

string ArgComando::getCommand(int indice) {
  char cstr[16];
  sprintf(cstr,"%02d",indice);
  int pos=procura(string(cstr));
  if (pos<0) erro("Error: There is no command #"+string(cstr));
  return vs[pos].second;
}

void ArgComando::leuTodos() {
  for (int i=0; i<int(vs.size()); i++) {
    if (vs[i].chequei==false) {
      erro("Error: Invalid extra command "+vs[i].first+" "+vs[i].second);
    }
  }
}
