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

#ifndef tools_sg_plots
#define tools_sg_plots

#include "_switch"
//#include "head_light"
#include "matrix"

#include "plotter"

namespace tools {
namespace sg {

class plots : public node {
  TOOLS_NODE(plots,tools::sg::plots,node)
public:
  sf<float> width;
  sf<float> height;
  sf<unsigned int> cols; //must never be 0.
  sf<unsigned int> rows; //must never be 0.
  sf<bool> view_border; //current plotter border
  sf<float> plotter_scale; //scale factor applied to each plotter.

  sf<bool> border_visible;
  sf<float> border_width;
  sf<float> border_height;
  sf_vec<colorf,float> border_color;

  // for gopaw :
  sf<float> left_margin;
  sf<float> right_margin;
  sf<float> top_margin;
  sf<float> bottom_margin;
  sf<float> horizontal_spacing;
  sf<float> vertical_spacing;
public:
  virtual const desc_fields& node_desc_fields() const {
    TOOLS_FIELD_DESC_NODE_CLASS(tools::sg::plots)
    static const desc_fields s_v(parent::node_desc_fields(),16, //WARNING : take care of count.
      TOOLS_ARG_FIELD_DESC(width),
      TOOLS_ARG_FIELD_DESC(height),
      TOOLS_ARG_FIELD_DESC(cols),
      TOOLS_ARG_FIELD_DESC(rows),
      TOOLS_ARG_FIELD_DESC(view_border),
      TOOLS_ARG_FIELD_DESC(plotter_scale),
      TOOLS_ARG_FIELD_DESC(border_visible),
      TOOLS_ARG_FIELD_DESC(border_width),
      TOOLS_ARG_FIELD_DESC(border_height),
      TOOLS_ARG_FIELD_DESC(border_color),
      TOOLS_ARG_FIELD_DESC(left_margin),
      TOOLS_ARG_FIELD_DESC(right_margin),
      TOOLS_ARG_FIELD_DESC(top_margin),
      TOOLS_ARG_FIELD_DESC(bottom_margin),
      TOOLS_ARG_FIELD_DESC(horizontal_spacing),
      TOOLS_ARG_FIELD_DESC(vertical_spacing)
    );
    return s_v;
  }
  virtual bool touched() {
    if(parent::touched()) return true;
     
     if(m_sep.empty()) return true;
     if(m_extras.size()!=m_extras_sep.size()) return true;
 
  //{size_t _number = m_sep.size();    
  // for(size_t index=0;index<_number;index++) {
  //   separator* sep = (separator*)m_sep[index];
  //   plotter* _plotter = (plotter*)(*sep)[PLOTTER()];
  //   if(_plotter->shape.touched()) return true;
  // }}

     return false;
   }
private:
  void add_fields(){
    add_field(&width);
    add_field(&height);
    add_field(&cols);
    add_field(&rows);
    add_field(&view_border);
    add_field(&plotter_scale);
    add_field(&border_visible);
    add_field(&border_width);
    add_field(&border_height);
    add_field(&border_color);
    add_field(&left_margin);
    add_field(&right_margin);
    add_field(&top_margin);
    add_field(&bottom_margin);
    add_field(&horizontal_spacing);
    add_field(&vertical_spacing);
  }
private:
  static unsigned int MATRIX()  {return 0;}
  static unsigned int BORDER()  {return 1;}
  static unsigned int PLOTTER() {return 2;}
  //static unsigned int TSF()     {return xxx;}
  //static unsigned int LIGHT()   {return xxx;}

  typedef std::vector<plottable*> ptbs_t;
  typedef std::vector<node*> todels_t;
  typedef std::vector<plotprim*> prims_t;
public:
/*
  class updater { 
  public:
    virtual void update(plots&,size_t) = 0;
    virtual updater* copy() const = 0;
  public:
    virtual ~updater(){}
  };
*/
public:
  virtual void render(render_action& a_action) {
    update_if_touched();
    m_group.render(a_action);
  }
  virtual void pick(pick_action& a_action) {
    update_if_touched();
    nodekit_pick(a_action,m_group,this);
    //m_group.pick(a_action);
  }
  virtual void search(search_action& a_action) {
    parent::search(a_action);
    if(a_action.done()) return;
    update_if_touched();
    if(a_action.do_path()) a_action.path_push(this);
    m_group.search(a_action);
    if(a_action.done()) return;
    if(a_action.do_path()) a_action.path_pop();
  }
  virtual void bbox(bbox_action& a_action) {
    update_if_touched();
    m_group.bbox(a_action);
  }

  virtual void event(event_action& a_action) {
    update_if_touched();
    m_group.event(a_action);
    if(a_action.done()) return;
  }
  virtual bool write(write_action& a_action) {
    update_if_touched();
    return m_group.write(a_action);
  }
public:
  plots(const base_freetype& a_ttf)
  :parent()
  ,width(1)
  ,height(1)
  ,cols(1)
  ,rows(1)
  ,view_border(true)
  ,plotter_scale(1)
  ,border_visible(false)
  ,border_width(0)
  ,border_height(0)
  ,border_color(colorf_grey())
  ,left_margin(0)
  ,right_margin(0)
  ,top_margin(0)
  ,bottom_margin(0)
  ,horizontal_spacing(0)
  ,vertical_spacing(0)

  ,m_ttf(a_ttf)
  ,m_current(0)
  ,m_extras()
  //,m_updater(0)
  ,m_old_cols(0)
  ,m_old_rows(0)
  {
    add_fields();
    init_sg();
  }
  virtual ~plots() {
    //delete m_updater;
  }
public:
  plots(const plots& a_from)
  :parent(a_from)
  ,width(a_from.width)
  ,height(a_from.height)
  ,cols(a_from.cols)
  ,rows(a_from.rows)
  ,view_border(a_from.view_border)
  ,plotter_scale(a_from.plotter_scale)
  ,border_visible(a_from.border_visible)
  ,border_width(a_from.border_width)
  ,border_height(a_from.border_height)
  ,border_color(a_from.border_color)
  ,left_margin(a_from.left_margin)
  ,right_margin(a_from.right_margin)
  ,top_margin(a_from.top_margin)
  ,bottom_margin(a_from.bottom_margin)
  ,horizontal_spacing(a_from.horizontal_spacing)
  ,vertical_spacing(a_from.vertical_spacing)

  ,m_ttf(a_from.m_ttf)
  ,m_current(a_from.m_current)
  ,m_extras(a_from.m_extras)
  //,m_updater(0)
  ,m_old_cols(0)
  ,m_old_rows(0)
  ,m_origins(a_from.m_origins)
  ,m_sizes(a_from.m_sizes)
  ,m_extras_origins(a_from.m_extras_origins)
  ,m_extras_sizes(a_from.m_extras_sizes)
  {
    add_fields();
    init_sg();
    //m_updater = a_from.m_updater?a_from.m_updater->copy():0;
    if(!copy_plotters(a_from)) {}
  }
  plots& operator=(const plots& a_from){    
    parent::operator=(a_from);
    if(&a_from==this) return *this;

    width = a_from.width;
    height = a_from.height;

    cols = a_from.cols;
    rows = a_from.rows;
    view_border = a_from.view_border;
    plotter_scale = a_from.plotter_scale;

    border_visible = a_from.border_visible;
    border_width = a_from.border_width;
    border_height = a_from.border_height;
    border_color = a_from.border_color;

    left_margin = a_from.left_margin;
    right_margin = a_from.right_margin;
    top_margin = a_from.top_margin;
    bottom_margin = a_from.bottom_margin;
    horizontal_spacing = a_from.horizontal_spacing;
    vertical_spacing = a_from.vertical_spacing;
  
    m_old_cols = 0;
    m_old_rows = 0;
    m_origins = a_from.m_origins;
    m_sizes = a_from.m_sizes;
    m_extras_origins = a_from.m_extras_origins;
    m_extras_sizes = a_from.m_extras_sizes;

    m_current = a_from.m_current;
    m_extras = a_from.m_extras;

    if(!copy_plotters(a_from)) {}
    
    return *this;
  }
public:
  unsigned int number() const {return cols*rows;}
  unsigned int current_index() const {return m_current;}
  
  void adjust_size(unsigned int a_ww,unsigned int a_wh) {
    if(!a_ww||!a_wh) return;
    float aspect = float(a_ww)/float(a_wh);
    width = height * aspect;
  }  

  //void enforce_update() { //used in geant4 plotting.
  //  update_sg();
  //  reset_touched();
  //}  

  const base_freetype& ttf() const {return m_ttf;}
public:
/*
  void set_updater(updater* a_updater) {
    //WARNING : we take ownership of a_updater
    delete m_updater;
    m_updater = a_updater;
  }
*/
  void clear() {
    update_if_touched();
   {size_t _number = m_sep.size();    
    for(size_t index=0;index<_number;index++) {
      separator* sep = (separator*)m_sep[index];
      plotter* _plotter = (plotter*)(*sep)[PLOTTER()];
      _plotter->clear();
    }}
    tools_vforit(extra,m_extras,it) {
      separator* sep = (*it).m_sep;
      plotter* _plotter = (plotter*)(*sep)[PLOTTER()];
      _plotter->clear();
    }
  }

  bool has_data() {
    update_if_touched();
   {size_t _number = m_sep.size();    
    for(size_t index=0;index<_number;index++) {
      separator* sep = (separator*)m_sep[index];
      plotter* _plotter = (plotter*)(*sep)[PLOTTER()];
      if(_plotter->plottables().size()) return true;
    }}
    tools_vforit(extra,m_extras,it) {
      separator* sep = (*it).m_sep;
      plotter* _plotter = (plotter*)(*sep)[PLOTTER()];
      if(_plotter->plottables().size()) return true;
    }
    return false;
  }

  void touch_plotters() { //used in exlib/geant4/session.
    update_if_touched();
   {size_t _number = m_sep.size();    
    for(size_t index=0;index<_number;index++) {
      separator* sep = (separator*)m_sep[index];
      plotter* _plotter = (plotter*)(*sep)[PLOTTER()];
      _plotter->touch();
    }}
    tools_vforit(extra,m_extras,it) {
      separator* sep = (*it).m_sep;
      plotter* _plotter = (plotter*)(*sep)[PLOTTER()];
      _plotter->touch();
    }
  }

  void next() {
    update_if_touched();
    size_t _number = m_sep.size();    
    if(m_current>=(uint32(_number)-1)) {
      m_current = 0;
    } else {
      m_current++;
    }
    update_current_border();
  }

  bool set_current_plotter(unsigned int a_index) {
    update_if_touched();
    if(a_index>=m_sep.size()) return false;
    m_current = a_index;
    update_current_border();
    return true;
  }

  bool set_current(plotter* a_plotter) { //for popup.
    update_if_touched();
    size_t _number = m_sep.size();    
    for(size_t index=0;index<_number;index++) {
      separator* sep = (separator*)m_sep[index];
      plotter* _plotter = (plotter*)(*sep)[PLOTTER()];
      if(_plotter==a_plotter) {
        m_current = uint32(index);
        update_current_border();
        return true;
      }
    }    
    return false;
  }

//  torche* current_torche() {
//    update_if_touched();
//    separator* sep = (separator*)m_sep[m_current];
//    return safe_cast<node,torche>(*((*sep)[LIGHT()]));
//  }

/*
  matrix* current_tsf() {
    plotter& _plotter = current_plotter();
    if(_plotter.shape.value()==plotter::xyz) {
      return &(_plotter.tsf());
    } else {
      update_if_touched();
      separator* sep = (separator*)m_sep[m_current];
      return safe_cast<node,matrix>(*((*sep)[TSF()]));
    }
  }
*/

  plotter& current_plotter() {
    update_if_touched();
    separator* sep = (separator*)m_sep[m_current];
    return *((plotter*)(*sep)[PLOTTER()]);
  }
  plotter* find_plotter(unsigned int a_index) {
    update_if_touched();
    if(a_index>=m_sep.size()) return 0;
    separator* sep = (separator*)m_sep[a_index];
    return (plotter*)(*sep)[PLOTTER()];
  }

/*
  bool find_plotter(unsigned int a_ww,unsigned int a_wh,
                    float a_x,float a_y,
                    unsigned int& a_index) {
    update_if_touched();

    a_index = 0;

    if(!a_ww) return false;
    if(!a_wh) return false;

    float aspect = float(a_ww)/float(a_wh);

    // one plotter in pixels :
    unsigned int rw = (unsigned int)(float(a_ww)/float(cols.value()));
    unsigned int rh = (unsigned int)(float(a_wh)/float(rows.value()));
    float ra = float(rw)/float(rh);

    // all window wc :
    float wh_wc = 1;
    float ww_wc = wh_wc * aspect;

    // plotter size in window wc :
    float rh_wc = wh_wc/rows;
    float rw_wc = rh_wc*ra;

    unsigned int _number = m_sep.size();    
    for(unsigned int index=0;index<_number;index++) {
      unsigned int row = index/cols;
      unsigned int col = index-cols*row;

      float x = -ww_wc*0.5f + col * rw_wc + rw_wc * 0.5f;
      float y =  wh_wc*0.5f - row * rh_wc - rh_wc * 0.5f;

      if( ((x-rw_wc*0.5f)<a_x)&&(a_x<(x+rw_wc*0.5f)) &&
          ((y-rh_wc*0.5f)<a_y)&&(a_y<(y+rh_wc*0.5f)) ){
        a_index = index;
        return true;
      }
    }    

    return false;
  }  
*/

  void set_regions(unsigned int a_cols = 1,unsigned int a_rows = 1,bool a_transfer = false) {
    unsigned int oldn = cols*rows;

    std::vector<ptbs_t> ptbss;
    std::vector<prims_t> primss;
    std::vector<todels_t> tdlss;
    std::vector<plotter> pls;
    
    if(a_transfer) {   
      ptbss.resize(oldn);
      primss.resize(oldn);
      tdlss.resize(oldn);
      pls.resize(oldn,plotter(m_ttf));
      for(unsigned int index=0;index<oldn;index++) {
        plotter* p = find_plotter(index);
        p->transfer_plottables(ptbss[index]);
        p->transfer_primitives(primss[index]);
        p->transfer_todels(tdlss[index]);
        pls[index] = *p; //to copy styles.
      }
    }

    cols = a_cols?a_cols:1;
    rows = a_rows?a_rows:1;
    if(view_border.value()) {view_border = (number()==1?false:true);}
    update_sg();
    set_current_plotter(0);
    clear();
    if(a_transfer) {   
      //fill new plotters with old data :
      unsigned int num = min_of(oldn,cols*rows);
      for(unsigned int index=0;index<num;index++) {
        plotter* p = find_plotter(index);
        p->copy_style(pls[index]);  //it must not touch p width, height, depth.

       {const ptbs_t& ptbs = ptbss[index];
        tools_vforcit(plottable*,ptbs,it) p->add_plottable(*it);}
       {const prims_t& prims = primss[index];
        tools_vforcit(plotprim*,prims,it) p->add_primitive(*it);}
       {const todels_t& todels = tdlss[index];
        tools_vforcit(node*,todels,it) p->add_node_todel(*it);}
      }
    }
    reset_touched();
  }

  void current_to_one() {
    ptbs_t ptbs;current_plotter().transfer_plottables(ptbs);
    prims_t prims;current_plotter().transfer_primitives(prims);
    todels_t tdls;current_plotter().transfer_todels(tdls);
    plotter pl = current_plotter(); //to copy styles.

    set_regions(1,1,false);

    plotter& p = current_plotter();
    p.copy_style(pl); //copy styles.

   {tools_vforcit(plottable*,ptbs,it) p.add_plottable(*it);}
   {tools_vforcit(plotprim*,prims,it) p.add_primitive(*it);}
   {tools_vforcit(node*,tdls,it) p.add_node_todel(*it);}
    
  }
  
  void plotters(std::vector<plotter*>& a_vec) { //a_vec do NOT get ownership of sg::plotter objects. Use in G4Analysis.
    a_vec.clear();
    update_if_touched();
    size_t _number = m_sep.size();    
    for(size_t index=0;index<_number;index++) {
      separator* sep = (separator*)m_sep[index];
      plotter* _plotter = (plotter*)(*sep)[PLOTTER()];
      a_vec.push_back(_plotter);
    }    
  }
  
  //////////////////////////////////////////////////////////////////////
  //// styling methods : ///////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////
  void set_line_width(float a_line_width) {
    update_if_touched();
    size_t _number = m_sep.size();    
    for(size_t index=0;index<_number;index++) {
      separator* sep = (separator*)m_sep[index];
      plotter* _plotter = (plotter*)(*sep)[PLOTTER()];
      
      _plotter->bins_style(0).line_width = a_line_width;
      _plotter->inner_frame_style().line_width = a_line_width;
      _plotter->grid_style().line_width = a_line_width;
      _plotter->x_axis().line_style().width = a_line_width;
      _plotter->x_axis().ticks_style().width = a_line_width;
      _plotter->y_axis().line_style().width = a_line_width;
      _plotter->y_axis().ticks_style().width = a_line_width;
      _plotter->z_axis().line_style().width = a_line_width;
      _plotter->z_axis().ticks_style().width = a_line_width;
      _plotter->colormap_axis().line_style().width = a_line_width;
      _plotter->colormap_axis().ticks_style().width = a_line_width;
  
      // needed if font is hershey :
      _plotter->title_style().line_width = a_line_width;
      _plotter->infos_style().line_width = a_line_width;
      _plotter->title_box_style().line_width = a_line_width;
  
      _plotter->x_axis().labels_style().line_width = a_line_width;
      _plotter->x_axis().mag_style().line_width = a_line_width;
      _plotter->x_axis().title_style().line_width = a_line_width;
  
      _plotter->y_axis().labels_style().line_width = a_line_width;
      _plotter->y_axis().mag_style().line_width = a_line_width;
      _plotter->y_axis().title_style().line_width = a_line_width;
  
      _plotter->z_axis().labels_style().line_width = a_line_width;
      _plotter->z_axis().mag_style().line_width = a_line_width;
      _plotter->z_axis().title_style().line_width = a_line_width;

      _plotter->colormap_axis().labels_style().line_width = a_line_width;
      _plotter->colormap_axis().mag_style().line_width = a_line_width;
      _plotter->colormap_axis().title_style().line_width = a_line_width;
    }    
  }

/*
  void set_border_style() {
    update_if_touched();
    size_t _number = m_sep.size();    
    for(size_t index=0;index<_number;index++) {
      separator* sep = (separator*)m_sep[index];
      plotter* _plotter = (plotter*)(*sep)[PLOTTER()];
      _plotter->background_style().visible = true;
      _plotter->background_style().color = colorf_black();
      _plotter->background_style().line_width = 0.003;
    }    
    border_visible = true;
  }
*/

  void set_grids_visibility(bool a_visible = false) {
    update_if_touched();
    size_t _number = m_sep.size();    
    for(size_t index=0;index<_number;index++) {
      separator* sep = (separator*)m_sep[index];
      plotter* _plotter = (plotter*)(*sep)[PLOTTER()];
      _plotter->grid_style().visible = a_visible;
    }    
    border_visible = true;
  }

  void adjust_scales(float a_plotter_scale = 1) {
    // Rescale some plotter parameters (for example margins) according to the number of plots.
    // We assume that these parameters had been set previously according to one plot per page.
    // Then this function must be applied after all the styles had been applied (because
    // a plotting style may set these parameters).
  
    float ww_wc = width.value();
    float wh_wc = height.value();
    float rw_wc = ww_wc/cols.value();
    float rh_wc = wh_wc/rows.value();
  
    float cooking = 1.2f; //if increased the data area is diminished.
  
    float wfac = (rw_wc/ww_wc)*cooking;
    float hfac = (rh_wc/wh_wc)*cooking;
  
    float label_cooking = 1.6f; //if increased the labels are bigger.
  
    if((cols.value()>=4)&&(cols.value()>rows.value())) label_cooking = 0.9f;
  
    float title_cooking = 1.1f; //extra title cooking.
  
    plotter_scale = a_plotter_scale;
  
    update_if_touched();
   {size_t _number = m_sep.size();    
    for(size_t index=0;index<_number;index++) {
      separator* sep = (separator*)m_sep[index];
      plotter* _plotter = (plotter*)(*sep)[PLOTTER()];
  
      _plotter->left_margin = _plotter->left_margin * wfac;
      _plotter->right_margin = _plotter->right_margin * wfac;
      _plotter->bottom_margin = _plotter->bottom_margin * hfac;
      _plotter->top_margin = _plotter->top_margin * hfac;
  
      _plotter->x_axis().tick_length = _plotter->x_axis().tick_length * wfac;
      _plotter->y_axis().tick_length = _plotter->y_axis().tick_length * hfac;
  
      _plotter->title_to_axis = _plotter->title_to_axis * hfac;
      _plotter->title_height = _plotter->title_height * hfac * title_cooking;
  
      _plotter->x_axis().label_height = _plotter->x_axis().label_height * hfac * label_cooking;
      _plotter->y_axis().label_height = _plotter->y_axis().label_height * hfac * label_cooking;
    }}
  }
  
  //gopaw:
  void configure_grid_PAW(unsigned int a_ww,unsigned int a_wh) {
    
    m_origins.clear();
    m_sizes.clear();

    float wvp = float(a_ww); //pixels.
    float hvp = float(a_wh); //pixels.
    if( (wvp<=0)||(hvp<=0)||(width.value()<=0)||(height.value()<=0) ) return;

    unsigned int _cols = cols.value();
    unsigned int _rows = rows.value();
    if((!_cols)||(!_rows)) return;

    float wdata = (width.value()-left_margin.value()-right_margin.value()-(_cols-1)*horizontal_spacing.value())/_cols;
    float hdata = (height.value()-bottom_margin.value()-top_margin.value()-(_rows-1)*vertical_spacing.value())/_rows;
    
    if((wdata<=0)||(hdata<=0)) return;
    
/*
    //    wpix = w * wvp pixels
    //    hpix = h * hvp pixels
    //  wpix/hpix = cst = (w * wvp) / (h * hvp) = width / height
    float h,w,xo,yo;
    h = w = xo = yo = 0;
    float waspect = wvp/hvp;
    float paspect = width.value()/height.value();
    if(waspect>=paspect) {
      h = 1;
      w = paspect * h * (hvp/wvp);
      xo = (1-w)/2;
      yo = 0;
    } else {
      w = 1;
      h = w * (wvp/hvp)/paspect;
      xo = 0;
      yo = (1-h)/2;
    }    
    float xfac = w / width.value();
    float yfac = h / height.value();
*/

    unsigned int _num = number();
    for(unsigned int iregion=0;iregion<_num;iregion++) {
      //iregion = col + row * cols
      unsigned int row = iregion/_cols;
      unsigned int col = iregion - row * _cols;
      
      float wr,hr,x,y,lm,rm,bm,tm;
      wr = hr = x = y = 0;
      lm = rm = bm = tm = 0;
      
      if(_cols==1) {
        wr = width.value();
        x = 0;
        lm = left_margin.value();
        rm = right_margin.value();
      } else {
        float wrl = left_margin.value()+wdata+horizontal_spacing.value()/2;
        float wrr = right_margin.value()+wdata+horizontal_spacing.value()/2;
        float wri = wdata+horizontal_spacing.value();
        if(col==0) {
          wr = wrl;
          x = 0;
          lm = left_margin.value();
          rm = horizontal_spacing.value()/2;
        } else if(col==(_cols-1)) {
          wr = wrr;
          x = width.value() - wrr;
          lm = horizontal_spacing.value()/2;
          rm = right_margin.value();
        } else {
          wr = wri;
          x = wrl + (col-1) * wri;
          lm = horizontal_spacing.value()/2;
          rm = horizontal_spacing.value()/2;
        }
      }
 
      if(_rows==1) {
        hr = height.value();
        y = 0;
        tm = top_margin.value();
        bm = bottom_margin.value();
      } else {
        float hrt = top_margin.value()+hdata+vertical_spacing.value()/2; //top
        float hrb = bottom_margin.value()+hdata+vertical_spacing.value()/2;	//bottom
        float hri = hdata+vertical_spacing.value();
        if(row==0) { //top row.
          hr = hrt;
          y = height.value()-hrt;
          tm = top_margin.value();
          bm = vertical_spacing.value()/2;
        } else if(row==(_rows-1)) {
          hr = hrb;
          y = 0;
          tm = vertical_spacing.value()/2;
          bm = bottom_margin.value();
        } else {
          hr = hri;
          y = height.value()- (hrt + row * hri);
          tm = vertical_spacing.value()/2;
          bm = vertical_spacing.value()/2;
        }
      }

      //m_origins.push_back(vec2f(xo/xfac+x,yo/yfac+y));
      m_origins.push_back(vec2f(x,y));
      m_sizes.push_back(vec2f(wr,hr));

      separator* sep = (separator*)m_sep[iregion];
      plotter* _plotter = (plotter*)(*sep)[PLOTTER()];
      
      //_plotter->width = wr;
      _plotter->left_margin = lm;
      _plotter->right_margin = rm;
      
      //_plotter->height = hr;
      _plotter->top_margin = tm;
      _plotter->bottom_margin = bm;

      // ignore margins and spacings :
      //float wr = w/_cols;
      //float hr = h/_rows; 
      //float x = xo + col * wr;
      //float y = yo + (rows-1-row) * hr;
      //viewportRegion->setPositionPercent(x,y);
      //viewportRegion->setSizePercent(wr,hr);
	    
    }
  }
  
  void configure_extras_PAW(unsigned int a_ww,unsigned int a_wh) {
    
    m_extras_origins.clear();
    m_extras_sizes.clear();

    float wvp = float(a_ww); //pixels.
    float hvp = float(a_wh); //pixels.
    if( (wvp<=0)||(hvp<=0)||(width.value()<=0)||(height.value()<=0) ) return;

    tools_vforcit(extra,m_extras,it) {
      const extra& _extra = *it;

      unsigned int _cols = _extra.m_cols;
      unsigned int _rows = _extra.m_rows;
      if((!_cols)||(!_rows)) continue;

      float wdata = (width.value()-left_margin.value()-right_margin.value()-(_cols-1)*horizontal_spacing.value())/_cols;
      float hdata = (height.value()-bottom_margin.value()-top_margin.value()-(_rows-1)*vertical_spacing.value())/_rows;
    
      if((wdata<=0)||(hdata<=0)) continue;
      
      unsigned int iregion = _extra.m_index;
      unsigned int row = iregion/_cols;
      unsigned int col = iregion - row * _cols;
      
      float wr,hr,x,y,lm,rm,bm,tm;
      wr = hr = x = y = 0;
      lm = rm = bm = tm = 0;
      
      if(_cols==1) {
        wr = width.value();
        x = 0;
        lm = left_margin.value();
        rm = right_margin.value();
      } else {
        float wrl = left_margin.value()+wdata+horizontal_spacing.value()/2;
        float wrr = right_margin.value()+wdata+horizontal_spacing.value()/2;
        float wri = wdata+horizontal_spacing.value();
        if(col==0) {
          wr = wrl;
          x = 0;
          lm = left_margin.value();
          rm = horizontal_spacing.value()/2;
        } else if(col==(_cols-1)) {
          wr = wrr;
          x = width.value() - wrr;
          lm = horizontal_spacing.value()/2;
          rm = right_margin.value();
        } else {
          wr = wri;
          x = wrl + (col-1) * wri;
          lm = horizontal_spacing.value()/2;
          rm = horizontal_spacing.value()/2;
        }
      }
 
      if(_rows==1) {
        hr = height.value();
        y = 0;
        tm = top_margin.value();
        bm = bottom_margin.value();
      } else {
        float hrt = top_margin.value()+hdata+vertical_spacing.value()/2; //top
        float hrb = bottom_margin.value()+hdata+vertical_spacing.value()/2;	//bottom
        float hri = hdata+vertical_spacing.value();
        if(row==0) { //top row.
          hr = hrt;
          y = height.value()-hrt;
          tm = top_margin.value();
          bm = vertical_spacing.value()/2;
        } else if(row==(_rows-1)) {
          hr = hrb;
          y = 0;
          tm = vertical_spacing.value()/2;
          bm = bottom_margin.value();
        } else {
          hr = hri;
          y = height.value()- (hrt + row * hri);
          tm = vertical_spacing.value()/2;
          bm = vertical_spacing.value()/2;
        }
      }

      //m_extras_origins.push_back(vec2f(xo/xfac+x,yo/yfac+y));
      m_extras_origins.push_back(vec2f(x,y));
      m_extras_sizes.push_back(vec2f(wr,hr));

      separator* sep = _extra.m_sep;
      plotter* _plotter = (plotter*)(*sep)[PLOTTER()];
      
      //_plotter->width = wr;
      _plotter->left_margin = lm;
      _plotter->right_margin = rm;
      
      //_plotter->height = hr;
      _plotter->top_margin = tm;
      _plotter->bottom_margin = bm;

      // ignore margins and spacings :
      //float wr = w/_cols;
      //float hr = h/_rows; 
      //float x = xo + col * wr;
      //float y = yo + (rows-1-row) * hr;
      //viewportRegion->setPositionPercent(x,y);
      //viewportRegion->setSizePercent(wr,hr);
	    
    } //tools_vforcit
  } 
  void configure_PAW(unsigned int a_ww,unsigned int a_wh) {
    configure_grid_PAW(a_ww,a_wh);
    configure_extras_PAW(a_ww,a_wh);
    touch();
    update_if_touched();
  }
  //////////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////

  //gopaw:
  void delete_extras() {
    m_extras_sep.clear();
    m_extras.clear();
  }

  plotter* create_extra_plotter(unsigned int a_cols,unsigned int a_rows,unsigned int a_index) {
    // Create a plotter with size and position as if in a grid of a_colsxa_rows and at position a_index.
    // The index numbering being for example for a grid of 3x2 :
    //  0 1 2
    //  3 4 5
    m_extras.push_back(extra(a_cols,a_rows,a_index));
    update_if_touched();
    separator* sep = m_extras.back().m_sep;
    return (plotter*)(*sep)[PLOTTER()];
  }
  plotter* last_extra_plotter() const {
    if(m_extras.empty()) return 0;
    separator* sep = m_extras.back().m_sep;
    return (plotter*)(*sep)[PLOTTER()];    
  }
public:
  void init_sg() {  // used also in gopaw::base_viewer::clean_gstos().
    m_group.clear();
    m_sep.clear();
    m_border_sep.clear();
    m_extras_sep.clear();
    m_group.add(new noderef(m_sep)); 
    m_group.add(new noderef(m_border_sep)); 
    m_group.add(new noderef(m_extras_sep)); 
  }
  void clear_sg() { //used in GL_plots_viewer.
    m_group.clear();
    m_sep.clear();
    m_border_sep.clear();
    m_extras_sep.clear();
  }
protected:
  void update_if_touched() {
    if(touched()) {
      update_sg();
      reset_touched();
    }
  }
  void update_sg(){

    if(m_sep.empty()||(cols.value()!=m_old_cols)||(rows.value()!=m_old_rows)){

      m_old_cols = cols;
      m_old_rows = rows;

      m_sep.clear();

      unsigned int index = 0;
      for(unsigned int irow=0;irow<rows;irow++) {
        for(unsigned int icol=0;icol<cols;icol++) {
          separator* sep = new separator;
          m_sep.add(sep);

          sep->add(new sg::matrix); //MATRIX()

          //head_light* light = new head_light;
          //light->direction = vec3f(1,-1,-10);
          //light->on = false;
          //sep->add(light); //LIGHT()

          _switch* border = new _switch;
          sep->add(border); //BORDER()

          //matrix* tsf = new matrix;
          //sep->add(tsf); //TSF()

          sep->add(new plotter(m_ttf)); //PLOTTER()

          index++;
        } 
      } 

      if(m_current>=m_sep.size()) m_current = 0;
    }

    update_current_border();
    update_border();

    if((width.value()>0)&&(height.value()>0)) {
      size_t _number = m_sep.size();
      
      bool configure = (m_origins.size()==_number)&&(m_sizes.size()==_number)?true:false;

      // all window wc :
      float ww_wc = width;
      float wh_wc = height;
  
      // plotter size in window wc :
      //float rw_wc = ww_wc/cols;
      //float rh_wc = wh_wc/rows;
  
      for(size_t index=0;index<_number;index++) {
        separator* sep = (separator*)m_sep[index];
        set_plotter_layout(*sep,index,configure,cols.value(),rows.value(),
	                   ww_wc,wh_wc,m_origins,m_sizes,plotter_scale.value());
      }    
    }
    
    update_extras();
  }  

  bool copy_plotters(const plots& a_from) {
    update_if_touched();
    if(m_sep.size()==a_from.m_sep.size()) {
      size_t _number = m_sep.size();    
      for(size_t index=0;index<_number;index++) {
        separator* _from_sep = (separator*)a_from.m_sep[index];
        matrix* _from_matrix = (matrix*)(*_from_sep)[MATRIX()];	 
        //_switch* _border = (_switch*)(*_sep)[BORDER()];
        plotter* _from_plotter = (plotter*)(*_from_sep)[PLOTTER()];

        separator* _sep = (separator*)m_sep[index];
        matrix* _matrix = (matrix*)(*_sep)[MATRIX()];	 
        plotter* _plotter = (plotter*)(*_sep)[PLOTTER()];

        _matrix->operator=(*_from_matrix);
        _plotter->operator=(*_from_plotter);
      }
    }
    if(m_extras_sep.size()==a_from.m_extras_sep.size()) {
      size_t _number = m_extras_sep.size();    
      for(size_t index=0;index<_number;index++) {
        separator* _from_sep = (separator*)a_from.m_extras_sep[index];
        matrix* _from_matrix = (matrix*)(*_from_sep)[MATRIX()];	 
        plotter* _from_plotter = (plotter*)(*_from_sep)[PLOTTER()];
      
        separator* _sep = (separator*)m_extras_sep[index];
        matrix* _matrix = (matrix*)(*_sep)[MATRIX()];	 
        plotter* _plotter = (plotter*)(*_sep)[PLOTTER()];
       
        _matrix->operator=(*_from_matrix);
        _plotter->operator=(*_from_plotter);
      }
    }    
    return true;
  }

  static void create_plotter_border(_switch& a_parent,float a_w,float a_h) {
    a_parent.clear();

    group* sep = new group;
    a_parent.add(sep);

    a_parent.add(new group()); //empty

    rgba* mat = new rgba();
    mat->color = colorf_red();
    sep->add(mat);

    draw_style* ds = new draw_style;
    ds->style = draw_lines;
    ds->line_width = 4;
    sep->add(ds);

    vertices* vtxs = new vertices;
    vtxs->mode = gl::line_strip();
    sep->add(vtxs);

    float dw = a_w*0.5f;
    float dh = a_h*0.5f;
    vtxs->add(-dw,-dh,0);
    vtxs->add( dw,-dh,0);
    vtxs->add( dw, dh,0);
    vtxs->add(-dw, dh,0);
    vtxs->add(-dw,-dh,0);
  }

  void update_current_border() {
    size_t _number = m_sep.size();    
    for(size_t index=0;index<_number;index++) {
      separator* sep = (separator*)m_sep[index];
      _switch* _border = (_switch*)(*sep)[BORDER()];
      if(index==m_current) {  
        _border->which = view_border.value()?0:1;
        //if(m_updater) m_updater->update(*this,index);
      } else {
        _border->which = 1;
      }
    }    
  }  

  void update_border() {
    m_border_sep.clear();

    if(!border_visible.value()) return;

    if(width.value()<=0) return;
    if(height.value()<=0) return;
    if(border_width.value()<=0) return;
    if(border_height.value()<=0) return;

    float bw = border_width;
    float bh = border_height;

    // do it with four externals back_area.

    float zz = 0;

    // top :
   {separator* sep = new separator;
    m_border_sep.add(sep);

    float wba = width+2*bw;
    float hba = bh;
    float x = 0;
    float y = height*0.5f+bh*0.5f;

    matrix* _m = new matrix;
    _m->set_translate(x,y,zz);
    sep->add(_m);

    back_area* b = new back_area;
    b->border_visible = false;
    b->color = border_color;      
    b->width = wba;
    b->height = hba;
    sep->add(b);}

    // bottom :
   {separator* sep = new separator;
    m_border_sep.add(sep);

    float wba = width+2*bw;
    float hba = bh;
    float x = 0;
    float y = -height*0.5f-bh*0.5f;

    matrix* _m = new matrix;
    _m->set_translate(x,y,zz);
    sep->add(_m);

    back_area* b = new back_area;
    b->border_visible = false;
    b->color = border_color;      
    b->width = wba;
    b->height = hba;
    sep->add(b);}

    // left :
   {separator* sep = new separator;
    m_border_sep.add(sep);

    float wba = bw;
    float hba = height+2*bh;
    float x = -width*0.5f-bw*0.5f;
    float y = 0;

    matrix* _m = new matrix;
    _m->set_translate(x,y,zz);
    sep->add(_m);

    back_area* b = new back_area;
    b->border_visible = false;
    b->color = border_color;      
    b->width = wba;
    b->height = hba;
    sep->add(b);}

    // right :
   {separator* sep = new separator;
    m_border_sep.add(sep);

    float wba = bw;
    float hba = height+2*bh;
    float x = width*0.5f+bw*0.5f;
    float y = 0;

    matrix* _m = new matrix;
    _m->set_translate(x,y,zz);
    sep->add(_m);

    back_area* b = new back_area;
    b->border_visible = false;
    b->color = border_color;      
    b->width = wba;
    b->height = hba;
    sep->add(b);}

  }
protected:
  class extra {
  public:
    extra(unsigned int a_cols,unsigned int a_rows,unsigned int a_index)
    :m_cols(a_cols),m_rows(a_rows),m_index(a_index),m_sep(0){}
    virtual ~extra(){}
  public:
    extra(const extra& a_from):m_cols(a_from.m_cols),m_rows(a_from.m_rows),m_index(a_from.m_index),m_sep(0){}
    extra& operator=(const extra& a_from) {
      m_cols = a_from.m_cols;
      m_rows = a_from.m_rows;
      m_index = a_from.m_index;
      m_sep = a_from.m_sep;
      return *this;
    }
  public:
    unsigned int m_cols;
    unsigned int m_rows;
    unsigned int m_index;
    separator* m_sep;
  };
  
  void update_extras() {
    if(m_extras.size()!=m_extras_sep.size()) {
      m_extras_sep.clear();
      tools_vforit(extra,m_extras,it) { // same sg layout than grid plotters.
        separator* sep = new separator;
        m_extras_sep.add(sep);
        (*it).m_sep = sep; //*it does not get ownership.
        
        sep->add(new sg::matrix); //MATRIX()
  
        //head_light* light = new head_light;
        //light->direction = vec3f(1,-1,-10);
        //light->on = false;
        //sep->add(light); //LIGHT()
  
        _switch* border = new _switch;
        sep->add(border); //BORDER()
  
        //matrix* tsf = new matrix;
        //sep->add(tsf); //TSF()
  
        sep->add(new plotter(m_ttf)); //PLOTTER()  
      }
    }

    if(width.value()<=0) return;
    if(height.value()<=0) return;
    
    // all window wc :
    float ww_wc = width;
    float wh_wc = height;
  
    size_t _number = m_extras.size();
    
    bool configure = (m_extras_origins.size()==_number)&&(m_extras_sizes.size()==_number)?true:false;
      
    tools_vforcit(extra,m_extras,it) {
      const extra& _extra = *it;
      unsigned int index = _extra.m_index;
      if(index>=(m_extras_sep.size())) index = 0;
      
      separator* sep = _extra.m_sep;
      set_plotter_layout(*sep,index,configure,_extra.m_cols,_extra.m_rows,
                         ww_wc,wh_wc,m_extras_origins,m_extras_sizes,plotter_scale.value());
    }    

  }
protected:
  static void set_plotter_layout(separator& a_sep,size_t a_index,bool a_configure,
                          unsigned int a_cols,unsigned int a_rows,
                          float a_ww_wc,float a_wh_wc,
                          const std::vector<vec2f>& a_origins,const std::vector<vec2f>& a_sizes,float a_scale) {
    size_t row = a_index/a_cols;
    size_t col = a_index-a_cols*row;
      
    float rw_wc = a_ww_wc/a_cols;
    float rh_wc = a_wh_wc/a_rows;

    matrix* _matrix = (matrix*)(a_sep)[MATRIX()];	
    plotter* _plotter = (plotter*)(a_sep)[PLOTTER()];
        
    if(a_configure) {
      _plotter->width = a_sizes[a_index].x();
      _plotter->height = a_sizes[a_index].y();
      float x = -a_ww_wc*0.5f + a_origins[a_index].x() + _plotter->width*0.5f;
      float y = -a_wh_wc*0.5f + a_origins[a_index].y() + _plotter->height*0.5f;
      _matrix->set_translate(x,y,0);
    } else {
      float x = -a_ww_wc*0.5f + col * rw_wc + rw_wc * 0.5f;
      float y =  a_wh_wc*0.5f - row * rh_wc - rh_wc * 0.5f;
      _matrix->set_translate(x,y,0);
    }
    _matrix->mul_scale(a_scale,a_scale,1); //applied first.

   {_switch* _border = (_switch*)(a_sep)[BORDER()];
    create_plotter_border(*_border,rw_wc,rh_wc);
    //_border->which = view_border.value()?(a_index==m_current?0:1):1;
    _border->which = 1;
    }
        
    if(_plotter->shape.value()==plotter::xy) {
      _plotter->depth = min_of(rw_wc,rh_wc);
    } else {      
      //if((rw_wc/rh_wc)>=1.0f) {
      //  _plotter->depth = rh_wc;
      //} else {
      //  _plotter->depth = rh_wc;
      //}
      _plotter->depth = rh_wc;
    }
	  
    if(a_configure) {
    } else {
      if(_plotter->shape.value()==plotter::xy) {
        _plotter->width = rw_wc;
        _plotter->height = rh_wc;
      } else {
        if((rw_wc/rh_wc)>=1.0f) {
          _plotter->width = rh_wc;
          _plotter->height = rh_wc;
        } else {
          _plotter->width = rw_wc;
          _plotter->height = rw_wc;
        }
      }
    }
  }      
protected:
  const base_freetype& m_ttf;

  group m_group;
  separator m_sep;
  separator m_border_sep;
  separator m_extras_sep;
  unsigned int m_current;

  std::vector<extra> m_extras;

  //updater* m_updater;
  unsigned int m_old_cols;
  unsigned int m_old_rows;

  std::vector<vec2f> m_origins;
  std::vector<vec2f> m_sizes;
  std::vector<vec2f> m_extras_origins;
  std::vector<vec2f> m_extras_sizes;

};

}}

#endif
