//: C04:Cppcheck.cpp
// Z knihy "Myslíme v jazyce C++, část 2" od Bruce Eckela a Chucka Allisona.
// (c) 1995-2004 MindView, Inc. Všechna práva vyhrazena.
// Přečtěte si podmínky používání zdrojových kódů umístěné v souboru 
// 'Licence.txt' dodávaném spolu s balíčkem kódu dostupným na www.MindView.net.
// Nastavuje .h & .cpp soubory, aby odpovídaly standardnímu formátu. 
// Existující soubory testuje, zda odpovídají standardu.
#include <fstream>
#include <sstream>
#include <string>
#include <cstddef>
#include "../require.h"
using namespace std;

bool startsWith(const string& base, const string& key) {
  return base.compare(0, key.size(), key) == 0;
}

void cppCheck(string fileName) {
  enum bufs { BASE, HEADER, IMPLEMENT, HLINE1, GUARD1,
    GUARD2, GUARD3, CPPLINE1, INCLUDE, BUFNUM };
  string part[BUFNUM];
  part[BASE] = fileName;
  // V řetězci najít libovolnou '.' :
  size_t loc = part[BASE].find('.');
  if(loc != string::npos)
    part[BASE].erase(loc); // oddělit příponu
  // Převést na velká písmena:
  for(size_t i = 0; i < part[BASE].size(); i++)
    part[BASE][i] = toupper(part[BASE][i]);
  // Vytvořit jména souborů a vnitřní řádky:
  part[HEADER] = part[BASE] + ".h";
  part[IMPLEMENT] = part[BASE] + ".cpp";
  part[HLINE1] = "//" ": " + part[HEADER];
  part[GUARD1] = "#ifndef " + part[BASE] + "_H";
  part[GUARD2] = "#define " + part[BASE] + "_H";
  part[GUARD3] = "#endif // " + part[BASE] +"_H";
  part[CPPLINE1] = string("//") + ": " + part[IMPLEMENT];
  part[INCLUDE] = "#include \"" + part[HEADER] + "\"";
  // Nejprve zkusit otevřít existující soubory:
  ifstream existh(part[HEADER].c_str()),
           existcpp(part[IMPLEMENT].c_str());
  if(!existh) { // Neexistují; vytvořit
    ofstream newheader(part[HEADER].c_str());
    assure(newheader, part[HEADER].c_str());
    newheader << part[HLINE1] << endl
              << part[GUARD1] << endl
              << part[GUARD2] << endl << endl
              << part[GUARD3] << endl;
  } else { // Již exitují; ověřit
    stringstream hfile; // čtení a zápis
    ostringstream newheader; // zápis
    hfile << existh.rdbuf();
    // Ověřit, že odpovídají první tři řádky
    bool changed = false;
    string s;
    hfile.seekg(0);
    getline(hfile, s);
    bool lineUsed = false;
    //Volání good() je pro Microsoft (také později):
    for(int line = HLINE1; hfile.good() && line <= GUARD2;
         ++line) {
      if(startsWith(s, part[line])) {
        newheader << s << endl;
        lineUsed = true;
        if(getline(hfile, s))
          lineUsed = false;
      } else {
        newheader << part[line] << endl;
        changed = true;
        lineUsed = false;
      }
    }
    // Kopírovat zbytek souboru
    if(!lineUsed)
      newheader << s << endl;
    newheader << hfile.rdbuf();
    // Zkontrolovat GUARD3
    string head = hfile.str();
    if(head.find(part[GUARD3]) == string::npos) {
      newheader << part[GUARD3] << endl;
      changed = true;
    }
    // Jestliže jsou změny přepsat soubor:
    if(changed) {
      existh.close();
      ofstream newH(part[HEADER].c_str());
      assure(newH, part[HEADER].c_str());
      newH << "//@//\n"  // Změnit značku
           << newheader.str();
    }
  }
  if(!existcpp) { // Vytvořit soubor cpp
    ofstream newcpp(part[IMPLEMENT].c_str());
    assure(newcpp, part[IMPLEMENT].c_str());
    newcpp << part[CPPLINE1] << endl
           << part[INCLUDE] << endl;
  } else { // Již existuje, ověřit.
    stringstream cppfile;
    ostringstream newcpp;
    cppfile << existcpp.rdbuf();
    // Zkontrolavet, že odpovídají první dva řádky
    bool changed = false;
    string s;
    cppfile.seekg(0);
    getline(cppfile, s);
    bool lineUsed = false;
    for(int line = CPPLINE1;
        cppfile.good() && line <= INCLUDE; ++line) {
      if(startsWith(s, part[line])) {
        newcpp << s << endl;
        lineUsed = true;
        if(getline(cppfile, s))
          lineUsed = false;
      } else {
        newcpp << part[line] << endl;
        changed = true;
        lineUsed = false;
      }
    }
    // Kopírovat zbytek souboru
    if(!lineUsed)
      newcpp << s << endl;
    newcpp << cppfile.rdbuf();
    // Jestliže jsou změny přepsat soubor:
    if(changed) {
      existcpp.close();
      ofstream newCPP(part[IMPLEMENT].c_str());
      assure(newCPP, part[IMPLEMENT].c_str());
      newCPP << "//@//\n"  // Změnit značku
             << newcpp.str();
    }
  }
}

int main(int argc, char* argv[]) {
  if(argc > 1)
    cppCheck(argv[1]);
  else
    cppCheck("cppCheckTest.h");
} ///:~
