/***************************************************************************
**
**  This file is part of GeopsyCore.
**
**  GeopsyCore 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.
**
**  GeopsyCore 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: 2003-11-10
**  Copyright: 2003-2019
**    Marc Wathelet
**    Marc Wathelet (ULg, Liège, Belgium)
**    Marc Wathelet (LGIT, Grenoble, France)
**
***************************************************************************/

#ifndef SUBSIGNALPOOL_H
#define SUBSIGNALPOOL_H

#include <QGpCoreTools.h>

#include "GeopsyCoreDLLExport.h"
#include "GeoSignal.h"
#include "SortKey.h"
#include "TimeRangeParameters.h"

namespace GeopsyCore {

  class AbstractSignalGroup;
  class MetaDataFieldList;
  class SeismicEvent;

  extern const char * SignalKeyName [];

  class GEOPSYCORE_EXPORT SubSignalPool: protected QList<Signal *>
  {
    TRANSLATIONS( "SubPoolWindow" )
  public:
    SubSignalPool() {}
    SubSignalPool(const SubSignalPool& subPool);
    ~SubSignalPool() {removeAll();}

    // Useful members from QList with public access
    typedef QList<Signal *>::iterator iterator;
    typedef QList<Signal *>::const_iterator const_iterator;
    iterator begin() {return QList<Signal *>::begin();}
    iterator end() {return QList<Signal *>::end();}
    const_iterator begin() const {return QList<Signal *>::begin();}
    const_iterator end() const {return QList<Signal *>::end();}
    SubSignalPool& operator=(const SubSignalPool& o);
    bool operator==(const SubSignalPool& o) const;
    bool operator!=(const SubSignalPool& o) const {return !operator==(o);}
    SubSignalPool copy() const;
    int count() const {return QList<Signal *>::count();}
    int samplesCount();
    bool isEmpty() const {return QList<Signal *>::isEmpty();}
    Signal * at(int index) const {return QList<Signal *>::at(index);}
    Signal * first() const {return QList<Signal *>::first();}
    Signal * last() const {return QList<Signal *>::last();}
    int indexOf(const Signal * sig) const {return QList<Signal *>::indexOf(const_cast<Signal *>(sig));}
    bool contains(const Signal * sig) const;
    QMap<QString,int> signalNames() const;
    int spectraCount() const;

    DateTime firstTimePick(const QString& timePick) const;
    DateTime lastTimePick(const QString& timePick) const;

    void sort() {std::sort(begin(), end(), SortKey::lessThan);}

    const QString& name() const {return _name;}
    void setName(const QString& n) {_name=n;}

    void setHeaderModified(bool b) const;
    bool isHeaderModified() const;
    bool isReadOnlySamples() const;

    TimeRange timeRange() const;
    double maximumFrequency() const;
    QStringList availableTimePicks() const;

    // Signal selection
    void addAll(SignalDatabase * db);
    void addSubPool(const SubSignalPool& subPool);
    void addReceiver(Point point);
    void addSignal(Signal * sig) {append(sig);}
    void addSignal(SignalDatabase * db, int id);
    void addFile(SignalDatabase * db, int fileN);
    void addFile(const SignalFile * sf);
    void addGroup(const AbstractSignalGroup * g);
    void addList(QString signalList);
    void insertSignal(Signal * sig, Signal * refSig=nullptr, bool before=true);
    void remove(Signal * sig);
    void remove(const SubSignalPool& subPool);
    void removeAt(int i);
    void remove(Signal::Components comp);
    void remove(const QStringList& keepSignalNames);
    void removeAll();
    void debugSignalSharing();

    // Saving to file
    static bool isExportFormat(const SignalFileFormat& f);
    bool save(QString filePath, bool useOriginalBaseName, SignalFileFormat format,
              int maximumSignalsPerFile=0, const QString& pickName=QString()) const;
    bool saveGeopsySignal (QString filePath) const;

    // Signal processing
    void subtractValue(double val=0.0);
    void removeTrend();
    void subtractSignal(Signal * bysig);
    void subtractSignals(SubSignalPool * bySubPool);
    void filter(const FilterParameters& param);
    void waveletTransform(const MorletParameters& param);
    void shift(double dt);
    void overSample(double factor);
    void whiten();
    void agc(double width);
    void stddevClip(double factor);
    void multiply(double value);
    bool mergeStations();
    bool merge();
    void stalta(double tsta, double tlta);
    void taper(const TimeRangeParameters& range, const WindowFunctionParameters& param);
    void cut(const TimeRangeParameters& param);
    void correlations(double maxDelay, const Signal * referenceSig=nullptr);
    void normalizedCorrelations(double maxDelay, const Signal * referenceSig=nullptr);
    void convolution(const Signal * referenceSig);
    bool removeInstrumentalResponse(const InstrumentalResponse& sensor);
    void decimateAmplitude(int maxCount, double maxRef);
    void decimateTime(int factor);
    void fastFourierTransform(DoubleSignal::SignalType st);
    void discreteFourierTransform();
    void importTable(QString fileName);
    void exportTable(QString fileName, const MetaDataFieldList& param);
    void unglitch(double threshold);
    int checkDuplicateRays();
    void rotateComponents(Matrix3x3 rotMatrix);
    bool associate3Components();
    QHash<const SeismicEvent *, SubSignalPool *> associateSources();
    SubSignalPool horizontalComponents() const;
    SubSignalPool verticalComponent() const;
    Curve<Point> receivers() const;
    Curve<Point> sources() const;
    int maximumTimePickCount() const;
    bool haveSameSampling(const QString& caption) const;
    bool lockSamples(const QString& caption, const double **samples) const;
    void unlockSamples() const;

    SignalDatabase * database() const;
  protected:
    QString _name;
  private:
    bool exportFilePath(QString& filePath, bool useOriginalBaseName, const SignalFileFormat& f) const;
    bool check3ComponentTriplet(SubSignalPool& triplet);
    inline void prepend(Signal * sig);
    inline void append(Signal * sig);

    bool saveSeg2(QString filePath) const;
    bool saveSeg2Return(bool ret, qint64 * offset) const;
    bool writeSeg2Header(QFile& f) const;
    bool writeSeg2AlignTextHeader(QFile& f, qint16 blocksize) const;
    bool writeSeg2TextHeader(QFile& f, const QString& keyword,
                             const QString& sep, const QString& value,
                             qint16& blocksize) const;
    bool writeSeg2TextHeader(QFile& f, double& delay) const;
    bool writeSeg2Offsets(QFile& f, qint64 *offset) const;
    bool saveArrivalTimes(QString filePath, const QString& pickName) const;
    bool saveAsciiOneColumn(QString filePath) const;
    bool saveAsciiMultiColumns(QString filePath) const;
    bool saveSaf(QString filePath) const;
    bool saveOneSaf(QString filePath) const;
    bool saveSac(QString filePath, bool useOriginalBaseName, QDataStream::ByteOrder bo) const;
    bool saveCityShark2(QString filePath) const;
    bool saveSu(QString filePath, QDataStream::ByteOrder bo) const;
    bool saveSegY(QString filePath, QDataStream::ByteOrder bo) const;
    bool saveWav(QString filePath) const;
    bool saveGse2(QString filePath) const;
    bool saveMiniSeed(QString filePath) const;

    mutable QSet<const Signal *> _lookup;
  };

  inline void SubSignalPool::append(Signal * sig)
  {
    TRACE;
    if(sig) {
      sig->addReference();
      QList<Signal *>::append(sig);
    }
  }

  inline void SubSignalPool::prepend(Signal * sig)
  {
    TRACE;
    if(sig) {
      sig->addReference();
      QList<Signal *>::prepend(sig);
    }
  }

} // namespace GeopsyCore

#endif // SUBSIGNALPOOL_H
