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

#ifndef tools_sg_draw_style
#define tools_sg_draw_style

#include "node"

#include "lpat"
#include "sf_enum"
#include "render_action"
#include "pick_action"
#include "bbox_action"

namespace tools {
namespace sg {

class draw_style : public node {
  TOOLS_NODE(draw_style,tools::sg::draw_style,node)
public:
  sf_enum<draw_type> style;
  sf<float> line_width;
  sf<lpat> line_pattern;
  sf<float> point_size;
  sf<bool> cull_face;
  sf<bool> winding_ccw;
  sf<bool> point_smooth;
public:
  virtual const desc_fields& node_desc_fields() const {
    TOOLS_FIELD_DESC_NODE_CLASS(tools::sg::draw_style)
    static const desc_fields s_v(parent::node_desc_fields(),7, //WARNING : take care of count.
      TOOLS_ARG_FIELD_DESC(style),
      TOOLS_ARG_FIELD_DESC(line_width),
      TOOLS_ARG_FIELD_DESC(line_pattern),
      TOOLS_ARG_FIELD_DESC(point_size),
      TOOLS_ARG_FIELD_DESC(cull_face),
      TOOLS_ARG_FIELD_DESC(winding_ccw),
      TOOLS_ARG_FIELD_DESC(point_smooth)
    );
    return s_v;
  }
private:
  void add_fields(){
    add_field(&style);
    add_field(&line_width);
    add_field(&line_pattern);
    add_field(&point_size);
    add_field(&cull_face);
    add_field(&winding_ccw);
    add_field(&point_smooth);
  }
public:
  virtual void render(render_action& a_action) {
    state& state = a_action.state();
    _set_state(state);

    if(style.value()==draw_lines) {
      a_action.line_width(state.m_line_width);
    } else if(style.value()==draw_points) {
      a_action.point_size(state.m_point_size);
      a_action.set_point_smooth(state.m_GL_POINT_SMOOTH);
    } else if(style.value()==draw_filled) {
      a_action.set_cull_face(state.m_GL_CULL_FACE);
      a_action.set_winding(state.m_winding);
    }
  }
  virtual void pick(pick_action& a_action) {_set_state(a_action.state());}
  virtual void bbox(bbox_action& a_action) {_set_state(a_action.state());}
public:
  draw_style()
  :parent()
  ,style(draw_filled)
  ,line_width(1) //NOTE : 0 induces a 501 gl error.
  ,line_pattern(line_solid)
  ,point_size(1)
  ,cull_face(true)
  ,winding_ccw(true)
  ,point_smooth(false)
  {
    add_fields();
  }
  virtual ~draw_style(){}
public:
  draw_style(const draw_style& a_from)
  :parent(a_from)
  ,style(a_from.style)
  ,line_width(a_from.line_width)
  ,line_pattern(a_from.line_pattern)
  ,point_size(a_from.point_size)
  ,cull_face(a_from.cull_face)
  ,winding_ccw(a_from.winding_ccw)
  ,point_smooth(a_from.point_smooth)
  {
    add_fields();
  }
  draw_style& operator=(const draw_style& a_from){
    parent::operator=(a_from);

    style = a_from.style;
    line_width = a_from.line_width;
    line_pattern = a_from.line_pattern;
    point_size = a_from.point_size;
    cull_face = a_from.cull_face;
    winding_ccw = a_from.winding_ccw;
    point_smooth = a_from.point_smooth;

    return *this;
  }
protected:
  void _set_state(state& a_state) {
    a_state.m_draw_type = style;
    a_state.m_line_width = line_width;
    a_state.m_line_pattern = line_pattern;
    a_state.m_point_size = point_size;
    a_state.m_GL_CULL_FACE = cull_face;
    a_state.m_winding = winding_ccw.value()?sg::winding_ccw:sg::winding_cw;
    a_state.m_GL_POINT_SMOOTH = point_smooth;
  }
};

}}

#endif
