/***************************************************************************
**
**  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-03-07
**  Copyright: 2019
**    Marc Wathelet (ISTerre, Grenoble, France)
**
***************************************************************************/

#include "SPACRing.h"

namespace ArrayCore {

  /*!
    \class SPACRing SPACRing.h
    \brief Brief description of class still missing

    Full description of class still missing
  */

  /*!
    Description of constructor still missing
  */
  SPACRing::SPACRing()
    : AutocorrRing()
  {
    TRACE;
  }

  /*!
    Description of constructor still missing
  */
  SPACRing::SPACRing(double minR, double maxR)
    : AutocorrRing(minR, maxR)
  {
    TRACE;
  }

  /*!
    Description of destructor still missing
  */
  SPACRing::~SPACRing()
  {
    TRACE;
  }

  bool SPACRing::lessThanDistance(const StationPair * p1,
                                  const StationPair * p2)
  {
    return p1->distance()<p2->distance();
  }

  bool SPACRing::lessThanAzimuth(const StationPair * p1,
                                 const StationPair * p2)
  {
    return p1->azimuth()<p2->azimuth();
  }

  void SPACRing::add(const StationPair * p)
  {
    TRACE;
    if(p->distance()>maxRadius()) {
      setMaxRadius(p->distance());
    }
    _pairs.append(p);
  }

  void SPACRing::setPairs(const QVector<StationPair>& stationPairs)
  {
    TRACE;
    _pairs.clear();
    int nPairs=stationPairs.count(); // Total number of pairs available
    for(int i=0;i<nPairs; i++) {
      const StationPair& p=stationPairs.at(i);
      if(p.distance()>=minRadius() && p.distance()<=maxRadius()) {
        _pairs.append(&p);
      }
    }
  }

  /*!
    Remove first pair (smallest distance, they are ordered)
  */
  void SPACRing::removeFirst()
  {
    _pairs.removeFirst();
    setMinRadius(_pairs.first()->distance());
  }

  /*!
    Calculate max angle step, eventually without counting the \a startIndex first pairs.
  */
  double SPACRing::maximumAngleStep(int startIndex) const
  {
    TRACE;
    double maxAngle=0.0;
    QList<const StationPair *> pairs=_pairs;
    for(int i=0; i<startIndex; i++) {
      pairs.removeFirst();
    }
    if(pairs.count()<2) {
      return 180.0;
    }
    std::sort(pairs.begin(), pairs.end(), lessThanAzimuth);

    double a;
    for(int i=pairs.count()-2; i>=0; i--) {
      a=pairs.at(i+1)->azimuth().degrees()-pairs.at(i)->azimuth().degrees();
      if(a>maxAngle) {
        maxAngle=a;
      }
    }
    a=pairs.first()->azimuth().degrees()-(pairs.last()->azimuth().degrees()-180.0);
    if(a>maxAngle) {
      maxAngle=a;
    }
    return maxAngle;
  }

  /*!
    Return the maximum relative overlap between two rings.
  */
  double SPACRing::overlap(const SPACRing& o) const
  {
    TRACE;
    if(minRadius()<o.minRadius()) {
      if(o.minRadius()<maxRadius()) {
        if(o.maxRadius()<maxRadius()) {
          return 1.0;
        } else {
          if(thickness()<o.thickness()) {
            return (maxRadius()-o.minRadius())/thickness();
          } else {
            return (maxRadius()-o.minRadius())/o.thickness();
          }
        }
      } else {
        return 0.0;
      }
    } else {
      if(minRadius()<o.maxRadius()) {
        if(maxRadius()<o.maxRadius()) {
          return 1.0;
        } else {
          if(thickness()<o.thickness()) {
            return (o.maxRadius()-minRadius())/thickness();
          } else {
            return (o.maxRadius()-minRadius())/o.thickness();
          }
        }
      } else {
        return 0.0;
      }
    }
  }

  /*!
    Pairs are ordered by increasing distance.
    They are added one by one checking the angle step.
  */
  QVector<SPACRing> SPACRing::autoRings(const QVector<StationPair>& pairs,
                                        double maxAngleStep,
                                        double maxRelThickness,
                                        int minPairCount,
                                        double maxOverlap)
  {
    TRACE;
    QVector<SPACRing> rings;
    int n=pairs.count();
    if(n>=2) {
      SPACRing ring;
      for(int i=0; i<n; i++) {
        ring.add(&pairs.at(i));
        if(ring.maximumAngleStep()<=maxAngleStep) { // Yeah, found at least a collection of pairs
                                                    // satifying the maximum angle step condition
          // Try to remove as many pairs as possible still satifying the angle step condition
          while(ring.maximumAngleStep(1)<=maxAngleStep) {
            ring.removeFirst();
          }
          // If the ring is sufficiently thin and contains enough pair, accept it
          if(ring.thickness()<=maxRelThickness*ring.averageRadius() &&
             ring.count()>=minPairCount) {
            ring.round(0.01);
            rings.append(ring);
          }
          ring.removeFirst();
        }
      }
    }
    // Check overlap
    for(int i=1; i<rings.count(); i++) {
      if(rings.at(i).overlap(rings.at(i-1))>maxOverlap) {
        if(rings.at(i).thickness()<rings.at(i-1).thickness()) {
          rings.removeAt(i-1);
          i--;
        } else {
          rings.removeAt(i);
        }
      }
    }
    return rings;
  }

} // namespace ArrayCore

