/***************************************************************************
**
**  This file is part of QGpCoreMath.
**
**  This library is free software; you can redistribute it and/or
**  modify it under the terms of the GNU Lesser General Public
**  License as published by the Free Software Foundation; either
**  version 2.1 of the License, or (at your option) any later version.
**
**  This file 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 Lesser General Public
**  License for more details.
**
**  You should have received a copy of the GNU Lesser General Public
**  License along with this library; if not, write to the Free Software
**  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
**
**  See http://www.geopsy.org for more information.
**
**  Created: 2007-11-12
**  Copyright: 2007-2019
**    Marc Wathelet
**    Marc Wathelet (LGIT, Grenoble, France)
**
***************************************************************************/

#include "AbstractFunction.h"
#include "FunctionSearch.h"

namespace QGpCoreMath {

  /*!
    \class AbstractFunction AbstractFunction.h
    \brief Abstract 2D function

    This function is used by GridSearch, ConcentricSearch and DirectionalSearch. Values are required for
    gridded (x,y) couples and for free couples. For gridded couples, computation of values can be cached.
  */

  /*!
    \fn AbstractFunction()
    Default constructor
  */

  /*!
    \fn virtual ~AbstractFunction()
    Default destructor
  */

  /*!
    \fn double AbstractFunction::value(double x, double y) const
    Implemement this function to calculate the 2D function at \a x and \a y.
  */

  /*!
    \fn double AbstractFunction::value(double x, double y, int index) const
    Re-mplemement this function to calculate the 2D function for \a x and \a y aligned to a grid.
    \a x and \a y correspond to \a index in the grid: \a index=iy * nx + ix

    If nothing is cached re-implementation of this function is useless.
  */

  /*!
    \fn bool AbstractFunction::initGrid(int n)
    Re-implemement if the computation of value(double x, double y) requires caching for gridded couples (x,y).
    \a n is the total number of grid points: nx*ny.
    Return true if initialization of values is required.
  */

  /*!
    \fn void AbstractFunction::initGrid(double x, double y, int index)
    Re-implemement if the computation of value(double x, double y) requires caching for gridded couples (x,y).
    This fonction initialize the cached values for \a x and \a y aligned to a grid and corresponding to \a index.
  */

  /*!
    Information storage for local maxima
  */
  FunctionSearchMaximum * AbstractFunction::createMaximum(double val, const Point& pos) const
  {
    return new FunctionSearchMaximum(val, pos);
  }

  /*!
    Return the maximum value along axis \a a at \a p.
    \a p is updated with the value providing the maximum.
  */
  double AbstractFunction::value2D(Point& p, AxisType a) const
  {
    double max=thirdMaximum();
    double step=thirdStep();
    double val, maxVal=-std::numeric_limits<double>::infinity(), maxX=0.0;
    switch(a) {
    case XAxis:
      for(double x=thirdMinimum(); x<=max; x+=step) {
        p.setX(x);
        val=value(p);
        if(val>maxVal) {
          maxVal=val;
          maxX=x;
        }
      }
      p.setX(maxX);
      break;
    case YAxis:
      for(double y=thirdMinimum(); y<=max; y+=step) {
        p.setY(y);
        val=value(p);
        if(val>maxVal) {
          maxVal=val;
          maxX=y;
        }
      }
      p.setY(maxX);
      break;
    case ZAxis:
      for(double z=thirdMinimum(); z<=max; z+=step) {
        p.setZ(z);
        val=value(p);
        if(val>maxVal) {
          maxVal=val;
          maxX=z;
        }
      }
      p.setZ(maxX);
      break;
    }
    return maxVal;
  }

  double AbstractFunction::vectorLength(Point v, AxisType a)
  {
    switch(a) {
    case XAxis:
      v.setX(v.y());
      v.setY(v.z());
      v.setZ(0.0);
      break;
    case YAxis:
      v.setY(v.z());
      v.setZ(0.0);
      break;
    case ZAxis:
      v.setZ(0.0);
      break;
    }
    return v.length();
  }

  double AbstractFunction::vectorDirection(Point v, AxisType a)
  {
    switch(a) {
    case XAxis:
      v.setX(v.y());
      v.setY(v.z());
      v.setZ(0.0);
      break;
    case YAxis:
      v.setY(v.z());
      v.setZ(0.0);
      break;
    case ZAxis:
      v.setZ(0.0);
      break;
    }
    if(v.length()>0.0) {
      return Angle::mathToGeographic(v.azimuth());
    } else {
      return 0.0;
    }
  }

} // namespace QGpCoreMath
