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

#include "DuplicateRays.h"
#include "TimePick.h"
#include "MetaDataFactory.h"

namespace GeopsyCore {

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

    Full description of class still missing

    TODO: move this class to the tomography tool
  */

  QStringList DuplicateRays::_names;

  REGISTER_METADATA(DuplicateRays)

  /*!
    \fn DuplicateRays::DuplicateRays()

    Internal SubSignalPool is stored as a pointer to avoid useless allocation
    when this class is used only be the MetaData factory. Under Mac SnowLeopard
    the particular order of library initialization generated a crash upon deleting
    of this object by the factory.
  */

  /*!
    \fn DuplicateRays::~DuplicateRays()

    Deletion of internal SubSignalPool.
  */

  QStringList DuplicateRays::subNames() const
  {
    if(_names.isEmpty()) {
      _names << "DuplicateRays"             // Is there any rays defined?
             << "DuplicateRaysId"           // Ray id
             << "DuplicateAverage"          // Average travel time on ray for TimePick[index]
             << "DuplicateStdErr";          // Std deviation for travel time on ray for TimePick[index]
    }
    return _names;
  }

  QStringList DuplicateRays::indexList(int subId) const
  {
    switch(subId) {
    case 0:
    case 1:
      return QStringList();
    default:
      return TimePick::registeredNames();
    }
  }

  bool DuplicateRays::isArray(int subId) const
  {
    switch(subId) {
    case 0:
    case 1:
      return false;
    default:
      return true;
    }
  }

  int DuplicateRays::count(int subId) const
  {
    switch(subId) {
    case 0:
    case 1:
      return 1;
    default:
      return TimePick::registeredNames().count();
    }
  }

  bool DuplicateRays::hasIndex(int subId) const
  {
    switch(subId) {
    case 0:
    case 1:
      return false;
    default:
      return true;
    }
  }

  bool DuplicateRays::hasIndex(int subId, const QString &index) const
  {
    switch(subId) {
    case 0:
    case 1:
      return false;
    default:
      if(!_rays || _rays->isEmpty()) {
        return false;
      } else {
        return _rays->first()->metaData<TimePick>().hasIndex(subId, index);
      }
    }
  }

  QString DuplicateRays::name(int subId, const QString& index) const
  {
    switch(subId) {
    case 0:
    case 1:
      return _names[subId];
    default:
      return _names[subId]+"[\""+index+"\"]";
    }
  }

  QString DuplicateRays::baseName(int subId) const
  {
    return _names[subId];
  }

  QString DuplicateRays::title(int subId, const QString& index) const
  {
    switch(subId) {
    case 0:
      return tr("Duplicate rays?");
    case 1:
      return tr("Dup. rays id");
    case 2:
      return tr("Dup. rays mean[\"%1\"]").arg(index);
    default:
      return tr("Dup. rays err[\"%1\"]").arg(index);
    }
  }

  QVariant DuplicateRays::data(int subId, const QString& index) const
  {
    switch(subId) {
    case 0:
      return (!_rays || _rays->isEmpty()) ? true : false;
    case 1:
      return 0;
    case 2:
      return average(index).toString();
    default:
      return standardDeviation(index);
    }
  }

  void DuplicateRays::addRay(Signal * sig)
  {
    TRACE;
    if(!_rays) {
      _rays=new SubSignalPool;
    }
    if(!_rays->contains(sig)) {
      _rays->addSignal(sig);
    }
  }

  /*!
    Returns the average of time pick \a index for all duplicates of this ray (if any)
  */
  DateTime DuplicateRays::average(const QString& index) const
  {
    TRACE;
    if(!_rays || _rays->isEmpty()) {
      return DateTime::null;
    } else {
      SubSignalPool::const_iterator it=_rays->begin();
      DateTime t0=(*it)->metaData<TimePick>().value(index);
      double average=0.0;
      for(it++; it!=_rays->end(); ++it) {
        average+=t0.secondsTo((*it)->metaData<TimePick>().value(index));
      }
      average/=_rays->count();
      t0.addSeconds(average);
      return t0;
    }
  }

  /*!
    Returns the standard deviation of time pick \a index for all duplicates of this ray (if any)
  */
  double DuplicateRays::standardDeviation(const QString& index) const
  {
    TRACE;
    if(!_rays || _rays->isEmpty()) {
      return 0.0;
    } else {
      DateTime mean=average(index);
      double tmp, res=0.0;
      for(SubSignalPool::const_iterator it=_rays->begin(); it!=_rays->end(); ++it) {
        tmp=mean.secondsTo((*it)->metaData<TimePick>().value(index));
        res+=tmp*tmp;
      }
      res=::sqrt(res/_rays->count());
      return res;
    }
  }

  /*!
    Used by Signal::compare() to sort signal in viewers.
  */
  int DuplicateRays::compare(int subId, const QString& index, const MetaData& o) const
  {
    const DuplicateRays& ro=reinterpret_cast<const DuplicateRays&>(o);
    switch(subId) {
    case 0: {
        int v1=(!_rays || _rays->isEmpty()) ? 0 : 1;
        int v2=(!ro._rays || ro._rays->isEmpty()) ? 0 : 1;
        if(v1<v2) return -1;
        else if(v1>v2) return 1;
        else return 0;
      }
    case 1: { // ID is not clear...
        return 0;
      }
    case 2: {
        DateTime v1=average(index);
        DateTime v2=ro.average(index);
        if(v1<v2) return -1;
        else if(v1>v2) return 1;
        else return 0;
      }
    default: {
        double v1=standardDeviation(index);
        double v2=ro.standardDeviation(index);
        if(v1<v2) return -1;
        else if(v1>v2) return 1;
        else return 0;
      }
    }
  }

  bool DuplicateRays::operator==(const MetaData& o) const
  {
    const DuplicateRays& ro=reinterpret_cast<const DuplicateRays&>(o);
    if(!_rays) {
      if(!ro._rays) {
        return true;
      }
      return false;
    }
    if(!ro._rays) {
      return false;
    }
    return *_rays==*ro._rays;
  }

} // namespace GeopsyCore
