/***************************************************************************
**
**  This file is part of gpreplace.
**
**  gpreplace is free software: you can redistribute it and/or modify
**  it under the terms of the GNU General Public License as published by
**  the Free Software Foundation, either version 3 of the License, or
**  (at your option) any later version.
**
**  gpreplace 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
**  along with Foobar.  If not, see <http://www.gnu.org/licenses/>
**
**  See http://www.geopsy.org for more information.
**
**  Created: 2005-08-03
**  Copyright: 2005-2019
**    Marc Wathelet
**    Marc Wathelet (LGIT, Grenoble, France)
**
***************************************************************************/

#include <QGpCoreTools.h>

#include "gpreplaceVersion.h"
#include "gpreplaceInstallPath.h"

static QString fileName, pattern, str, outputFile;
static bool hasStringOption=false;
static bool interactive=false;
static bool minimalMatching=false;

ApplicationHelp * help();
bool contains(QRegExp reg, QString& buf);
void replace(QRegExp reg, QString& buf);

PACKAGE_INFO("gpreplace", GPREPLACE)

int main(int argc, char ** argv)
{
  CoreApplication a(argc, argv, help);
  QTextStream sOut(stdout);

  // Check arguments
  int i, j=1;
  for(i=1; i<argc; i++) {
    QString arg=argv[i];
    if(arg[0]=='-') {
      if(arg=="-f") {
        CoreApplication::checkOptionArg(i, argc, argv);
        fileName=argv[i];
      } else if(arg=="-p") {
        CoreApplication::checkOptionArg(i, argc, argv);
        pattern=QString::fromUtf8(argv[i]);
      } else if(arg=="-m") {
        minimalMatching=true;
      } else if(arg=="-s") {
        CoreApplication::checkOptionArg(i, argc, argv);
        hasStringOption=true;
        str=argv[i];
      } else if(arg=="-i") {
        interactive=true;
      } else if(arg=="-o") {
        CoreApplication::checkOptionArg(i, argc, argv);
        outputFile=argv[i];
      } else {
        App::log(tr("gpreplace: bad option %1, see -help\n").arg(argv[i]) );
        return 2;
      }
    } else {
      argv[j++]=argv[i];
    }
  }
  if(j < argc) {
    argv[j]=nullptr;
    argc=j;
  }
  if(fileName.isEmpty()) {
    sOut << "Empty filename, see -h for help." << endl;
    return 2;
  }
  QFile f(fileName);
  if(!f.open(QIODevice::ReadOnly)) {
    sOut << "Cannot open input file " << fileName << endl;
    return 2;
  }
  QTextStream s(&f);
  s.setCodec("UTF-8");
  QString buf=s.readAll();
  pattern.replace("\\!","!");
  QRegExp reg(pattern);
  reg.setMinimal(minimalMatching);

  if(hasStringOption)
    replace(reg, buf);
  else
    return contains(reg, buf) ? 0 : 1;

  return 0;
}

ApplicationHelp * help()
{
  ApplicationHelp * h=new ApplicationHelp;
  h->setOptionSummary( "[OPTIONS] -f <FILE> -p \"<PATTERN>\" -s \"<STRING>\"" );
  h->setComments( "Replace PATTERN by STRING in FILE, "
                  "or search FILE if '-s' is not specified." );
  h->addGroup("", "main");
  h->addOption("-f <FILE>","Input file");
  h->addOption("-p <PATTERN>","Pattern (regular expressions and multi lines admitted)");
  h->addOption("-s <STRING>","Replacement string");
  h->addOption("-i","Interactive replacements");
  h->addOption("-m","Sets minimal matching for repetion operators (non-greedy or lazy)");
  h->addOption("-o <FILE>","Output to file, else to stdout");
  return h;
}

bool contains(QRegExp reg, QString& buf)
{
  return buf.contains(reg);
}

void replace(QRegExp reg, QString& buf)
{
  QTextStream sOut(stdout);
  QTextStream sIn(stdin);
  str.replace("\\n","\n");
  if(interactive) {
    int from=0, cur;
    QString rep;
    while((cur=buf.indexOf(reg, from))>=0) {
      QString c=reg.cap(0);
      QString fStr=str;
      for(int i=0;i<10;i++)
        fStr.replace(QString("\\%1").arg(i), reg.cap(i));

      if(interactive) {
        QString context;

        int bContextCur=cur;
        bContextCur=buf.lastIndexOf("\n",bContextCur);
        if(bContextCur<0)
          bContextCur=0;
        int eContextCur=cur+c.size();
        eContextCur=buf.indexOf("\n",eContextCur);
        if(eContextCur<0)
          eContextCur=buf.size()-1;

        int bContext=cur;
        for(int i=0;i<5 && bContext>=0;i++)
          bContext=buf.lastIndexOf("\n",bContext-1);
        if(bContext<0)
          bContext=0;
        if(bContextCur>bContext) {
          context=buf.mid(bContext, bContextCur-bContext);
          context.replace("\n", "\n|| ");
          sOut << context;
        }

        context=buf.mid(bContextCur, eContextCur-bContextCur);
        context.replace("\n", "\n-- ");
        sOut << context;
        context=buf.mid(bContextCur, cur-bContextCur)+fStr;
        if(eContextCur>(cur+c.size()))
          context+=buf.mid(cur+c.size(), eContextCur-(cur+c.size()));
        context.replace("\n", "\n++ ");
        sOut << context;

        int eContext=eContextCur;
        for(int i=0;i<5 && eContext>=0;i++)
          eContext=buf.indexOf("\n",eContext+1);
        if(eContext<0)
          eContext=buf.size()-1;
        if(eContext>eContextCur) {
          context=buf.mid(eContextCur, eContext-eContextCur);
          context.replace("\n", "\n|| ");
          sOut << context;
        }

        sOut << endl << endl;
        sOut << "Do you want to replace? (y/n/a) [y] " << flush;
        CoreApplication::instance()->debugUserInterrupts(false);
        rep=sIn.readLine();
        CoreApplication::instance()->debugUserInterrupts(true);
        rep=rep.toLower();
      }
      if(rep=="n") {
        from=cur+c.size();
      } else {
        buf.replace(cur, c.size(), fStr);
        from=cur+fStr.size();
      }
      if(rep=="a")
        interactive=false;
    }
  } else {
    // Basic Qt implementation already contains support for \1 \2, ... sub expresions
    buf.replace(reg, str);
  }
  if(outputFile.isEmpty()) {
    sOut.setCodec("UTF-8");
    sOut << buf;
  } else {
    QFile f(outputFile);
    if(!f.open(QIODevice::WriteOnly)) {
      sOut << "Cannot open output file " << outputFile << endl;
    }
    QTextStream s(&f);
    s.setCodec("UTF-8");
    s << buf;
  }
}

