/***************************************************************************
**
**  This file is part of QGpCoreMath.
**
**  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-15
**  Copyright: 2008-2019
**    Marc Wathelet
**    Marc Wathelet (LGIT, Grenoble, France)
**
***************************************************************************/

#ifndef MATRIXITERATOR_H
#define MATRIXITERATOR_H

#include "QGpCoreMathDLLExport.h"
#include "Matrix.h"

namespace QGpCoreMath {

template <typename T> class QGPCOREMATH_EXPORT MatrixIterator
{
public:
  inline MatrixIterator(const Matrix<T>& m);

  inline const T * next() {_cur += _offset; return _cur;}
  inline const T * previous() {_cur -= _offset; return _cur;}
  inline const T& operator*() {ASSERT(_cur >= _start && _cur <= _end); return *_cur;}
  bool hasNext() const {return _cur < _end;}
  bool hasPrevious() const {return _cur > _start;}
  void toBack() {return _cur=_end;}
  void toFront() {return _cur=_start;}
protected:
  MatrixIterator() {}

  const T * _cur, * _start, * _end;
  int _offset;
};

template <typename T> class QGPCOREMATH_EXPORT MutableMatrixIterator
{
public:
  inline MutableMatrixIterator(Matrix<T>& m);

  inline T * next() {_cur+=_offset; return _cur;}
  inline T * previous() {_cur-=_offset; return _cur;}
  inline T& operator*() {ASSERT(_cur>=_start && _cur<=_end); return *_cur;}
  bool hasNext() const {return _cur<_end;}
  bool hasPrevious() const {return _cur>_start;}
  void toBack() {return _cur=_end+_offset;}
  void toFront() {return _cur=_start-_offset;}
protected:
  MutableMatrixIterator() {}

  T * _cur, * _start, * _end;
  int _offset;
};

template <typename T> class QGPCOREMATH_EXPORT MatrixRowIterator : public MatrixIterator<T>
{
public:
  inline MatrixRowIterator(const Matrix<T>& m, int iRow);
};

template <typename T> class QGPCOREMATH_EXPORT MatrixColumnIterator : public MatrixIterator<T>
{
public:
  inline MatrixColumnIterator(const Matrix<T>& m, int iCol);
};

template <typename T>
inline MatrixIterator<T>::MatrixIterator(const Matrix<T>& m)
{
  TRACE;
  _offset=1;
  _start=m._d->values();
  _end=_start+m.rowCount()*m.columnCount()-1;
  _cur=_start-_offset;
}

template <typename T>
inline MutableMatrixIterator<T>::MutableMatrixIterator(Matrix<T>& m)
{
  TRACE;
  _offset=1;
  _start=m._d->values();
  _end=_start+m.rowCount()*m.columnCount()-1;
  _cur=_start-_offset;
}

template <typename T>
inline MatrixRowIterator<T>::MatrixRowIterator(const Matrix<T>& m, int iRow)
   : MatrixIterator<T>()
{
  TRACE;
  ASSERT(iRow>=0 && iRow<m.rowCount());
  MatrixIterator<T>::_offset=m.rowCount();
  MatrixIterator<T>::_start=m._d->values() + iRow;
  MatrixIterator<T>::_end=MatrixIterator<T>::_start+m.rowCount()*(m.columnCount()-1);
  MatrixIterator<T>::_cur=MatrixIterator<T>::_start-MatrixIterator<T>::_offset;
}

template <typename T>
inline MatrixColumnIterator<T>::MatrixColumnIterator(const Matrix<T>& m, int iCol)
   : MatrixIterator<T>()
{
  TRACE;
  ASSERT(iCol>=0 && iCol<m.columnCount());
  MatrixIterator<T>::_offset=1;
  MatrixIterator<T>::_start=m._d->values()+m.rowCount()*iCol;
  MatrixIterator<T>::_end=MatrixIterator<T>::_start+m.rowCount()-1;
  MatrixIterator<T>::_cur=MatrixIterator<T>::_start-MatrixIterator<T>::_offset;
}

template <typename T> class QGPCOREMATH_EXPORT MutableMatrixRowIterator : public MatrixIterator<T>
{
public:
  inline MutableMatrixRowIterator(const Matrix<T>& m, int iRow);
};

template <typename T>
inline MutableMatrixRowIterator<T>::MutableMatrixRowIterator(const Matrix<T>& m, int iRow)
   : MutableMatrixIterator<T>()
{
  TRACE;
  ASSERT(iRow >= 0 && iRow < m.rowCount());
  MutableMatrixIterator<T>::_offset=m.rowCount();
  MutableMatrixIterator<T>::_start=m._data.data()+iRow;
  MutableMatrixIterator<T>::_end=MutableMatrixIterator<T>::_start+m.rowCount()*(m.columnCount()-1);
  MutableMatrixIterator<T>::_cur=MutableMatrixIterator<T>::_start-m.rowCount();
}

template <typename T> class QGPCOREMATH_EXPORT MutableMatrixColumnIterator : public MatrixIterator<T>
{
public:
  inline MutableMatrixColumnIterator(const Matrix<T>& m, int iCol);
};

template <typename T>
inline MutableMatrixColumnIterator<T>::MutableMatrixColumnIterator(const Matrix<T>& m, int iCol)
   : MutableMatrixIterator<T>()
{
  TRACE;
  ASSERT(iCol >= 0 && iCol < m.columnCount());
  MutableMatrixIterator<T>::_offset=1;
  MutableMatrixIterator<T>::_start=m._data.data() + m.rowCount() * iCol;
  MutableMatrixIterator<T>::_end=MutableMatrixIterator<T>::_start + m.rowCount() - 1;
  MutableMatrixIterator<T>::_cur=MutableMatrixIterator<T>::_start - 1;
}

} // namespace QGpCoreMath

#endif // MATRIXITERATOR_H
