/**
 * \file tsv2ods.cpp
 * \date 11/11/2018
 * \author Olivier Langella
 * \brief translate TSV to ODS file
 */

/*******************************************************************************
 * Copyright (c) 2018 Olivier Langella <Olivier.Langella@u-psud.fr>.
 *
 * This file is part of the libodsstream library.
 *
 *    libodsstream is a library to read and write ODS documents as streams
 *    Copyright (C) 2018  Olivier Langella <Olivier.Langella@u-psud.fr>
 *
 *    This program is free software: you can redistribute it and/or modify
 *    it under the terms of the GNU Lesser General Public License as published
 *by the Free Software Foundation, either version 3 of the License, or (at your
 *option) any later version.
 *
 *    This program 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 Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Contributors:
 *     Olivier Langella <Olivier.Langella@u-psud.fr> - initial API and
 *implementation
 ******************************************************************************/

#include "tsv2ods.h"

#include <QCommandLineParser>
#include <QTimer>
#include <QFile>
#include <QFileInfo>
#include <odsstream/tsvreader.h>
#include <odsstream/odsdocwriter.h>
#include <odsstream/odsexception.h>


class CustomHandler : public OdsDocHandlerInterface
{
  public:
  CustomHandler(QFileInfo &fi)
  {
    qDebug() << QString("%1.ods").arg(fi.completeBaseName());
    _p_writer = new OdsDocWriter(QString("%1.ods").arg(fi.completeBaseName()));
  }

  ~CustomHandler()
  {
    delete _p_writer;
  }

  /**
   * callback that indicates the begining of a data sheet. Override it in
   * order to retrieve information about the current data sheet.
   *
   */
  virtual void
  startSheet(const QString &sheet_name)
  {
    qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__ << " "
             << sheet_name;
    _p_writer->writeSheet(sheet_name);
  };

  /**
   * callback that indicates the end of the current data sheet. Override it if
   * needed
   */
  virtual void endSheet(){
    //_p_writer-> endSheet();
  };

  /**
   * callback that indicates a new line start. Override it if needed.
   */

  virtual void startLine(){
    //_p_writer-> writeLine();
  };

  /**
   * callback that indicates a line ending. Override it if needed.
   */

  virtual void
  endLine()
  {
    _p_writer->writeLine();
  };

  /**
   * callback that report the content of the current cell in a dedicated Cell
   * object. Override it if you need to retrieve cell content.
   */
  virtual void
  setCell(const OdsCell &cell)
  {
    qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__ << " "
             << cell.toString();
    if(cell.isBoolean())
      {
        _p_writer->writeCell(cell.getBooleanValue());
      }
    else if(cell.isDate())
      {
        _p_writer->writeCell(cell.getDateTimeValue());
      }
    else if(cell.isDouble())
      {
        _p_writer->writeCell(cell.getDoubleValue());
      }
    else if(cell.isEmpty())
      {
        _p_writer->writeEmptyCell();
      }
    else
      {

        qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__ << " "
                 << cell.toString();
        _p_writer->writeCell(cell.toString());
      }
  };

  /**
   * callback that report the end of the ODS document. Override it if you need
   * to know that reading is finished.
   */
  virtual void
  endDocument()
  {
    _p_writer->close();
  };

  private:
  OdsDocWriter *_p_writer;
};


Tsv2Ods::Tsv2Ods(QObject *parent) : QObject(parent)
{
  // get the instance of the main application
  app = QCoreApplication::instance();
  // setup everything here
  // create any global objects
  // setup debug and warning mode
}

// 10ms after the application starts this method will run
// all QT messaging is running at this point so threads, signals and slots
// will all work as expected.
void
Tsv2Ods::run()
{

  QTextStream errorStream(stderr, QIODevice::WriteOnly);

  try
    {
      qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__;
      QCommandLineParser parser;

      // throw pappso::PappsoException("test");
      parser.setApplicationDescription(tr(
        "tsv2ods converts tabulated text files to Open Document Spreadsheet"));
      parser.addHelpOption();
      parser.addVersionOption();
      parser.addPositionalArgument(
        "TSV file",
        QCoreApplication::translate("main", "TSV file to convert."));


      QCommandLineOption separatorOption(
        QStringList() << "s"
                      << "separator",
        tr("separator character to use. Please choose one of : tab (default), "
           "comma, "
           "semicolon"),
        "separator",
        "tab");
      parser.addOption(separatorOption);


      qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__;

      // Process the actual command line arguments given by the user
      parser.process(*app);

      // QCoreApplication * app(this);
      // Add your main code here
      qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__;

      const QDateTime dt_begin = QDateTime::currentDateTime();
      const QStringList args   = parser.positionalArguments();


      QStringList tsv_list = parser.positionalArguments();
      for(QString &tsv_file : tsv_list)
        {
          QFileInfo fi(tsv_file);
          if(!fi.exists())
            {
              throw OdsException(QObject::tr("file %1 does not exists")
                                   .arg(fi.absoluteFilePath()));
            }
          QFile file(tsv_file);
          CustomHandler handler(fi);

          TsvReader reader(handler);

          QString separator_str = parser.value(separatorOption);
          if(separator_str == "comma")
            {
              reader.setSeparator(TsvSeparator::comma);
            }
          else
            {
              if(separator_str == "semicolon")
                {
                  reader.setSeparator(TsvSeparator::semicolon);
                }
            }
          reader.parse(file);
          // file.close();
        }
    }
  catch(OdsException &error)
    {
      errorStream << "Oops! an error occurred in tsv2ods. Don't Panic :"
                  << Qt::endl;
      errorStream << error.qwhat() << Qt::endl;
      exit(1);
      app->exit(1);
    }

  catch(std::exception &error)
    {
      errorStream << "Oops! an error occurred in tsv2ods. Don't Panic :"
                  << Qt::endl;
      errorStream << error.what() << Qt::endl;
      exit(1);
      app->exit(1);
    }


  // you must call quit when complete or the program will stay in the
  // messaging loop
  quit();
}

// call this routine to quit the application
void
Tsv2Ods::quit()
{
  // you can do some cleanup here
  // then do emit finished to signal CoreApplication to quit
  emit finished();
}

// shortly after quit is called the CoreApplication will signal this routine
// this is a good place to delete any objects that were created in the
// constructor and/or to stop any threads
void
Tsv2Ods::aboutToQuitApp()
{
  // stop threads
  // sleep(1);   // wait for threads to stop.
  // delete any objects
}


int
main(int argc, char **argv)
{
  qSetMessagePattern(QString("%{file}@%{line}, %{function}(): %{message}"));
  qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__;
  QCoreApplication app(argc, argv);
  qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__;
  QCoreApplication::setApplicationName("tsv2ods");
  QCoreApplication::setApplicationVersion(LIBODSSTREAM_VERSION);
  QLocale::setDefault(QLocale::system());

  // create the main class
  Tsv2Ods myMain;
  // connect up the signals
  QObject::connect(&myMain, SIGNAL(finished()), &app, SLOT(quit()));
  QObject::connect(
    &app, SIGNAL(aboutToQuit()), &myMain, SLOT(aboutToQuitApp()));
  qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__;


  // This code will start the messaging engine in QT and in
  // 10ms it will start the execution in the MainClass.run routine;
  QTimer::singleShot(10, &myMain, SLOT(run()));
  return app.exec();
}
