/***************************************************************************
**
**  This file is part of gptime.
**
**  gptime 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.
**
**  gptime 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: 2009-06-19
**  Copyright: 2009-2019
**    Marc Wathelet
**    Marc Wathelet (LGIT, Grenoble, France)
**
***************************************************************************/

#include <QtCore>

#include "TimeReader.h"

/*!
  \class TimeReader TimeReader.h
  \brief Brief description of class still missing

  Full description of class still missing
*/

/*!
  Description of constructor still missing
*/
TimeReader::TimeReader()
{
  TRACE;
  _action=Convert;
  _mode=Absolute;
  _column=-1;
  _delimiters=StringSection::defaultSeparators;
  _startByte=0;
  _endByte=0;
  _outputPrecision=6;
  _verbose=false;
}

bool TimeReader::setOptions(int& argc, char ** argv)
{
  TRACE;
  int i, j=1;
  for(i=1; i<argc; i++) {
    QByteArray arg=argv[i];
    if(arg[0]=='-') {
      if(arg=="-convert") {
        _action=Convert;
      } else if(arg=="-relative") {
        _mode=Relative;
      } else if(arg=="-i" || arg=="-in-format") {
        CoreApplication::checkOptionArg(i, argc, argv);
        _inFormats.append(argv[i]);
      } else if(arg=="-o" || arg=="-out-format") {
        CoreApplication::checkOptionArg(i, argc, argv);
        _outFormat=argv[i];
      } else if(arg=="-p") {
        CoreApplication::checkOptionArg(i, argc, argv);
        _outputPrecision=atoi(argv[i]);
      } else if(arg=="-c") {
        CoreApplication::checkOptionArg(i, argc, argv);
        _column=atoi(argv[i])-1;
      } else if(arg=="-d") {
        CoreApplication::checkOptionArg(i, argc, argv);
        _delimiters=argv[i];
      } else if(arg=="-s") {
        CoreApplication::checkOptionArg(i, argc, argv);
        _startByte=atoi(argv[i]);
      } else if(arg=="-e") {
        CoreApplication::checkOptionArg(i, argc, argv);
        _endByte=atoi(argv[i]);
      } else if(arg=="-verbose") {
        _verbose=true;
      } else {
        App::log(tr("gptime: bad option %1, see -help\n").arg(argv[i]) );
        return false;
      }
    } else {
      argv[j++]=argv[i];
    }
  }
  if(j < argc) {
    argv[j]=nullptr;
    argc=j;
  }
  switch(_mode) {
  case Absolute:
    if(_inFormats.isEmpty()) {
      _inFormats.append("time_t");
    }
    if(_outFormat.isEmpty()) {
      _outFormat="yyyy-MM-dd hh:mm:ssz";
    }
    break;
  case Relative:
    if(!_inFormats.isEmpty()) {
      App::log(tr("gptime: for relative mode, input format is automatically recognized, '-i' is not valid.\n") );
      return false;
    }
    if(_outFormat.isEmpty()) {
      _outFormat="w";
    } else {
      if(_outFormat.count()>1) {
        App::log(tr("gptime: not a valid format in relative mode (%1).\n").arg(_outFormat) );
        return false;
      }
    }
    break;
  }
  return true;
}

bool TimeReader::parse(QTextStream& s)
{
  TRACE;
  QTextStream sOut(stdout);
  QString buf;
  int lineNumber=0;
  while(!s.atEnd()) {
    lineNumber++;
    buf=s.readLine();
    if(!buf.isEmpty() && buf[0]!='#') {
      switch(_action) {
      case Convert: {
          bool ok=true;
          QString textBefore, textAfter;
          if(_column>-1) {
            LineParser p(buf);
            p.setDelimiters(_delimiters);
            textAfter=buf.mid(p.endsAt(_column, ok)+1);
            textBefore=buf.left(p.startsAt(_column, ok));
            buf=p.toString(_column, ok);
            if(!ok) {
              App::log(tr("gptime: error parsing column %1 at line %2\n")
                       .arg(_column)
                       .arg(lineNumber));
              return false;
            }
          } else {
            if(_endByte>0 && _endByte<buf.length()) {
              textAfter=buf.mid(_endByte+1);
              buf=buf.left(_endByte+1);
            }
            if(_startByte>0 && _startByte<buf.length()) {
              textBefore=buf.left(_startByte);
              buf=buf.mid(_startByte);
            }
          }
          buf=buf.trimmed();
          if(_verbose) {
            sOut << "# converting \"" << buf << "\"" << endl;
          }
          QString str;
          switch(_mode) {
          case Absolute: {
              DateTime dt=inDateTime(buf, ok);
              if(!ok) {
                App::log(tr("gptime: error parsing input at line %2: '%1'\n")
                         .arg(buf)
                         .arg(lineNumber));
                return false;
              }
              str=outDateTime(dt);
            }
            break;
          case Relative: {
              double t=Number::durationToSeconds(buf, ok);
              if(!ok) {
                App::log(tr("gptime: error parsing input at line %2: '%1'\n")
                         .arg(buf)
                         .arg(lineNumber));
                return false;
              }
              str=Number::secondsToDuration(t, _outputPrecision);
            }
            break;
          }
          if(!str.isNull()) {
            sOut << textBefore << str << textAfter << endl;
          }
        }
        break;
      case None:
        App::log(tr("gptime: no action defined, see -h\n") );
        return false;
      }
    }
  }
  return true;
}

DateTime TimeReader::inDateTime(const QString& s, bool& ok)
{
  DateTime dt;
  int n=_inFormats.count();
  QString errMsg;
  StringStream * errStream=new StringStream(&errMsg);
  App::beginRedirect(errStream);
  for(int i=0; i<n; i++) {
    const QString& format=_inFormats.at(i);
    if(format=="time_t") {
      dt.fromTime_t(s);
    } else if(format=="julian") {
      QDate d(QDate::fromJulianDay(s.toUInt()));
      ok=dt.setDate(d.year(), d.month(), d.day());
    } else if(format=="gps-week") {
      QDate d(1980, 1, 6);
      d=d.addDays(s.toUInt()*7);
      ok=dt.setDate(d.year(), d.month(), d.day());
    } else if(format=="doy" || format=="dow" || format=="julian-year") {
      App::log(tr("gptime: bad input format\n\n") );
    } else {
      ok=dt.fromString(s, format);
    }
    if(ok) {
      break;
    }
  }
  App::endRedirect(errStream);
  if(!ok) {
    App::log(errMsg);
  }
  return dt;
}

QString TimeReader::outDateTime(const DateTime& dt)
{
  if(_outFormat=="time_t") {
    return dt.toTime_t(_outputPrecision);
  } else if(_outFormat=="julian") {
    return QString::number(dt.date().toJulianDay());
  } else if(_outFormat=="doy") {
    return QString::number(dt.date().dayOfYear());
  } else if(_outFormat=="dow") {
    return QString::number(dt.date().dayOfWeek());
  } else if(_outFormat=="gps-dow") {
    return QString::number(QDate(1980, 1 ,6).daysTo(dt.date())%7);
  } else if(_outFormat=="gps-week") {
    return QString::number(QDate(1980, 1 ,6).daysTo(dt.date())/7);
  } else if(_outFormat=="julian-year") {
    App::log(tr("gptime: bad output format\n\n") );
    return QString();
  } else if(_outFormat.startsWith("seconds since ")) {
    DateTime ref;
    if(!ref.fromString(_outFormat.mid(14), "yyyyMMdd")) {
      App::log(tr("gptime: bad format for reference date in output format\n\n") );
      return QString();
    }
    return QString::number(ref.secondsTo(dt), 'f', _outputPrecision);
  } else {
    return dt.toString(_outFormat, _outputPrecision);
  }
}

