/***************************************************************************
**
**  This file is part of ArrayCore.
**
**  ArrayCore 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.
**
**  ArrayCore 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: 2019-12-02
**  Copyright: 2019
**    Marc Wathelet (ISTerre, Grenoble, France)
**
***************************************************************************/

#ifndef FKCROSSSPECTRUM_H
#define FKCROSSSPECTRUM_H

#include "ArrayCoreDLLExport.h"
#include "ArrayCrossSpectrum.h"
#include "AbstractFKFunction.h"
#include "ActiveFKSteering.h"

namespace ArrayCore {

  class ARRAYCORE_EXPORT FKCrossSpectrum : public ArrayCrossSpectrum
  {
  public:
    FKCrossSpectrum(const ArraySelection * array,
                    const FKParameters * param);
    ~FKCrossSpectrum();

    const FKParameters * parameters() const {return static_cast<const FKParameters *>(_parameters);}

    double rotationStep() const {return _rotateStep;}
    int rotationStepCount() const {return _rotateStepCount;}

    bool calculate(const QVector<int>& blocks, AbstractFKFunction * rayleigh, AbstractFKFunction * love);

    void setActiveSource(const SeismicEvent * src) {_source=src;}
    Point setActiveSource(const QVector<int>& blocks, bool& ok);

    const Point& sourceOffset() const {return _sourceOffset;}
    void setSourceOffset(const Point& o) {_sourceOffset=o;}

    const ArraySelection& array() const {return _array;}

    void resetRotatedRayleigh();
    void resetRotatedLove();

    void addThreeComponent();
    // Test of optical fiber arrays
    //void addHorizontalRotatedRayleigh() {addHorizontalRotated(_rayleigh);}
    //void addHorizontalRotatedLove() {addHorizontalRotated(_love);}
    void addRotatedRadial();
    void addRotatedTransverse();
    void addRotatedRayleigh();
    void addActiveVertical(const ActiveFKSteering& geom);
    void addActiveRayleigh(const ActiveFKSteering& geom);
    void addActiveTransverse(const ActiveFKSteering& geom);
    void addActiveThreeComponent(const ActiveFKSteering& geom);

    void meanRotatedRayleigh(int nBlocks);
    void meanRotatedLove(int nBlocks);

    bool invertRayleigh();
    bool invertLove();
    bool invertRotatedRayleigh();
    bool invertRotatedLove();

    inline const ComplexMatrix& rayleigh() const;
    inline const ComplexMatrix& love() const;
    inline const ComplexMatrix& rotatedRayleigh(const Point2D& k) const;
    inline const ComplexMatrix& rotatedRayleigh(int rotationIndex) const;
    inline const ComplexMatrix& rotatedLove(const Point2D& k) const;

    bool setMask(AbstractFKFunction::Type t);
    void setEllipticity(const ComplexMatrix& ell);
  private:
    void resize();
    bool errorInvert(QString component);
    // Test of optical fiber arrays
    //void addHorizontalRotated(ComplexMatrix& covmat);
    void addRotatedRadial(int rotationIndex);
    void addRotatedTransverse(int rotationIndex);
    void addRotatedRayleigh(int rotationIndex);
    inline int rotationIndex(const Point2D& k) const;
    void normalize(ComplexMatrix& covmat);
    bool invert(ComplexMatrix& covmat);

    bool setRayleighArrayMask();
    bool setOneComponentArrayMask();

    int _rotateStepCount;
    double _rotateStep, _invRotateStep;

    ArraySelection _array;
    const SeismicEvent * _source;
    Point _sourceOffset;

    DoubleMatrix _rayleighArrayMask;
    DoubleMatrix _loveArrayMask;

    ComplexMatrix * _rotatedRayleigh;
    ComplexMatrix * _rotatedLove;
  };

  inline int FKCrossSpectrum::rotationIndex(const Point2D& k) const
  {
    double angle=::atan2(k.y(), k.x()); // From -M_PI to M_PI
    if(angle<0.0) {
      angle+=2.0*M_PI;
    }
    // Angle from 0 to M_PI
    int index=qRound(angle*_invRotateStep);
    if(index<_rotateStepCount) {
      return index;
    } else {
      return 0;
    }
  }

  inline const ComplexMatrix& FKCrossSpectrum::rayleigh() const
  {
    return ArrayCrossSpectrum::rayleigh();
  }

  inline const ComplexMatrix& FKCrossSpectrum::love() const
  {
    return ArrayCrossSpectrum::love();
  }

  inline const ComplexMatrix& FKCrossSpectrum::rotatedRayleigh(const Point2D& k) const
  {
    return _rotatedRayleigh[rotationIndex(k)];
  }

  inline const ComplexMatrix& FKCrossSpectrum::rotatedRayleigh(int rotationIndex) const
  {
    return _rotatedRayleigh[rotationIndex];
  }

  inline const ComplexMatrix& FKCrossSpectrum::rotatedLove(const Point2D& k) const
  {
    return _rotatedLove[rotationIndex(k)];
  }

} // namespace ArrayCore

#endif // FKCROSSSPECTRUM_H

