/***************************************************************************
**
**  This file is part of DinverDCCore.
**
**  DinverDCCore 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.
**
**  DinverDCCore 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-04-02
**  Copyright: 2010-2019
**    Marc Wathelet (LGIT, Grenoble, France)
**
***************************************************************************/

#include "TargetList2D.h"
#include "Param3DGroundModel.h"
#include "ParamGroundModel.h"
#include "ParamProfile.h"

namespace DinverDCCore {

/*!
  \class TargetList2D TargetList2D.h
  \brief A 9-node target

  1D target are defined for 9 nodes
*/

const QString TargetList2D::xmlTargetList2DTag="TargetList2D";

/*!
  Description of constructor still missing
*/
TargetList2D::TargetList2D()
    : XMLClass()
{
  TRACE;
}

/*!
  Description of constructor still missing
*/
TargetList2D::TargetList2D(const TargetList2D& o)
    : XMLClass()
{
  TRACE;
  qDeleteAll(_targets);
  _targets.clear();
  for(QVector<TargetList *>::const_iterator it=o._targets.begin(); it!=o._targets.end(); it++) {
    _targets.append(new TargetList(**it));
  }
}

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

void TargetList2D::addTarget(TargetList * t)
{
  TRACE;
  _targets.append(t);
}

void TargetList2D::xml_writeChildren(XML_WRITECHILDREN_ARGS) const
{
  TRACE;
  for(QVector<TargetList *>::const_iterator it=_targets.begin(); it!=_targets.end(); it++) {
    (*it)->xml_save(s, context);
  }
}

XMLMember TargetList2D::xml_member(XML_MEMBER_ARGS)
{
  TRACE;
  Q_UNUSED(context)
  Q_UNUSED(attributes)
  if(tag=="TargetList" ) {
    _targets.append(new TargetList);
    return XMLMember(_targets.last());
  }
  return XMLMember(XMLMember::Unknown);
}

/*!
*/
bool TargetList2D::setParamProfiles(Param3DGroundModel * gm, RealSpace& ps)
{
  TRACE;
  ASSERT(gm->modelCount()==3);
  for(int i=0; i<3; i++) {
    ParamGroundModel * m=gm->model(i);
    _vp[i]=m->find( "Vp" );
    _vs[i]=m->find( "Vs" );
    _rho[i]=m->find( "Rho" );
    if(_vp[i] && _vs[i]) {
      _nu[i]=m->find( "Nu" );
      ASSERT(_nu[i] && _nu[i]->type()==ParamProfile::Condition);
      _poissonCondition[i]=new PoissonCondition(_vp[i], _vs[i], _nu[i] );
      ps.addCondition(_poissonCondition[i]);
    }
    _planeEngine.setReference(i, m->position());
    _models.insert(m, i);
  }
  if(!_planeEngine.setNormalVector()) {
    return false;
  }

  // Check if number of layers is the same for all models
  for(int i=1; i<3; i++) {
    if(_vp[i]->nLayers()!=_vp[i-1]->nLayers() ||
       _nu[i]->nLayers()!=_nu[i-1]->nLayers() ||
       _vs[i]->nLayers()!=_vs[i-1]->nLayers() ||
       _rho[i]->nLayers()!=_rho[i-1]->nLayers()) {
      App::log(tr("Numer of layers must be the same for all reference models\n") );
      return false;
    }
  }
  return true;
}

void TargetList2D::setVp()
{
  for(int i=1; i<3; i++) {
    _poissonCondition[i]->setVp();
  }
}

/*!
  \a nDimensions is number of free parameters. This is used only if Akaike misfit is computed.
*/
double TargetList2D::misfit(int nDimensions, bool& ok)
{
  TRACE;
  double totalMisfit=0;
  double totalWeight=0;
  Seismic1DModel * m=0;
  int n=_targets.count();
  for(int i=0; i<n; i++) {
    TargetList * tl=_targets.at(i);
    Profile vp, nuMin, nuMax, vs, rho, rvp, rnuMin, rnuMax, rvs, rrho;
    // Construction of vp, vs and rho profiles at target position
    interpole(tl->position(), _vp[0]->rawProfile(), _vp[1]->rawProfile(), _vp[2]->rawProfile(), vp);
    interpole(tl->position(), _nu[0]->minRawProfile(), _nu[1]->minRawProfile(), _nu[2]->minRawProfile(), nuMin);
    interpole(tl->position(), _nu[0]->maxRawProfile(), _nu[1]->maxRawProfile(), _nu[2]->maxRawProfile(), nuMax);
    interpole(tl->position(), _vs[0]->rawProfile(), _vs[1]->rawProfile(), _vs[2]->rawProfile(), vs);
    interpole(tl->position(), _rho[0]->rawProfile(), _rho[1]->rawProfile(), _rho[2]->rawProfile(), rho);
    // Merge profile sampling
    QVector<double> depths;
    vp.collectDepths(depths);
    nuMin.collectDepths(depths);
    nuMax.collectDepths(depths);
    vs.collectDepths(depths);
    rho.collectDepths(depths);
    /*QVector<double>::iterator itD;
    for(itD=depths.begin();itD!=depths.end();itD++) {
      printf("Before sort %lg\n",*itD);
    }*/
    std::sort(depths.begin(), depths.end());
    unique(depths);
    /*for(itD=depths.begin();itD!=depths.end();itD++) {
      printf("After unique %lg\n",*itD);
    }*/
    rvp.resample(vp, depths);
    rnuMin.resample(nuMin, depths);
    rnuMax.resample(nuMax, depths);
    rvs.resample(vs, depths);
    rrho.resample(rho, depths);
    m=DCReportBlock::surfaceWaveModel(rvp.depths(), rvp.values(), rvs.values(), rrho.values(),
                                        &rnuMin.values(), &rnuMax.values());
    tl->surfaceMisfit(totalMisfit, totalWeight, m, nDimensions, ok);
    delete m;
    if(!ok) return 0.0;
    if(i==0) {
      _reportVp=rvp;
      _reportVs=rvs;
      _reportRho=rrho;
    }
  }
  if(totalWeight > 0) return totalMisfit/totalWeight;
  else return totalMisfit;
}

void TargetList2D::interpole(const Point& at, const Profile& p1, const Profile& p2, const Profile& p3, Profile& p)
{
  TRACE;
  int n=p1.count();
  p.resize(n);
  for(int i=0;i<n;i++) {
    _planeEngine.setZ(0, p1.depths()[i]);
    _planeEngine.setZ(1, p2.depths()[i]);
    _planeEngine.setZ(2, p3.depths()[i]);
    _planeEngine.setNormalVectorXY();
    p.setDepth(i, _planeEngine.z(at));
    _planeEngine.setZ(0, p1.values()[i]);
    _planeEngine.setZ(1, p2.values()[i]);
    _planeEngine.setZ(2, p3.values()[i]);
    _planeEngine.setNormalVectorXY();
    p.setValue(i, _planeEngine.z(at));
  }
}

void TargetList2D::writeReport(ReportWriter * report) const
{
  TRACE;
  DCReportBlock::write(report, *_targets[0], &_reportVp, &_reportVs, &_reportRho);
}


void TargetList2D::humanInfo() const
{
  TRACE;
  App::log(tr( "\n---------------------- Positions of targets\n\n"));
  int n=_targets.count();
  for(int i=0; i<n; i++) {
    TargetList * tl=_targets.at(i);
    App::log(tl->position().toString()+"\n");
  }
}

} // namespace DinverDCCore
