/***************************************************************************
**
**  This file is part of QGpCoreTools.
**
**  This library 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 2.1 of the License, or (at your option) any later version.
**
**  This file 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 library; if not, write to the Free Software
**  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
**
**  See http://www.geopsy.org for more information.
**
**  Created: 2008-07-11
**  Copyright: 2008-2019
**    Marc Wathelet
**    Marc Wathelet (LGIT, Grenoble, France)
**
***************************************************************************/

#ifndef COLUMNTEXTPARSER_H
#define COLUMNTEXTPARSER_H

#include "Thread.h"
#include "XMLClass.h"
#include "ColumnTextDescription.h"
#include "QGpCoreToolsDLLExport.h"

namespace QGpCoreTools {

class QGPCORETOOLS_EXPORT ColumnTextParser : public Thread, public XMLClass
{
  Q_OBJECT
public:
  ColumnTextParser(QObject * parent=nullptr);
  ColumnTextParser(const ColumnTextParser& o);
  ~ColumnTextParser();

  virtual const QString& xml_tagName() const {return xmlColumnTextParserTag;}
  static const QString xmlColumnTextParserTag;

  // Initialization
  void setText(QTextStream * s);
  void setStandardTypes(const QStringList& standardTypes);
  const QStringList& standardTypes() const {return _standardTypes;}

  // Line properties
  int startLine() const {return _startLine;}
  void setStartLine(int s) {_startLine=s<0 ? 0 : s;  }

  int endLine() const {return _endLine;}
  void setEndLine(int e) {_endLine=e<0 ? 0 : e;}

  QString acceptPattern() const {return _acceptPattern;}
  void setAcceptPattern(QString a) {_acceptPattern=a;}

  QString rejectPattern() const {return _rejectPattern;}
  void setRejectPattern(QString r) {_rejectPattern=r;}

  bool hasComments() const {return _hasComments;}
  void setHasComments(bool c) {_hasComments=c;}

  // Column properties
  void setFixedWidth(bool f) {_fixedWidth=f;}
  bool isFixedWidth() const {return _fixedWidth;}

  QString delimiters() const {return _delimiters;}
  void setDelimiters(QString d) {_delimiters=d;}

  int maximumColumnCount() const {return _maximumColumnCount;}
  void setMaximumColumnCount(int m) {_maximumColumnCount=m;}

  bool acceptNullColumns() const {return _acceptNullColumns;}
  void setAcceptNullColumns(bool a) {_acceptNullColumns=a;}

  QString columnWidths() const {return columnWidths(_columnWidths);}
  void setColumnWidths(QString w) {_columnWidths=columnWidths(w);}

  // Section properties
  bool isSectionLinePattern() const {return _isSectionLinePattern;}
  void setSectionLinePattern(bool b) {_isSectionLinePattern=b;}

  QString sectionLinePattern() const {return _sectionLinePattern;}
  void setSectionLinePattern(QString s) {_sectionLinePattern=s;}

  bool isSectionLineCount() const {return _isSectionLineCount;}
  void setSectionLineCount(bool b) {_isSectionLineCount=b;}

  int sectionLineCount() const {return _sectionLineCount;}
  void setSectionLineCount(int s) {_sectionLineCount=s;}

  bool isSectionRowCount() const {return _isSectionRowCount;}
  void setSectionRowCount(bool b) {_isSectionRowCount=b;}

  int sectionRowCount() const {return _sectionRowCount;}
  void setSectionRowCount(int s) {_sectionRowCount=s;}

  bool isSectionCellPattern() const {return _isSectionCellPattern;}
  void setSectionCellPattern(bool b) {_isSectionCellPattern=b;}

  int sectionCellColumn() const {return _sectionCellColumn;}
  void setSectionCellColumn(int s) {_sectionCellColumn=s;}

  QString sectionCellPattern() const {return _sectionCellPattern;}
  void setSectionCellPattern(QString s) {_sectionCellPattern=s;}

  // Column type and operations
  int type(int column) const {return columnDescription(column).type();}
  void setType(int column, int type) {columnDescription(column).setType(type);}
  void setTypes(const QVector<int>& types);
  QString typeName(int column) const {return _standardTypes[columnDescription(column).type()];}
  void setTypeName(int column, QString type);

  double factor(int column) const {return columnDescription(column).factor();}
  void setFactor(int column, double f) {columnDescription(column).setFactor(f);}

  QString replaceRx(int column) const {return columnDescription(column).replaceRx();}
  void setReplaceRx(int column, QString rx) {columnDescription(column).setReplaceRx(rx);}

  QString replaceAfter(int column) const {return columnDescription(column).replaceAfter();}
  void setReplaceAfter(int column, QString after) {columnDescription(column).setReplaceAfter(after);}

  // Start/stop parser engine
  void startUpdates();
  void stopUpdates();

  // Final queries
  const QString& text(int row, int column) const;
  int columnCount() const;
  int rowCount() const;
  int lineNumber(int row) const;
  int sectionBeginRow(int index) const;
  int sectionEndRow(int index) const;
  int sectionIndexOf(int row) const;
  const QString& sectionComments(int index) const;

  void errorParsingColumn(int iCol, int iRow) const;
protected:
  virtual void xml_writeProperties(XML_WRITEPROPERTIES_ARGS) const;
  virtual void xml_writeChildren(XML_WRITECHILDREN_ARGS) const;
  virtual bool xml_setProperty(XML_SETPROPERTY_ARGS);
  virtual XMLMember xml_member(XML_MEMBER_ARGS);
  virtual bool xml_polish(XML_POLISH_ARGS);
signals:
  void rowsAboutToBeParsed();
  void rowsParsed();
private:
  virtual void run();
  void parseRows();
  void addSection(int row);
  void parseColumns(const QString& rowStr, QRegExp * sectionCellRegExp);
  QString cellContent(int column, QString s) const;
  static QVector<int> columnWidths(const QString& ws);
  static QString columnWidths(const QVector<int>& wi);

  ColumnTextDescription& columnDescription(int column);
  const ColumnTextDescription& columnDescription(int column) const;

  QTextStream * _text;
  QStringList _standardTypes;

  QVector<ColumnTextDescription> _columnDescriptions;

  int _startLine, _endLine;
  QString _acceptPattern, _rejectPattern;
  QString _sectionLinePattern, _sectionCellPattern;
  int _sectionLineCount, _sectionRowCount, _sectionCellColumn;
  unsigned int _fixedWidth:1;
  unsigned int _acceptNullColumns:1;
  unsigned int _isSectionLinePattern:1;
  unsigned int _isSectionLineCount:1;
  unsigned int _isSectionRowCount:1;
  unsigned int _isSectionCellPattern:1;
  unsigned int _hasComments:1;
  QString _delimiters;
  int _maximumColumnCount;
  QVector<int> _columnWidths;

  QAtomicInt _terminate;
  mutable Mutex _mutex;
  int _columnCount, _lineNumber;
  QList<QStringList> _cells;
  QMap<int,int> _lines2rows;
  QList<int> _sectionMarkers;
  QList<QString> _sectionComments;
  int _lastSectionMarkerLine, _lastSectionMarkerRow;

  static const QString _nullCell;
};

} // namespace QGpCoreTools

#endif // COLUMNTEXTPARSER_H
