// Copyright (C) 2010, Guy Barrand. All rights reserved.
// See the file tools.license for terms.

#ifndef tools_data_axis
#define tools_data_axis

#include "mathf"

namespace tools {

class data_axis {
public:
  data_axis():m_min_value(0),m_max_value(0),m_is_log(false){}
  virtual ~data_axis(){}
public:
  data_axis(const data_axis& a_from)
  :m_min_value(a_from.m_min_value)
  ,m_max_value(a_from.m_max_value)
  ,m_is_log(a_from.m_is_log)
  {}
  data_axis& operator=(const data_axis& a_from){
    m_min_value = a_from.m_min_value;
    m_max_value = a_from.m_max_value;
    m_is_log = a_from.m_is_log;
    return *this;
  }
public:
  bool set_is_log(bool a_v) {if(m_is_log==a_v) return false;m_is_log = a_v;return true;}
  bool set_min_value(float a_v) {if(m_min_value==a_v) return false;m_min_value = a_v;return true;}
  bool set_max_value(float a_v) {if(m_max_value==a_v) return false;m_max_value = a_v;return true;}
  float min_value() const {return m_min_value;}
  float max_value() const {return m_max_value;}
  bool is_log() const {return m_is_log;}

  void adjust() { //from hippodraw.
    int _axis = 0;
    float step;
    float mylow, myhigh;
    int N_NICE = 4;
    static const float nice[/*N_NICE*/4] = { 1.0,2.0,2.5,5.0 };

    if (m_min_value > m_max_value) {
      float low = m_min_value;
      m_min_value = m_max_value;
      m_max_value = low;
    } else if (m_min_value == m_max_value) {
      float value = m_min_value;
      m_min_value = value - 1;
      m_max_value = value + 1;
      return;
    }

    //if (m_steps <= 0) { //if letting the if and m_steps as a field, twice this function do not give the same result.
      _axis    = 1;
    unsigned int m_steps = 10;
    //}

    // Round the "bin width" to a nice number.
    // If this is being done for an axis (ie m_steps was 0 , then
    // we don't have to go > *m_max_value.
    //
    float w = (m_max_value - m_min_value)/((float)m_steps);
    float mag = ffloor(flog10(w));
    int i = 0;
    do {
      step   = nice[i] * fpow(10.0,mag);
      mylow  = ffloor(m_min_value/step) * step;
    //myhigh = _axis==1 ? fceil(m_max_value/step) * step : mylow + step * m_steps;
      myhigh = fceil(m_max_value/step) * step; //quiet Coverity.
      i++;
      if (i>=N_NICE) {i = 0;mag++;}
    }
    while ( ( (_axis==1) && (myhigh < m_max_value)) ||
            ( (_axis==0) && (myhigh <= m_max_value)) );

    float range = myhigh - mylow;

    // we now have decided on a range. Try to move
    // m_min_value/m_max_value a little
    // to end up on a nice number.
    //
    // first check if either end is near 0.0
    if ( !m_is_log && (m_min_value >= 0.0) &&
        (( (_axis==1) && (range>=m_max_value) ) ||
         ( (_axis==0) && (range>m_max_value) )) ) {
      m_min_value = 0.0;
      m_max_value = range;
      return;
    }

    if ( (( (_axis==1) && (m_max_value<=0.0) ) ||
          ( (_axis==0) && (m_max_value<0.0) ))
         && (-range<=m_min_value)) {
      m_max_value = 0.0;
      m_min_value = -range;
      return;
    }

    // try to round *m_min_value.
    // correction
    if( m_is_log && (m_min_value<=0.0)) m_min_value = 1.0;

    i   = N_NICE-1;
    mag = myhigh != 0.0 ? fceil(flog10(ffabs(myhigh))) : fceil(flog10(ffabs(mylow)));

    do {
      step   = nice[i] * fpow(10.0,mag);
      mylow  = ffloor(m_min_value/step) * step;
      myhigh = mylow + range;
      i--;
      if (i<0) {
        i = N_NICE-1;
        mag--;
      }
    }
    while (( m_is_log && (mylow  <= 0.0)     ) ||
           ( (_axis==1)  && (myhigh < m_max_value)  ) ||
           ( (_axis==0)  && (myhigh <= m_max_value) ) );

    m_min_value = mylow;
    m_max_value = myhigh;
  }
protected:
  float m_min_value;
  float m_max_value;
  //int m_steps;
  bool m_is_log;
};

}

#endif
