#include "Xml.h"

#include <vector>
#include <string>
#include <map>
#include <sstream>
using namespace std;




Xml::Xml(const string& filename, bool verbose, bool validation, bool printInfo)
{
  p_doc = xmlParseFile(filename.c_str());
  if (p_doc == NULL) 
  {
    validDocument = false;
    return;
  }
  p_node = xmlDocGetRootElement(p_doc);
  validDocument = true;

  if (verbose == false)
  {
    cerr.rdbuf(0);
  }

  if (validation) 
  {
    xmlValidCtxt cvp;
    // validation
    cvp.userData = stderr;
    cvp.error = (xmlValidityErrorFunc)     fprintf;
    cvp.warning = (xmlValidityWarningFunc) fprintf;
    if (!xmlValidateDocument(&cvp, p_doc))
      validDocument = false;
  }
}


Xml::~Xml()
{
  xmlFreeDoc(p_doc);
}

bool Xml::documentValid() 
{
  return validDocument;
}

void Xml::tokenize(const string& str,vector<string>& tokens,const string& delimiters) 
{
  string::size_type lastPos = str.find_first_not_of(delimiters, 0);
  string::size_type pos = str.find_first_of(delimiters, lastPos);

  while (string::npos != pos || string::npos != lastPos) 
  {
    tokens.push_back(str.substr(lastPos, pos - lastPos));
    lastPos = str.find_first_not_of(delimiters, pos);
    pos = str.find_first_of(delimiters, lastPos);
  }
}


void Xml::makeScene(Noeud* parent,vector<Noeud *>& obj)
{
  makeScene(parent, p_node, obj);
}

void Xml::getFloatArrayXml(xmlNodePtr node, char* attribname, double x[])
{
  string text = (const char*) xmlGetProp(node, (const xmlChar *) attribname);
  vector<string> tokens;
  tokenize(text, tokens, " ");
  for (int i=0; i<(int)tokens.size(); i++)
  {
    istringstream istr(tokens[i]);
    //x[i] = atof(test.c_str());
    istr >> x[i];
  }
}

void Xml::getFloatXml(xmlNodePtr node, char* attribname,double &x)
{
  string text = (const char*) xmlGetProp(node, (const xmlChar *) attribname);

  istringstream istr(text);
  //x = atof(text.c_str());
  istr >> x;

}

void Xml::getUnsignedXml(xmlNodePtr node, char* attribname,unsigned &x)
{
  string text = (const char*) xmlGetProp(node, (const xmlChar *) attribname);
  istringstream istr(text);
  //x = atoi(text.c_str());
  istr >> x;
}


void Xml::addId(Noeud* noeud, xmlNodePtr node) 
{
  const char* key = (const char*) xmlGetProp(node, (const xmlChar *) "id");
  if (key != NULL) {
    ids[key] = noeud;
  }
}

Noeud* Xml::getId(const string& key)
{
  std::map<const string, Noeud*>::iterator it = ids.find(key);
  if (it != ids.end()) {
    return it->second;
  }
  return NULL;
}

void Xml::manageAttach(Noeud* parent, xmlNodePtr currentNode)
{
  xmlNodePtr child = currentNode->children;
  while (child != NULL) 
  {
    if (child->type == XML_ELEMENT_NODE) 
    {
      string subname = (const char*)child->name;
      if (subname == "attach")
	       parent->attache(getId((const char*)xmlGetProp(child,(const xmlChar *)"child")));
    }
    child = child->next;
  }
}

void Xml::makeScene(Noeud* parent, xmlNodePtr parentNode, vector<Noeud *>& obj)
{
  xmlNodePtr child = parentNode->children;
  while (child != NULL)
  {
    if (child->type == XML_ELEMENT_NODE) 
    {
      string name = (const char*) child->name;
      // Material
      //
      if (name == "material")
      {
      	Material *Mat;
      	Mat = new Material(parent);
      	cerr << "Material *Mat = new Material("  << parentNode->name << ");" << endl;
      	xmlAttrPtr attr = child->properties;
      	// afficher les attributs
      	string attribute;
      	while (attr != NULL) 
        {
      	  attribute = (const char*) attr->name;
      	  if (attribute == "id")
          {
      	    unsigned d;
      	    getUnsignedXml(child, "id", d);
      	    Mat->id = d;
      	    cerr << "M->id(" << d << ");" << endl;
      	  }
          else if (attribute == "color")
          {
      	    double d[3];
      	    getFloatArrayXml(child, "color", d);
      	    Mat->col = Color(d[0], d[1], d[2]);
      	    cerr << "M->color(" << d[0] << ", " << d[1] << ", " << d[2] << ");" << endl;
      	  }
          else if (attribute == "refraction")
          {
      	    double e;
      	    getFloatXml(child, "refraction", e);
      	    Mat->ref = e;
      	    cerr << "M->ref(" << e << ");" << endl;
      	  }
          else if (attribute == "diffraction")
          {
      	    double s;
      	    getFloatXml(child, "diffraction", s);
      	    Mat->dif = s;
      	    cerr << "M->dif(" << s << ");" << endl;
      	  }
          else if (attribute == "density")
          {
      	    double s;
      	    getFloatXml(child, "density", s);
      	    Mat->den = s;
      	    cerr << "M->den(" << s << ");" << endl;
      	  }
      	  attr=attr->next;
      	}
      	obj.push_back(Mat);
      	makeScene(Mat, child, obj);
      }
      // Conf
      // <conf recursion="1" gamma="1" exposure="1" />
      else if (name == "conf")
      {
      	Conf *C;
      	C = new Conf(parent);
      	cerr << "Conf *C = new Conf("  << parentNode->name << ");" << endl;
      	xmlAttrPtr attr = child->properties;
      	// afficher les attributs
      	string attribute;
      	while (attr != NULL)
        {
      	  attribute = (const char*) attr->name;
      	  if (attribute == "recursion")
          {
      	    unsigned rec;
      	    getUnsignedXml(child, "recursion", rec);
      	    C->max_level = rec;
      	    cerr << "C->recursion(" << rec << ");" << endl;
      	  }
      	  else if (attribute == "phong")
          {
      	    unsigned p;
      	    getUnsignedXml(child, "phong", p);
      	    C->phong = ((p==0)?false:true);
      	    cerr << "C->phong(" << p << ");" << endl;
      	  }
          else if (attribute == "gamma")
          {
      	    double gamma;
      	    getFloatXml(child, "gamma", gamma);
      	    C->gamma = gamma;
      	    cerr << "C->gamma(" << gamma << ");" << endl;
      	  }
          else if (attribute == "exposure")
          {
      	    double exposure;
      	    getFloatXml(child, "exposure", exposure);
      	    C->exposure = exposure;
      	    cerr << "C->exposure(" << exposure << ");" << endl;
      	  }
      	  attr=attr->next;
      	}
      	obj.push_back(C);
      	makeScene(C, child, obj);
      }
      // Light
      // <light position="1.00 0.00 200  " color="0.0 1.0 1.0" />
      else if (name == "light")
      {
      	Light *L;
      	L = new Light(parent);
      	cerr << "Light *L = new Light("  << parentNode->name << ");" << endl;
      	xmlAttrPtr attr = child->properties;
      	// afficher les attributs
      	string attribute;
      	while (attr != NULL)
        {
      	  attribute = (const char*) attr->name;
      	  if (attribute == "position")
          {
      	    double d[3];
      	    getFloatArrayXml(child, "position", d);
      	    L->pos = Vector3(d[0], d[1], d[2]);
      	    cerr << "L->pos(" << d[0] << ", " << d[1] << ", " << d[2] << ");" << endl;
      	  }
          else if (attribute == "color")
          {
      	    double d[3];
      	    getFloatArrayXml(child, "color", d);
      	    L->col = Vector3(d[0], d[1], d[2]);
      	    cerr << "L->col(" << d[0] << ", " << d[1] << ", " << d[2] << ");" << endl;
      	  }
      	  attr=attr->next;
      	}
      	obj.push_back(L);
      	makeScene(L, child, obj);
      }
      // Screen
      // <screen width="640" height="480" />
      else if (name == "screen")
      {
      	Screen *S;
      	S = new Screen (parent);
      	cerr << "Screen *S = new Screen("  << parentNode->name << ");" << endl;
      	xmlAttrPtr attr = child->properties;
      	// afficher les attributs
      	string attribute;
      	while (attr != NULL)
        {
      	  attribute = (const char*) attr->name;
      	  if (attribute == "width")
          {
      	    unsigned d;
      	    getUnsignedXml(child, "width", d);
      	    S->width = d;
      	    cerr << "S->width(" << d << ");" << endl;
      	  }
          else if (attribute == "height")
          {
      	    unsigned d;
      	    getUnsignedXml(child, "height", d);
      	    S->height = d;
      	    cerr << "S->height(" << d << ");" << endl;
      	  }
          else if (attribute == "fond")
          {
      	    double d[3];
      	    getFloatArrayXml(child, "fond", d);
      	    S->fond = Color(d[0], d[1], d[2]);
      	    cerr << "S->fond(" << d[0] << ", " << d[1] << ", " << d[2] << ");" << endl;
      	  }
      	  attr=attr->next;
      	}
      	obj.push_back(S);
      	makeScene(S, child, obj);
      }
      // Camera
      else if (name == "camera")
      {
      	Camera *C;
      	C = new Camera (parent);
      	cerr << "Camera *C = new Camera("  << parentNode->name << ");" << endl;
      	xmlAttrPtr attr = child->properties;
      	// afficher les attributs
      	string attribute;
      	while (attr != NULL)
        {
      	  attribute = (const char*) attr->name;
      	  if (attribute == "position")
          {
      	    double d[3];
      	    getFloatArrayXml(child, "position", d);
      	    C->pos = Vector3(d[0], d[1], d[2]);
      	    cerr << "C->pos(" << d[0] << ", " << d[1] << ", " << d[2] << ");" << endl;
      	  }
      	  attr=attr->next;
      	}
      	obj.push_back(C);
      	makeScene(C, child, obj);
      }
      // Sphere
      //   <sphere position="100.0 200.0 00.0" radius="500" idMat="1" />
      else if (name == "sphere")
      {
      	Sphere *S;
        S = new Sphere(parent);
      	cerr << "Sphere *S = new Sphere("  << parentNode->name << ");" << endl;
      	xmlAttrPtr attr = child->properties;
      	  // afficher les attributs
      	string attribute;
      	while (attr != NULL)
        {
      	  attribute = (const char*) attr->name;
      	  if (attribute == "radius")
          {
      	    double d;
      	    getFloatXml(child, "radius", d);
      	    S->r = d;
      	    cerr << "S->r(" << d << ");" << endl;
      	  }
      	  else if (attribute == "idMat")
      	  {
      	    unsigned d;
      	    getUnsignedXml(child, "idMat", d);
      	    S->idMat = d;
      	    cerr << "S->idMat(" << d << ");" << endl;
      	  }
      	  else if (attribute == "position")
      	  {
      	    double d[3];
      	    getFloatArrayXml(child, "position", d);
      	    S->pos = Vector3(d[0], d[1], d[2]);
      	    cerr << "S->pos(" << d[0] << ", " << d[1] << ", " << d[2] << ");" << endl;
      	  }
      	  attr=attr->next;
      	}
      	obj.push_back(S);
      	makeScene(S, child, obj);
      }
      // Triangle
      //   <triangle p1="10 10 0" p2="320 640 0" p3="200 200 400" idMat="4" />
      else if (name == "triangle")
      {
      	Triangle *T;
        T = new Triangle(parent);
      	cerr << "Triangle *T = new Triangle("  << parentNode->name << ");" << endl;
      	xmlAttrPtr attr = child->properties;
      	  // afficher les attributs
      	string attribute;
      	while (attr != NULL)
        {
      	  attribute = (const char*) attr->name;
      	  if (attribute == "p1")
      	  {
      	    double d[3];
      	    getFloatArrayXml(child, "p1", d);
      	    T->p1 = Vector3(d[0], d[1], d[2]);
      	    cerr << "T->p1(" << d[0] << ", " << d[1] << ", " << d[2] << ");" << endl;
      	  }
      	  else if (attribute == "p2")
      	  {
      	    double d[3];
      	    getFloatArrayXml(child, "p2", d);
      	    T->p2 = Vector3(d[0], d[1], d[2]);
      	    cerr << "T->p2(" << d[0] << ", " << d[1] << ", " << d[2] << ");" << endl;
      	  }
      	  else if (attribute == "p3")
      	  {
      	    double d[3];
      	    getFloatArrayXml(child, "p3", d);
      	    T->p3 = Vector3(d[0], d[1], d[2]);
      	    cerr << "T->p3(" << d[0] << ", " << d[1] << ", " << d[2] << ");" << endl;
      	  }
      	  else if (attribute == "idMat")
      	  {
      	    unsigned d;
      	    getUnsignedXml(child, "idMat", d);
      	    T->idMat = d;
      	    cerr << "T->idMat(" << d << ");" << endl;
      	  }
      	  attr=attr->next;
      	}
      	obj.push_back(T);
      	makeScene(T, child, obj);
      }
      // Plan
      //   <plane normal="0.0 1.0 1.0" position="150.0 450.0 500" idMat="1" />
      else if (name == "plane")
      {
      	Plane *P;
        P = new Plane(parent);
      	cerr << "Plan *P = new Plan("  << parentNode->name << ");" << endl;
      	xmlAttrPtr attr = child->properties;
      	  // afficher les attributs
      	string attribute;
      	while (attr != NULL)
        {
      	  attribute = (const char*) attr->name;
      	  if (attribute == "normal")
          {
      	    double d[3];
      	    getFloatArrayXml(child, "normal", d);
      	    P->n = Vector3(d[0], d[1], d[2]);
      	    cerr << "S->n(" << d[0] << ", " << d[1] << ", " << d[2] << ");" << endl;
      	  }
      	  else if (attribute == "position")
      	  {
      	    double d[3];
      	    getFloatArrayXml(child, "position", d);
      	    P->pos = Vector3(d[0], d[1], d[2]);
      	    cerr << "S->pos(" << d[0] << ", " << d[1] << ", " << d[2] << ");" << endl;
      	  }
      	  else if (attribute == "idMat")
      	  {
      	    unsigned d;
      	    getUnsignedXml(child, "idMat", d);
      	    P->idMat = d;
      	    cerr << "P->idMat(" << d << ");" << endl;
      	  }
      	  attr=attr->next;
      	}
      	obj.push_back(P);
      	makeScene(P, child, obj);
      }
      else
      {
      	//cerr << "other child: " << child->name << endl;
      }
    }
    child = child->next;
  }
}

