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

#ifndef tools_sg_perspective
#define tools_sg_perspective

#include "base_camera"

#include "../mathf"

namespace tools {
namespace sg {

class perspective : public base_camera {
  TOOLS_NODE(perspective,tools::sg::perspective,base_camera)
public:
  virtual float near_height() const {
    return 2.0f*znear.value()*ftan(height_angle.value()*0.5f);
  }
  virtual void zoom(float a_fac) {
    //for exa :
    //  a_fac = 0.99f is a zoom in
    //  a_fac = 1.01f is a zoom out
    height_angle.value(height_angle.value()*a_fac);
  }
  virtual camera_type type() const {return camera_perspective;}
public:
  sf<float> height_angle;
public:
  virtual const desc_fields& node_desc_fields() const {
    TOOLS_FIELD_DESC_NODE_CLASS(tools::sg::perspective)
    static const desc_fields s_v(parent::node_desc_fields(),1, //WARNING : take care of count.
      TOOLS_ARG_FIELD_DESC(height_angle)
    );
    return s_v;
  }
private:
  void add_fields(){
    add_field(&height_angle);
  }
public:
  perspective()
  :parent()
  ,height_angle(fpi()/4.0f) //45 degrees.
  {
#ifdef TOOLS_MEM
    mem::increment(s_class().c_str());
#endif
    add_fields();
  }
  virtual ~perspective(){
#ifdef TOOLS_MEM
    mem::decrement(s_class().c_str());
#endif
  }
public:
  perspective(const perspective& a_from)
  :parent(a_from)
  ,height_angle(a_from.height_angle)
  {
#ifdef TOOLS_MEM
    mem::increment(s_class().c_str());
#endif
    add_fields();
  }
  perspective& operator=(const perspective& a_from){
    parent::operator=(a_from);
    height_angle = a_from.height_angle;
    return *this;
  }
public: //operators:
  bool operator==(const perspective& a_from) const{
    if(!parent::operator==(a_from)) return false;
    if(height_angle!=a_from.height_angle) return false;
    return true;
  }
  bool operator!=(const perspective& a_from) const {
    return !operator==(a_from);
  }
public:
  void dump(std::ostream& a_out) {
    parent::dump(a_out);
    a_out << " height_angle " << height_angle.value() << std::endl;
  }

protected:
  virtual void get_lrbt(unsigned int a_ww,unsigned int a_wh,
                        float& a_l,float& a_r,float& a_b,float& a_t) {
    float aspect = float(a_ww)/float(a_wh);
    float hh = near_height()*0.5f;
    a_l = -aspect*hh;
    a_r = aspect*hh;
    a_b = -hh;
    a_t = hh;
  }
};

inline perspective* cast_perspective(base_camera& a_bcam) {return safe_cast<base_camera,perspective>(a_bcam);}

}}

#endif
