/*------------------------------------------------------------------------------
 src/lib/dj/math.cxx
 
 Copyright (c) 2004-2006 Darby Johnston.
 All rights reserved.
 Use is subject to license terms.
------------------------------------------------------------------------------*/

#include "djMath.h"
#include <math.h>

namespace dj {

int pow(int in, int power) { return floor(::pow(double(in), double(power))); }
float pow(float in, float power) { return ::powf(in, power); }
double pow(double in, double power) { return ::pow(in, power); }

float sqrt(float in) { return ::sqrtf(in); }
double sqrt(double in) { return ::sqrt(in); }

float exp(float in) { return ::expf(in); }
double exp(double in) { return ::exp(in); }

float log(float in) { return ::logf(in); }
double log(double in) { return ::log(in); }
float log10(float in) { return ::log10(in); }
double log10(double in) { return ::log10(in); }
float antiLog10(float in) { return expf(in * log(10.0f)); }
double antiLog10(double in) { return exp(in * log(10.0)); }

int wrap(int in, int min, int max)
{
  return min + mod(in - min, max - min + 1);
}

int step(int in, int v) { return in <= v; }

int pulse(int in, int min, int max)
{
  return step(min, in) - step(max, in);
}

float bias(float in, float bias)
{
  return pow(in, log(bias) / log(0.5f));
}

double bias(double in, double bias)
{
  return pow(in, log(bias) / log(0.5));
}

float gain(float in, float gain)
{
  if (in < almostZeroF) return 0.0f;
  else if (in > almostOneF) return 1.0f;
  const float tmp = log(1.0f - gain) / log(0.5f);
  if (in < 0.5f) return pow(2.0f * in, tmp) / 2.0f;
  else return 1.0f - pow(2.0f * (1.0f - in), tmp) / 2.0f;
}

double gain(double in, double gain)
{
  if (in < almostZero) return 0.0;
  else if (in > almostOne) return 1.0;
  const double tmp = log(1.0 - gain) / log(0.5);
  if (in < 0.5) return pow(2.0 * in, tmp) / 2.0;
  else return 1.0 - pow(2.0 * (1.0 - in), tmp) / 2.0;
}

float softClip(float in, float value)
{
  const float tmp = 1.0f - value;
  if (in > tmp) in = tmp + (1.0f - exp(-(in - tmp) / value)) * value;
  return in;
}

double softClip(double in, double value)
{
  const double tmp = 1.0 - value;
  if (in > tmp) in = tmp + (1.0 - exp(-(in - tmp) / value)) * value;
  return in;
}

double smoothStep(double in, double min, double max)
{
  return lerp(in * in * (3.0 - (2.0 * in)), min, max);
}

double sin(double in) { return ::sin(in); }
double cos(double in) { return ::cos(in); }
double arcSin(double in) { return ::asin(in); }
double arcCos(double in) { return ::acos(in); }
double sec(double in)
{
  const double tmp = cos(in);
  
  // XXX What should really happen with zero?
  //
  if (isZero(tmp)) return 0.0;
  
  return 1.0 / tmp;
}
double coSec(double in)
{
  const double tmp = sin(in);
  
  // XXX What should really happen with zero?
  //
  if (isZero(tmp)) return 0.0;
  
  return 1.0 / tmp;
}
double tan(double in) { return ::tan(in); }
double coTan(double in)
{
  const double tmp = tan(in);
  
  // XXX What should really happen with zero?
  //
  if (isZero(tmp)) return 0.0;
  
  return 1.0 / tmp;
}
double arcTan(double in) { return ::atan(in); }
double arcTan(double a, double b) { return ::atan2(a, b); }

bool isZero(double in) { return abs(in) < almostZero; }
bool compare(double a, double b) { return isZero(a - b); }

int ceil(double in)
{
  //return ::ceil(in);
  const int tmp = int(in);
  return tmp + (in > 0 && ! compare(in, tmp));
}

int floor(double in)
{
  //return ::floor(in);
  const int tmp = int(in);
  return tmp - (in < 0 && ! compare(in, tmp));
}

double fraction(double in) { return in - int(in); }

double degreesToRadians(double in) { return in * 0.017453; }
double radiansToDegrees(double in) { return in * 57.29578; }

// XXX Left vs. right handed coordinates?
//
void sphericalToCartesian(
  double latitude,
  double longitude,
  double distance,
  double * x, double * y, double * z
) {
  DJ_ASSERT(x);
  DJ_ASSERT(y);
  DJ_ASSERT(z);
  const double ls = sin(latitude);
  *x = distance * ls * cos(longitude);
  *y = distance * ls * sin(longitude);
  *z = distance * cos(latitude);
}
void cartesianToSpherical(
  double x, double y, double z,
  double * latitude,
  double * longitude,
  double * distance
) {
  DJ_ASSERT(latitude);
  DJ_ASSERT(longitude);
  DJ_ASSERT(distance);
  *distance = sqrt(x * x + y * y + z * z);
  if (! isZero(*distance)) *latitude = arcCos(z / *distance);
  else *latitude = 0.0;
  if (! isZero(x)) *longitude = arcTan(y / x);
  else *longitude = 0.0;
}

}
