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

#ifndef tools_sg_axis
#define tools_sg_axis

#include "node"
#include "line_style"
#include "text_style"
#include "enums"
#include "noderef"
#include "vertices"
#include "draw_style"
#include "rgba"
#include "normal"
#include "separator"
#include "tools"
#include "nodekit"

#include "../lina/vec3f"
#include "../mnmx"
#include "../hplot"

#include <cstdio> //sprintf
#include <cstring> //strcpy

namespace tools {
namespace sg {

class axis : public node {
public:
  TOOLS_NODE(axis,tools::sg::axis,node)
public:
  sf<float> width;
  sf<float> minimum_value;
  sf<float> maximum_value;
  sf<unsigned int> divisions;
  sf_string modeling; //hippo, hplot
  sf<bool> is_log;
  // If modeling is hippo or hplot,
  // labels_enforced true let labels be an input field.
  sf<bool> labels_enforced;
  sf<bool> tick_up;
  sf<float> tick_length;

  // NOTE : if modeling is none,the below are input fields.
  //        If modeling is hippo or hplot, the below are output field
  //        (filled by compute_ticks).
  sf<unsigned int> tick_number; //output
  mf_string labels;       //output
  mf<float> values;       //output //in [minimumValue,maximumValue]
  mf<float> coords;       //output //in [0,width]
  mf<float> sub_coords;   //output
  sf<int> magnitude;      //output

  sf_string title;
  sf<float> title_to_axis;
  sf<float> title_height;
  sf_enum<hjust> title_hjust;

  sf<float> label_to_axis;
  sf<float> label_height;

  sf<bool>  labels_no_overlap_automated;
  sf<float> labels_gap; //in percent of width.

  // time labels only in hplot modeling for the moment.
  sf<bool> time_labels;
  sf_string time_format;
  sf<double> time_offset;
  sf<bool> time_offset_is_GMT;
public:
  virtual const desc_fields& node_desc_fields() const {
    TOOLS_FIELD_DESC_NODE_CLASS(tools::sg::axis)
    static const desc_fields s_v(parent::node_desc_fields(),27, //WARNING : take care of count.
      TOOLS_ARG_FIELD_DESC(width),
      TOOLS_ARG_FIELD_DESC(minimum_value),
      TOOLS_ARG_FIELD_DESC(maximum_value),
      TOOLS_ARG_FIELD_DESC(divisions),
      TOOLS_ARG_FIELD_DESC(modeling),
      TOOLS_ARG_FIELD_DESC(is_log),
      TOOLS_ARG_FIELD_DESC(labels_enforced),
      TOOLS_ARG_FIELD_DESC(tick_up),
      TOOLS_ARG_FIELD_DESC(tick_length),
      TOOLS_ARG_FIELD_DESC(tick_number),
      TOOLS_ARG_FIELD_DESC(labels),
      TOOLS_ARG_FIELD_DESC(values),
      TOOLS_ARG_FIELD_DESC(coords),
      TOOLS_ARG_FIELD_DESC(sub_coords),
      TOOLS_ARG_FIELD_DESC(magnitude),
      TOOLS_ARG_FIELD_DESC(title),
      TOOLS_ARG_FIELD_DESC(title_to_axis),
      TOOLS_ARG_FIELD_DESC(title_height),
      TOOLS_ARG_FIELD_DESC(title_hjust),
      TOOLS_ARG_FIELD_DESC(label_to_axis),
      TOOLS_ARG_FIELD_DESC(label_height),
      TOOLS_ARG_FIELD_DESC(labels_no_overlap_automated),
      TOOLS_ARG_FIELD_DESC(labels_gap),

      TOOLS_ARG_FIELD_DESC(time_labels),
      TOOLS_ARG_FIELD_DESC(time_format),
      TOOLS_ARG_FIELD_DESC(time_offset),
      TOOLS_ARG_FIELD_DESC(time_offset_is_GMT)
    );
    return s_v;
  }
  virtual bool touched() {
    if(parent::touched()) return true;
    if(line_style().touched()) return true;
    if(ticks_style().touched()) return true;
    if(labels_style().touched()) return true;
    if(mag_style().touched()) return true;
    if(title_style().touched()) return true;
    return false;
  }
  virtual void reset_touched() {
    parent::reset_touched();
    line_style().reset_touched();
    ticks_style().reset_touched();
    labels_style().reset_touched();
    mag_style().reset_touched();
    title_style().reset_touched();
  }
private:
  void add_fields(){
    // if adding a field, look for reset_style() and set_from_style()
    add_field(&width);
    add_field(&minimum_value);
    add_field(&maximum_value);
    add_field(&divisions);
    add_field(&modeling);
    add_field(&is_log);
    add_field(&labels_enforced);
    add_field(&tick_up);
    add_field(&tick_length);
    add_field(&tick_number);
    add_field(&labels);
    add_field(&values);
    add_field(&coords);
    add_field(&sub_coords);
    add_field(&magnitude);
    add_field(&title);
    add_field(&title_to_axis);
    add_field(&title_height);
    add_field(&title_hjust);
    add_field(&label_to_axis);
    add_field(&label_height);
    add_field(&labels_no_overlap_automated);
    add_field(&labels_gap);

    add_field(&time_labels);
    add_field(&time_format);
    add_field(&time_offset);
    add_field(&time_offset_is_GMT);
  }
  void init_sg(){
    m_group.add(new noderef(m_line_sep));
    m_group.add(new noderef(m_ticks_sep));
    m_group.add(new noderef(m_labels_sep));
    m_group.add(new noderef(m_mag_sep));
    m_group.add(new noderef(m_title_sep));
  }
public:
  virtual void render(render_action& a_action) {
    if(touched()) {
      update_sg(a_action.out());
      reset_touched();
    }
    m_group.render(a_action);
  }
  virtual void pick(pick_action& a_action) {
    if(touched()) {
      update_sg(a_action.out());
      reset_touched();
    }
    //m_group.pick(a_action);
    nodekit_pick(a_action,m_group,this);
  }
  virtual void search(search_action& a_action) {
    if(touched()) {
      update_sg(a_action.out());
      reset_touched();
    }
    parent::search(a_action);
    if(a_action.done()) return;
    m_group.search(a_action);
  }
  virtual void bbox(bbox_action& a_action) {
    if(touched()) {
      update_sg(a_action.out());
      reset_touched();
    }
    m_group.bbox(a_action);
  }

  virtual bool write(write_action& a_action) {
    //FIXME : this method should not be needed !
    //        But m_[line,ticks,labels,mag,title]_style not written !

    if(touched()) {
      update_sg(a_action.out());
      reset_touched();
    }
    //if(!write_fields(a_action)) return false;
    return m_group.write(a_action);
  }
public:
  axis(const base_freetype& a_ttf)
  :parent()
  ,width(1)
  ,minimum_value(0)
  ,maximum_value(1)
  ,divisions(510)
  ,modeling(tick_modeling_hippo())
  ,is_log(false)
  ,labels_enforced(false)
  ,tick_up(true)
  ,tick_length(0)

  ,tick_number(0)
  ,magnitude(0)

  ,title("")
  ,title_to_axis(0)   //inited below
  ,title_height(0)    //inited below
  ,title_hjust(right)

  ,label_to_axis(0) //inited below
  ,label_height(0)  //inited below

  ,labels_no_overlap_automated(true)
  ,labels_gap(0.02f)

  ,time_labels(false)
  ,time_format("%H:%M:%S")
  ,time_offset(0) //UTC_time_1970_01_01__00_00_00
  ,time_offset_is_GMT(false)

  ,m_ttf(a_ttf)
  {
    add_fields();

    init_sg();

    reset_style(true);
  }
  virtual ~axis(){}
public:
  axis(const axis& a_from)
  :parent(a_from)
  ,width(a_from.width)
  ,minimum_value(a_from.minimum_value)
  ,maximum_value(a_from.maximum_value)
  ,divisions(a_from.divisions)
  ,modeling(a_from.modeling)
  ,is_log(a_from.is_log)
  ,labels_enforced(a_from.labels_enforced)
  ,tick_up(a_from.tick_up)
  ,tick_length(a_from.tick_length)

  ,tick_number(a_from.tick_number)
  ,magnitude(a_from.magnitude)

  ,title(a_from.title)
  ,title_to_axis(a_from.title_to_axis)
  ,title_height(a_from.title_height)
  ,title_hjust(a_from.title_hjust)

  ,label_to_axis(a_from.label_to_axis)
  ,label_height(a_from.label_height)

  ,labels_no_overlap_automated(a_from.labels_no_overlap_automated)
  ,labels_gap(a_from.labels_gap)

  ,time_labels(a_from.time_labels)
  ,time_format(a_from.time_format)
  ,time_offset(a_from.time_offset)
  ,time_offset_is_GMT(a_from.time_offset_is_GMT)

  ,m_ttf(a_from.m_ttf)

  ,m_line_style(a_from.m_line_style)
  ,m_ticks_style(a_from.m_ticks_style)
  ,m_labels_style(a_from.m_labels_style)
  ,m_mag_style(a_from.m_mag_style)
  ,m_title_style(a_from.m_title_style)
  {
    add_fields();
    init_sg();
  }
  axis& operator=(const axis& a_from){
    parent::operator=(a_from);

    width = a_from.width;
    minimum_value = a_from.minimum_value;
    maximum_value = a_from.maximum_value;
    divisions = a_from.divisions;
    modeling = a_from.modeling;
    is_log = a_from.is_log;
    labels_enforced = a_from.labels_enforced;
    tick_up = a_from.tick_up;
    tick_length = a_from.tick_length;

    tick_number = a_from.tick_number;
    magnitude = a_from.magnitude;

    title = a_from.title;
    title_to_axis = a_from.title_to_axis;
    title_height = a_from.title_height;
    title_hjust = a_from.title_hjust;

    label_to_axis = a_from.label_to_axis;
    label_height = a_from.label_height;

    labels_no_overlap_automated = a_from.labels_no_overlap_automated;
    labels_gap = a_from.labels_gap;

    time_labels = a_from.time_labels;
    time_format = a_from.time_format;
    time_offset = a_from.time_offset;
    time_offset_is_GMT = a_from.time_offset_is_GMT;

    m_line_style = a_from.m_line_style;
    m_ticks_style = a_from.m_ticks_style;
    m_labels_style = a_from.m_labels_style;
    m_mag_style = a_from.m_mag_style;
    m_title_style = a_from.m_title_style;

    return *this;
  }
public:
  sg::line_style& line_style() {return m_line_style;}
  sg::line_style& ticks_style() {return m_ticks_style;}
  text_style& labels_style() {return m_labels_style;}
  text_style& title_style() {return m_title_style;}
  text_style& mag_style() {return m_mag_style;}

  void set_color(const colorf& a_color){
    m_line_style.color = a_color;
    m_ticks_style.color = a_color;
    m_labels_style.color = a_color;
    m_title_style.color = a_color;
    m_mag_style.color = a_color;
  }
public:
  void update_sg(std::ostream& a_out) {
    if(width<=0) {
      m_line_sep.clear();
      m_ticks_sep.clear();
      m_labels_sep.clear();
      m_mag_sep.clear();
      m_title_sep.clear();
      return;
    }

    // line scene graph :
    m_line_sep.clear();
    if(m_line_style.visible) {
      rgba* mat = new rgba();
      mat->color = m_line_style.color;
      m_line_sep.add(mat);

      draw_style* ds = new draw_style;
      ds->style = draw_lines;
      ds->line_pattern = m_line_style.pattern;
      ds->line_width = m_line_style.width;
      m_line_sep.add(ds);

      vertices* vtxs = new vertices;
      vtxs->mode = gl::line_strip();
      vtxs->add(0,0,0);
      vtxs->add(width,0,0);
      m_line_sep.add(vtxs);
    }

    // ticks scene graph :
    if(modeling==tick_modeling_none()) {
    } else if(modeling==tick_modeling_hplot()) {
      compute_ticks_HPLOT(a_out);
    } else {
      compute_ticks_hippo();
    }

    m_ticks_sep.clear();
    if(m_ticks_style.visible) {

      vertices* vtxs = new vertices;
      vtxs->mode = gl::lines();

      if(modeling==tick_modeling_hplot()) {

        size_t num = m_sub_ticks.size()/4;
        size_t pos = 0;
        for(size_t index=0;index<num;index++) {
          float bx = m_sub_ticks[pos];pos++;
          float by = m_sub_ticks[pos];pos++;
          float ex = m_sub_ticks[pos];pos++;
          float ey = m_sub_ticks[pos];pos++;
          if(tick_up) {
            vtxs->add(bx,by,0);
            vtxs->add(ex,ey,0);
          } else {
            vtxs->add(bx,-by,0);
            vtxs->add(ex,-ey,0);
          }
        }

      } else {

        float yy = tick_up ? tick_length.value():-tick_length.value();
        for(unsigned int index=0;index<tick_number;index++) {
          float xx = coords.values()[index];
          vtxs->add(xx,0,0);
          vtxs->add(xx,yy,0);
        }
      }

      if(vtxs->number()) {

        rgba* mat = new rgba();
        mat->color = m_ticks_style.color;
        m_ticks_sep.add(mat);

        draw_style* ds = new draw_style;
        ds->style = draw_lines;
        ds->line_pattern = m_ticks_style.pattern;
        ds->line_width = m_ticks_style.width;
        m_ticks_sep.add(ds);

        m_ticks_sep.add(vtxs);
      } else {
        delete vtxs;
      }

    }

    // labels scene graph :
    m_labels_sep.clear();
    if(m_labels_style.visible && tick_number ) {
      m_labels_seps.clear();
      m_labels_xs.clear();
      m_labels_mtxs.clear();

      rgba* mat = new rgba();
      mat->color = m_labels_style.color;
      m_labels_sep.add(mat);

      float text_size = label_height*m_labels_style.scale;
      std::string font = m_labels_style.font.value();

      if(font==font_hershey()) {
        draw_style* ds = new draw_style;
        ds->style = draw_lines;
        ds->line_pattern = m_labels_style.line_pattern;
        ds->line_width = m_labels_style.line_width;
        m_labels_sep.add(ds);
      } else {
        m_labels_sep.add(new normal);
      }

      vec3f X = m_labels_style.x_orientation.value();
      vec3f Y = m_labels_style.y_orientation.value();
      X.normalize();
      Y.normalize();
      vec3f Z;X.cross(Y,Z);
      Z.cross(X,Y);
      mat4f scale_rot(X.v0(),Y.v0(),Z.v0(),0, //first row
                      X.v1(),Y.v1(),Z.v1(),0,
                      X.v2(),Y.v2(),Z.v2(),0,
                      0,0,0,1);
      scale_rot.mul_scale(text_size,text_size,1);

      bool bin_center = (m_labels_style.options.value()=="center"?true:false); //gopaw.

      vec3f vec;float xx;
     {unsigned int number = tick_number;
      for(unsigned int index=0;index<number;index++) {

        if(bin_center) { //label at the center of the bin :
	  if(index==(number-1)) continue;
          xx = 0.5f*(coords.values()[index]+coords.values()[index+1]);
	} else { // label on tick :
          xx = coords.values()[index];
	}

        vec.set_value(xx,-label_to_axis,0);
        vec += m_labels_style.translation.value();

        separator* sep = new separator;
        m_labels_sep.add(sep);

        matrix* _tsf =
	  add_string_opt(*sep,
                         font,
                         m_labels_style.font_modeling.value(),
                         m_labels_style.encoding.value(),
                         m_labels_style.smoothing,
                         labels.values()[index],
                         vec[0],vec[1],vec[2],
                         scale_rot,
                         m_labels_style.hjust,
                         m_labels_style.vjust,
                         m_ttf);
        if(_tsf) {
          m_labels_seps.push_back(sep);
          m_labels_xs.push_back(xx);
          m_labels_mtxs.push_back(_tsf);
        }
      }}

      if(labels_no_overlap_automated.value()) avoid_labels_overlap(a_out);
      m_labels_seps.clear();
      m_labels_xs.clear();
      m_labels_mtxs.clear();
    }

    m_mag_sep.clear();
    if( magnitude.value() && m_mag_style.visible) {
      rgba* mat = new rgba();
      mat->color = m_mag_style.color;
      m_mag_sep.add(mat);

      char string[64];
      if(magnitude>=0)
        snpf(string,sizeof(string),"x10+%d",magnitude.value());
      else
        snpf(string,sizeof(string),"x10-%d",::abs(magnitude));

      vec3f vec(width*1.03f,0,0);
      vec += m_mag_style.translation.value();

      float text_size = label_height*0.8f * m_mag_style.scale;
      std::string font = m_mag_style.font.value();

      if(font==font_hershey()) {
        draw_style* ds = new draw_style;
        ds->style = draw_lines;
        ds->line_pattern = m_mag_style.line_pattern;
        ds->line_width = m_mag_style.line_width;
        m_mag_sep.add(ds);
      }

      add_string(m_mag_sep,
                         font,
                         m_mag_style.font_modeling.value(),
                         m_mag_style.encoding.value(),
                         m_mag_style.smoothing,
                         string,
                         vec[0],vec[1],vec[2],
                         m_mag_style.x_orientation.value(),
                         m_mag_style.y_orientation.value(),
                         text_size,
                         m_mag_style.hjust,
                         m_mag_style.vjust,
                         m_ttf);
    }

    // title scene graph :if(update_title) {
    m_title_sep.clear();
    if(title.value().size() && m_title_style.visible) {
      rgba* mat = new rgba();
      mat->color = m_title_style.color;
      m_title_sep.add(mat);

      float text_size = title_height*m_title_style.scale;
      std::string font = m_title_style.font.value();

      if(font==font_hershey()) {
        draw_style* ds = new draw_style;
        ds->style = draw_lines;
        ds->line_pattern = m_title_style.line_pattern;
        ds->line_width = m_title_style.line_width;
        m_title_sep.add(ds);
      } else {
        m_title_sep.add(new normal);
      }

      float xx = 0; //left
      if(title_hjust==center) {
         xx = width/2;
      } else if(title_hjust==right) {
         xx = width;
      }

      vec3f vec(xx,-title_to_axis,0);
      vec += m_title_style.translation.value();

      add_string(m_title_sep,
                         font,
                         m_title_style.font_modeling.value(),
                         m_title_style.encoding.value(),
                         m_title_style.smoothing,
                         title.value(),
                         vec[0],vec[1],vec[2],
                         m_title_style.x_orientation.value(),
                         m_title_style.y_orientation.value(),
                         text_size,
                         m_title_style.hjust,
                         m_title_style.vjust,
                         m_ttf);
    }

  }

public: //style
  void reset_style(bool a_geom = false) {
    //reset fields that are considered as part of the style.

    ////////////////////////////////////////////
    // we do not touch :
    ////////////////////////////////////////////
    //width
    //minimum_value
    //maximum_value
    //labels_enforced
    //tick_number
    //magnitude
    //time_labels
    //time_format
    //time_offset
    //time_offset_is_GMT
    //labels
    //values
    //coords
    //sub_coords

    ////////////////////////////////////////////
    divisions = 510;
    modeling = tick_modeling_hippo();
    tick_up = true;
    is_log = false;
    title.value().clear();

    labels_no_overlap_automated = true;
    labels_gap = 0.02f;

    if(a_geom) {
    ////////////////////////////////////////////
    // Take PAW default :
    float YSIZ = 20; //page height
    float YMGL = 2;  //low y margin (to data frame).
    float YMGU = 2;  //up y margin (to data frame).
    float VSIZ = 0.28F; //tick label character size.
    float YVAL = 0.4F;  //y distance of x tick label to data frame.
    float XTIC = 0.3F;  //y length of X axis ticks.
    float YLAB = 0.8F; //y distance of x title to data frame.
    float ASIZ = 0.28F; // axis title (label) character size.

    float hData = YSIZ-YMGL-YMGU;

    // To map data space to width :
    float to1 = width/hData;

    float vsiz = VSIZ * to1; //0.0175F
    float yval = YVAL * to1; //0.025F
    float xtic = XTIC * to1; //0.01875F
    float ylab = YLAB * to1; //0.05F
    float asiz = ASIZ * to1; //0.0175F

    //sf
    tick_length = xtic;
    label_to_axis = yval;
    label_height = vsiz;

    // The axis title is the PAW axis label.
    // It is right justified at the end of axis
    // (at end right for XY_X, at end top for XY_Y)
    title_to_axis = ylab;
    title_height = asiz;
    }

    title_hjust = right;

    ////////////////////////////////////////////
    // setup styles :
    m_line_style = line_style();
    m_ticks_style = line_style();
    m_labels_style = text_style();
    m_mag_style = text_style();
    m_title_style = text_style();

    m_line_style.color = colorf_black();
    m_ticks_style.color = colorf_black();

    m_labels_style.color = colorf_black();
    m_labels_style.font = font_hershey();
    m_labels_style.encoding = encoding_PAW();

    m_mag_style.color = colorf_black();
    m_mag_style.font = font_hershey();
    m_mag_style.encoding = encoding_PAW();

    m_title_style.color = colorf_black();
    m_title_style.font = font_hershey();
    m_title_style.encoding = encoding_PAW();
  }

  typedef std::pair<std::string,std::string> style_item_t;
  typedef std::vector<style_item_t> style_t;
  bool set_from_style(std::ostream& a_out,const style_t& a_style) {
    style_t::const_iterator it;
    for(it=a_style.begin();it!=a_style.end();++it) {
      const std::string& key = (*it).first;
      const std::string& sv = (*it).second;

      // not part of style :
      //width
      //minimum_value
      //maximum_value
      //labels_enforced
      //tick_number
      //magnitude
      //title
      //time_labels
      //time_format
      //time_offset
      //time_offset_is_GMT
      //labels
      //values
      //coords
      //sub_coords

      if(key=="divisions") {
        unsigned int v;
        if(!to(sv,v)) {style_failed(a_out,key,sv);return false;}
        divisions = v;
      } else if(key=="modeling") {
        modeling = sv;
      } else if(key=="is_log") {
        bool v;
        if(!to(sv,v)) {style_failed(a_out,key,sv);return false;}
        is_log = v;

      } else if(key=="tick_up") {
        bool v;
        if(!to(sv,v)) {style_failed(a_out,key,sv);return false;}
        tick_up = v;
      } else if(key=="tick_length") {
        float v;
        if(!to(sv,v)) {style_failed(a_out,key,sv);return false;}
        tick_length = v;

      } else if(key=="title") {
        title = sv;
      } else if(key=="title_to_axis") {
        float v;
        if(!to(sv,v)) {style_failed(a_out,key,sv);return false;}
        title_to_axis = v;
      } else if(key=="title_height") {
        float v;
        if(!to(sv,v)) {style_failed(a_out,key,sv);return false;}
        title_height = v;
      } else if(key=="title_hjust") {
        hjust v;
        if(!shjust(sv,v))
          {style_failed(a_out,key,sv);return false;}
        title_hjust = v;

      } else if(key=="label_to_axis") {
        float v;
        if(!to(sv,v)) {style_failed(a_out,key,sv);return false;}
        label_to_axis = v;
      } else if(key=="label_height") {
        float v;
        if(!to(sv,v)) {style_failed(a_out,key,sv);return false;}
        label_height = v;

      } else if(key=="labels_no_overlap_automated") {
        bool v;
        if(!to(sv,v)) {style_failed(a_out,key,sv);return false;}
        labels_no_overlap_automated = v;
      } else if(key=="labels_gap") {
        float v;
        if(!to(sv,v)) {style_failed(a_out,key,sv);return false;}
        labels_gap = v;

      } else {
        a_out << "axis::set_from_style :"
              << " unknown key " << key << "."
              << std::endl;
      }
    }
    return true;
  }

  void set_encoding(const std::string& a_value) {
    labels_style().encoding = a_value;
    mag_style().encoding = a_value;
    title_style().encoding = a_value;
  }
protected:
  float get_overlap(std::ostream& a_out,bool& a_overlap) {
    a_overlap = false;
    std::vector<float> x_mins;
    std::vector<float> x_maxs;
   {size_t index = 0;
    bbox_action _action(a_out);
    tools_vforcit(separator*,m_labels_seps,it) {
      _action.reset();
      (*it)->bbox(_action);
      if(_action.end()) {
        float dx,dy,dz;
        if(_action.box().get_size(dx,dy,dz)) {
          if(dx>0) {
            x_mins.push_back(m_labels_xs[index]-dx*0.5f);
            x_maxs.push_back(m_labels_xs[index]+dx*0.5f);
          }
        }
      }
      index++;
    }}
    float dx_overlap = 0;
   {size_t number = x_mins.size();
    for(size_t index=1;index<number;index++) {
      float dx = x_mins[index]-x_maxs[index-1];
      if(dx<0) {
        a_overlap = true;
	dx_overlap = max_of(dx_overlap,-dx);
      }
    }}
    return dx_overlap;
  }
  void avoid_labels_overlap(std::ostream& a_out) {
    bool overlap;
    float first_scale = 1;
    float first_overlap = get_overlap(a_out,overlap);
    if(overlap) {
      float second_scale = 1.1f; // greater than one to be sure to overlap again.
     {tools_vforcit(matrix*,m_labels_mtxs,it) {
        (*it)->mul_scale(second_scale,second_scale,1);
      }}
      float second_overlap = get_overlap(a_out,overlap);
      if(overlap) {
        // first_overlap  = a*first_scale+b
        // second_overlap = a*second_scale+b
	float a = (second_overlap-first_overlap)/(second_scale-first_scale);
	float b = first_overlap-a*first_scale;
      //float wanted_scale = -b/a; //zero overlap.
        float wanted_gap = width.value()*labels_gap.value();
        float wanted_scale = (a==0.0f) ? 1 : (-wanted_gap-b)/a; //a==0.0f should not happen.
        if(wanted_scale<=0) wanted_scale = 1; // this if() should not happen.
        wanted_scale /= second_scale;
       {tools_vforcit(matrix*,m_labels_mtxs,it) {
          (*it)->mul_scale(wanted_scale,wanted_scale,1);
        }}
      } else { //it should not happen.
        tools_vforcit(matrix*,m_labels_mtxs,it) {
          (*it)->mul_scale(1.0f/second_scale,1.0f/second_scale,1);
        }
      }
    }
  }

  static void style_failed(std::ostream& a_out,
                           const std::string& a_key,
                           const std::string& a_value) {
    a_out << "axis::set_from_style :"
          << " failed for key " << sout(a_key)
          << " and value " << sout(a_value) << "."
          << std::endl;
  }
protected:
  //////////////////////////////////////////////////////////////////////////
  /// Hippodraw tick modeling //////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////////
  void compute_ticks_hippo() {
    //  input fields :
    //    minimum_value
    //    maximum_value
    //    is_log
    //  output fields :
    //    values
    //    coords
    //    sub_coords
    //    labels (if labels_enforced is false)
    //    tick_number


    float mn = minimum_value;
    float mx = maximum_value;

    bool a_is_log = is_log;
    if(a_is_log) {
      if((mn<=0) || (mx<=0) ) a_is_log = false;
    }

    float magxxx,y,yr,startTick,tickSize;
    float pmag;
    char pstr[10] = "";
    char tmp[10];

    unsigned int tick_num = 0;
    std::vector<float> tick_values;
    std::vector<std::string> tick_labels;

    // need include <float.h> which does not exist on some system
    //NUM_FUZZ DBL_EPSILON*4
    float NUM_FUZZ = 0.01f;

    if (mn >= mx) {
      if(tick_number) {
        tick_number.value(0);
        values.clear();
        coords.clear();
        sub_coords.clear();
        labels.clear();
      }
      if(magnitude.value()) magnitude.value(0);
      m_sub_ticks.clear();
      return;
    }

    if (!a_is_log) {

      tickSize  = calculate_ticks_hippo(mx-mn,magxxx);
      startTick = fceil( mn / tickSize) * tickSize;

      if (ffabs(magxxx) <= 3)
        pmag = 0.0;
      else
        pmag = startTick != 0.0 ? ffloor(flog10(ffabs(startTick))) : magxxx;

      snpf(pstr,sizeof(pstr),"%%1.%df",(int)max_of<float>((pmag-magxxx),0.0));

      y = startTick;
      while (y <= mx*(1.0+NUM_FUZZ)) {

        yr = ffloor(y/fpow(10,magxxx) + 0.5F);

        snpf(tmp,sizeof(tmp),pstr,yr*fpow(10,magxxx-pmag));

       {float val = yr * fpow(10.0,magxxx);
        if((val>=mn)&&(val<=mx)) { //G.Barrand : add this test.
          tick_values.push_back(val);
          tick_labels.push_back(tmp);
          tick_num++;
        }}

        y += tickSize;
      }
      if (ffabs(magxxx) <= 3.0) magxxx = 0.0;

    }  else {

      if (mn <= 0) {
        if(tick_number) {
          tick_number.value(0);
          values.clear();
          coords.clear();
          sub_coords.clear();
          labels.clear();
        }
        if(magnitude.value()) magnitude.value(0);
        m_sub_ticks.clear();
        return;
      }

      int nLogTicks;
      float logTicks[5];
      float magStep;

      float maghigh = fceil(flog10(mx));
      float maglow  = ffloor(flog10(mn));
      float magrng  = maghigh - maglow;

      if (magrng <=3) {
        nLogTicks   = 3;
        logTicks[0] = 1.0;
        logTicks[1] = 2.0;
        logTicks[2] = 5.0;
        magStep     = 1.0;
      } else {
        nLogTicks   = 1;
        logTicks[0] = 1.0;
        magStep     = magrng <= 7 ? 1.0F : 2.0F;
      }

      pmag = (nLogTicks == 3 && (ffabs(maglow)>3 || ffabs(maghigh)>3)) ?
        maglow : 0;

      magxxx = maglow;
      int i = 0;
      while ((y=logTicks[i]*fpow(10,magxxx)) < mx*(1+NUM_FUZZ)) {
        if (y >= mn) {

          // be careful: there is a bug in the NeXT (s)printf
          //   routine when you do, eg. printf("%1.0g",0.01);
          if ((magxxx-pmag) > 4 || (magxxx-pmag) < -3) {
            ::strcpy(pstr,"%1.0e");
          } else {
            snpf(pstr,sizeof(pstr),
                 "%%1.%df",(int)((magxxx-pmag)>0?0.:-(magxxx-pmag)));
          }
          snpf(tmp,sizeof(tmp),pstr,y*fpow(10.0,-pmag));

         {float val = flog10(y);
          if((val>=flog10(mn))&&(val<=flog10(mx))) { //G.Barrand : add this if
            tick_values.push_back(val);
            tick_labels.push_back(tmp);
            tick_num++;
          }}

        }

        i++;
        if (i>=nLogTicks)        {
          i = 0;
          magxxx += magStep;
        }
      }

      mn = flog10(mn);
      mx = flog10(mx);
    }

    float range = mx - mn;

    // it is assumes that tick_values are ordered min to max.
    tick_number.value(tick_num);
    values.clear();
    coords.clear();
    for(unsigned int index=0;index<tick_num;index++) {
      float val = tick_values[index];
      float coord = width * (val-mn)/range;
      values.add(val);
      coords.add(coord);
    }

    if(labels_enforced) {
      size_t n = labels.size();
      if(tick_num>n) {
        for(size_t index=n;index<tick_num;index++) labels.add("");
      }
    } else {
      labels = tick_labels;
    }

    magnitude.value((int)pmag);

    sub_coords.clear();
    m_sub_ticks.clear();
  }

  static float calculate_ticks_hippo(float aSize,float& a_mag) {
    unsigned int MIN_TICKS = 4;

    if (aSize <= 0.0) {
      //printf ("CalculateTicks : bad value \n");
      aSize = ffabs(aSize);
      if (aSize == 0.0) aSize = 1.0;
    }

    a_mag = ffloor(flog10(aSize));
    if (aSize/fpow(10.0,a_mag) < MIN_TICKS) (a_mag)--;

    // now fit the max number of ticks into this range

    float  tickSize;
    int tickIndex;
    static const float goodTicks[] = {10.0, 5.0, 4.0, 2.0, 1.0};
    for(tickIndex = 0;
       aSize/(tickSize=goodTicks[tickIndex]*fpow(10.0,a_mag))<MIN_TICKS;
        tickIndex++){}

    if (tickIndex == 0) a_mag++;

    return tickSize;
  }

  ////////////////////////////////////////////////////////////////////////////
  /// HPLOT tick modeling ////////////////////////////////////////////////////
  ////////////////////////////////////////////////////////////////////////////
  ////////////////////////////////////////////////////////////////////////////
  void compute_ticks_HPLOT(std::ostream& a_out) {
    //  Controlled by fields :
    //    minimum_value
    //    maximum_value
    //    divisions
    //    is_log
    //  Set value on fields :
    //    values
    //    coords
    //    labels (if labels_enforced is false)
    //    tick_number

    float mn = minimum_value;
    float mx = maximum_value;

    bool a_is_log = is_log;
    if(a_is_log) {
      if((mn<=0) || (mx<=0) ) a_is_log = false;
    }

    // Use hplot::axis to get the ticks and subticks positions
    // and the labels text.

    double xmin = 0;
    double ymin = 0;
    double xmax = width;
    double ymax = 0;

    double gridlength = 0;
    std::string chopt;
    if(a_is_log) chopt += "G";

    std::vector<float> linesGrid;
    std::vector<hplot::_text> texts;

    hplot::axis sbAxisHPLOT(a_out);

    chopt += "S";
    sbAxisHPLOT.set_tick_size(tick_length/width.value());

    if(time_labels.value()) {
      chopt += "t";
      sbAxisHPLOT.set_time_format(time_format.value());
      sbAxisHPLOT.set_time_offset(time_offset,
                                  time_offset_is_GMT);
    }

    // Get ticks :
   {double wmin = mn;
    double wmax = mx;
    int ndiv = divisions;
    sbAxisHPLOT.set_title("");
    sbAxisHPLOT.paint(xmin,ymin,xmax,ymax,
                      wmin,wmax,ndiv, //Modified
                      chopt,gridlength,false,
                      m_sub_ticks,linesGrid,texts);}

    if(a_is_log) {
      mn = flog10(mn);
      mx = flog10(mx);
    }

    float range = mx - mn;

    size_t tick_num = texts.size();

    // HPLOT stores the magnitude on the last label :
    magnitude.value(0);
    if(tick_num) {
      int pmag;
      if(::sscanf(texts[tick_num-1].fString.c_str(),"x10^%d!",&pmag)==1) {
        magnitude.value(pmag);
        tick_num--;
      }
    }

    tick_number.value(uint32(tick_num));
    values.clear();
    coords.clear();
    for(size_t index=0;index<tick_num;index++) {
      float coord = (float)texts[index].fX;
      //NOTE : are we sure that val is in [mn,mx]
      float val = (coord/width.value()) * range + mn;
      coords.add(coord);
      values.add(val);
    }

    if(labels_enforced) {
      size_t n = labels.size();
      if(tick_num>n) {
        for(size_t index=n;index<tick_num;index++) labels.add("");
      }
    } else {
      labels.clear();
      for(size_t index=0;index<tick_num;index++) {
        labels.add(texts[index].fString);
      }
    }

   {sub_coords.clear();
    size_t num = m_sub_ticks.size()/4;
    size_t pos = 0;
    for(size_t index=0;index<num;index++) {
      float coord = m_sub_ticks[pos];
      pos += 4;
      bool found = false;
      for(size_t i=0;i<tick_num;i++) {
        if((float)texts[i].fX==coord) {
          found = true;
          break;
        }
      }
      if(!found) { //not a main tick
        sub_coords.add(coord);
      }
    }}
  }

protected:
  const base_freetype& m_ttf;

  group m_group;

  separator m_line_sep;
  separator m_ticks_sep;
  separator m_labels_sep;
  separator m_mag_sep;
  separator m_title_sep;

  sg::line_style m_line_style;
  sg::line_style m_ticks_style;
  text_style m_labels_style;
  text_style m_mag_style;
  text_style m_title_style;

  std::vector<float> m_sub_ticks; //n*(2+2)

  std::vector<separator*> m_labels_seps;
  std::vector<float> m_labels_xs;
  std::vector<matrix*> m_labels_mtxs;
};

}}

#endif
