#ifndef __STRUCTURE_H
#define __STRUCTURE_H

#include "Vector3.h"
#include <list>
#include <iostream>
typedef Vector3 Color;
typedef Vector3 Point;

// On va y définir les structures de base

enum {SPHERE=0,PLANE,QUAD,TRIANGLE,SPLINE,SCREEN,LIGHT,RAY,MATERIAL,CAMERA,CONF,OTH};


struct Noeud
{
  unsigned _type;
	std::list<Noeud *> enfants;                   ///< liste de pointeurs sur les enfants
	// Ajoute un enfant
	void attache( Noeud* n )
  { 
    enfants.push_back(n);
  }
  // obtenir le type
  inline
  unsigned type() const
  {
    return _type;
  }

	// Operation d'affichage
	virtual void affiche()
	{
		for(std::list<Noeud*>::const_iterator e=enfants.begin(); e!=enfants.end(); ++e )
    {
			(*e)->affiche();
		}
	}
	// Creer un fils du noeud passe un parametre
	Noeud(unsigned type, Noeud* parent=0 ){ _type = type; if( parent ) parent->attache( this ); }
	// Destructeur (ne fait rien)
	virtual ~Noeud(){};
};

// configuration de toute la scène
struct Conf : public Noeud
{
  unsigned max_level;
  real     gamma;
  real     exposure;
  bool     phong;

	Conf(Noeud* parent=0) : Noeud(CONF,parent){};
	Conf(const unsigned _max_level, const real& _gamma, const real& _exposure, const bool _phong, Noeud* parent=0)
   : Noeud(CONF,parent), max_level(_max_level), gamma(_gamma), exposure(_exposure), phong(_phong)
  {}
  Conf(const Conf& _s,Noeud* parent=0)
   : Noeud(CONF,parent), max_level(_s.max_level), gamma(_s.gamma), exposure(_s.exposure), phong(_s.phong)
  {}

  void affiche()
  {
    std::cout << "Conf: max_level=" << max_level << ", gamma=" << gamma << ", exposure="<<exposure<<",phong="<< phong <<" );"<< std::endl;
  }
};

struct Screen : public Noeud
{
  unsigned width;
  unsigned height;
  Color    fond;

	Screen(Noeud* parent=0) : Noeud(SCREEN,parent){};
	Screen(const unsigned _width, const unsigned _height, Noeud* parent=0)
   : Noeud(SCREEN,parent), width(_width), height(_height)
  {}
  Screen(const Screen& _s,Noeud* parent=0)
   : Noeud(SCREEN,parent), width(_s.width), height(_s.height)
  {}

  void affiche()
  {
    std::cout << "Screen (" << width << "x" << height << ");" << std::endl;
  }

};


struct Camera : public Noeud
{
  Vector3 pos;

	Camera(Noeud* parent=0) : Noeud(CAMERA,parent){};
	Camera(const Vector3& _pos, Noeud* parent=0)
   : Noeud(CAMERA,parent), pos(_pos)
  {}
  Camera(const Camera& _s,Noeud* parent=0)
   : Noeud(CAMERA,parent), pos(_s.pos)
  {}

  void affiche()
  {
    std::cout << "Camera (" << pos.x << "," << pos.y << "," << pos.z << ");" << std::endl;
  }

};




struct Light : public Noeud
{
  Vector3 pos;
  Color   col;

	Light(Noeud* parent=0) : Noeud(LIGHT,parent), pos( Vector3( 0, 0, 0 ) ), col( Vector3( 1, 1, 1 ) ) {};
	Light(const Vector3& _pos, const Color& _col ,Noeud* parent=0)
   : Noeud(LIGHT,parent), pos(_pos), col(_col)
  {}
  Light(const Light& _l,Noeud* parent=0)
   : Noeud(LIGHT,parent), pos(_l.pos), col(_l.col)
  {}

  Light& operator=(const Light& _l)
  {
    this->pos = _l.pos;
    this->col = _l.col;
    return *this;
  }

  void affiche()
  {
    std::cout<<"Light: pos("<<pos.x<<","<<pos.y<<","<<pos.z<<") et col("<<col.x<<","<<col.y<<","<<col.z<<");"<<std::endl;
  }

};

struct Ray : public Noeud
{
	Vector3 origin;
	Vector3 direction;

	Ray(Noeud* parent=0) : Noeud(RAY,parent), origin( Vector3( 0, 0, 0 ) ), direction( Vector3( 0, 0, 0 ) ) {};
	Ray(const Vector3& _origin, const Vector3& _dir ,Noeud* parent=0)
   : Noeud(RAY,parent), origin(_origin), direction(_dir)
  {}
  Ray(const Ray& _r,Noeud* parent=0)
   : Noeud(RAY,parent), origin(_r.origin), direction(_r.direction)
  {}

};


struct Sphere : public Noeud
{
  Vector3    pos;
  real         r;
  unsigned idMat;

  Sphere(Noeud* parent=0) : Noeud(SPHERE,parent) {};
  Sphere(const Vector3& _pos, const real& _r, const unsigned& _idMat, Noeud* parent=0)
    : Noeud(SPHERE,parent), r(_r), pos(_pos), idMat(_idMat)
  {}

  Sphere(const Sphere& _s, Noeud* parent=0)
   : Noeud(SPHERE,parent), r(_s.r), pos(_s.pos), idMat(_s.idMat)
  {}

  void affiche()
  {
    std::cout<<"Sphere: pos("<<pos.x<<","<<pos.y<<","<<pos.z<<") et rayon="<<r<<" et idMat="<<idMat<<std::endl;
  }

};

struct Plane : public Noeud
{
  Vector3 n;
  Vector3 pos;
  unsigned idMat;

  Plane(Noeud* parent=0) : Noeud(PLANE,parent) {};
  Plane(const Vector3& _n, const Vector3& _pos, const unsigned& _idMat, Noeud* parent=0) 
   : Noeud(PLANE,parent), n(_n), pos(_pos), idMat(_idMat) 
  {}
  Plane(const Plane& _p,Noeud* parent=0)
   : Noeud(PLANE,parent), n(_p.n), pos(_p.pos), idMat(_p.idMat)
  {}
  
  void affiche()
  {
    std::cout<<"Plan: n("<<n.x<<","<<n.y<<","<<n.z<<") et idMat="<<idMat<<std::endl;
  }
};


struct Triangle : public Noeud
{
  Vector3 p1;
  Vector3 p2;
  Vector3 p3;
  unsigned idMat;

  Triangle(Noeud* parent=0) : Noeud(TRIANGLE,parent) {};
  Triangle(const Vector3& _p1, const Vector3& _p2, const Vector3& _p3, const unsigned& _idMat, Noeud* parent=0)
   : Noeud(TRIANGLE,parent), p1(_p1),p2(_p2),p3(_p3), idMat(_idMat)
  {}

  Triangle(const Triangle& _t,Noeud* parent=0)
   : Noeud(TRIANGLE,parent), p1(_t.p1),p2(_t.p2),p3(_t.p3),idMat(_t.idMat)
  {}

  void affiche()
  {
    std::cout<<"Triangle: p1("<<p1.x<<","<<p1.y<<","<<p1.z<<") et idMat="<<idMat<<std::endl;
  }
};



struct Material : public Noeud
{
  unsigned id;
  real ref; // reflexion du materiaux
  real dif; // refraction
  real den; // densité
  Color col; // couleur

  Material(Noeud* parent=0)
  : Noeud(MATERIAL,parent), ref(1.0), dif(0.0)
  {
    col = Color(0,0,0);
    id = 0;
    den = 0.0;
  }

  Material(const Color& _c, const real& _r, const real& _d, const unsigned _id, const real& _den,Noeud* parent=0)
   : Noeud(MATERIAL,parent), id(_id), den(_den)
  {
    ref = _r;
    dif = _d;
    col = _c;
  }

  Material(const Material& _mat,Noeud* parent=0)
   : Noeud(MATERIAL,parent), ref(_mat.ref), dif(_mat.dif)
  {
    col = _mat.col;
    id  = _mat.id;
    den = _mat.den;
  }
  
  Material& operator=(const Material& _mat)
  {
    ref = _mat.ref;
    dif = _mat.dif;
    col = _mat.col;
    id  = _mat.id;
    den = _mat.den;
    return *this;
  }

  void affiche()
  {
    std::cout<<"Material: col("<<col.x<<","<<col.y<<","<<col.z<<") et (ref,dif)=("<<ref<<","<<dif<<"),id=" <<id<<",den="<<den<<std::endl;
  }


};



#endif
