/***************************************************************************
**
**  This file is part of DinverCore.
**
**  DinverCore 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.
**
**  DinverCore 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: 2009-05-06
**  Copyright: 2009-2019
**    Marc Wathelet
**    Marc Wathelet (LGIT, Grenoble, France)
**
***************************************************************************/

#include <QGpCoreTools.h>

#include "AbstractForward.h"
#include "ReportWriter.h"

namespace DinverCore {

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

  Full description of class still missing
*/

/*!
  Copy current values from \a o. Used during the initialization of several
  parallel forward threads (intialized with the same values).
*/
void AbstractForward::copyValues(const AbstractForward& o)
{
  TRACE;
  int ndVar=_parameterSpace.variableParameterCount();
  for(int i=0; i < ndVar; i++ ) {
    Parameter * p=_parameterSpace.variableParameter(i);
    p->setRealValue(o.parameterSpace().variableParameter(i)->realValue());
  }
  valueChanged();
}

/*!
  Provided for convenience, it calls misfit(double *, bool) to compute misfit from a vector
  of parameter values. Reimplement this function if you do not need values under this
  format (direct access to parameter space).
*/
double AbstractForward::misfit(bool& ok)
{
  int ndAll=_parameterSpace.allParameterCount();
  double model[ndAll];
  for(int i=0;i<ndAll;i++) {
    model[i]=_parameterSpace.parameter(i)->realValue();
  }
  return misfit(model, ok);
}

/*!
  Init all random walks by generating the very first model.
  The forward thread must be in 'Running' state.
*/
bool AbstractForward::firstModel(Random& randomNumbers, AtomicBoolean& terminated)
{
  TRACE;
  // Generate very first model
  // Set random values for parameters without checking conditions
  int ndVar=_parameterSpace.variableParameterCount();
  for(int i=0; i < ndVar; i++ ) {
    Parameter * p=_parameterSpace.variableParameter(i);
    p->setGridValue(randomNumbers.uniform(0, p->gridCount()) );
  }
  App::log(tr("   The current model is probably not yet inside the parameter space.\n"
              "   Some warnings here can be ignored.\n"));
  // Some parametrizations may need some updates before proceeding
  valueChanged();

  // Iterate untill finding a valid model (fussy parameters conditions are not
  // handled because the current model may not be totally valid)
  int nWalks=1;
  int nWalksDiagnostic=64;
  int minp, maxp;
  SAFE_UNINITIALIZED(minp, 0);
  SAFE_UNINITIALIZED(maxp, 0);
  while(!_parameterSpace.isOk()) {
    if(nWalks==nWalksDiagnostic) {
      App::log(tr("   Random model generated from scratch: not possible to find a good model after %1 walks.\n").arg(nWalks) );
      _parameterSpace.conditionDiagnostic();
      nWalksDiagnostic*=2;
    }
    nWalks++;
    for(int i=0;i<ndVar; i++) {
      Parameter * p=_parameterSpace.variableParameter(i);
      p->getGridLimits(minp, maxp);
      //double dmin, dmax;
      //p->getRectangularLimits(dmin, dmax);
      //p->getLimits(dmin, dmax);
      //printf("Parameter %s from %lf to %lf",p->name().toLatin1().data(), dmin, dmax);
      if(maxp<=minp) { // Forced to a fixed parameter, break it, back to initial range
        //printf("Back to original range for parameter %s\n",p->name().toLatin1().data());
        minp=0;
        maxp=p->gridCount();
      }
      p->setGridValue(randomNumbers.uniform(minp, maxp));
      //printf("  ---> %lf\n", p->realValue());
      // Some parametrizations may need some updates before proceeding
      valueChanged(p);
    }
    //_parameterSpace.printModel();
    if(terminated.value()) {
      return false;
    }
  }
  //unlock();
  App::log(tr("   Random model generated from scratch: perfomed %1 random walk(s)\n").arg(nWalks) );
  //printModel();
  return true;
}

} // namespace DinverCore
