/* This is a simplistic xml example how to load matrix - data from xml files.
 *
 * For those of you unfamiliar with the standard template library (stl), simply
 * suppose that vector<float> is nothing else than an one dimensional array/buffer of type float,
 * just like float array[10]; 
 * 
 * this (hopefully) simple and self-explaning example was written for libxml2 from 
 * www.xmlsoft.org, wich is avialable across many platforms, including win32.
 *
 * feel free to use this code as a template for your own programs.
 *
 * 04.2002, www.andre-krause.net
*/


// create a file named "matrix.xml" from the following comment:
/*
<?xml version="1.0"?> 
<root>
	<matrix>
		<r>	<c>1.2</c>	<c>3.5</c>	<c>-1.2</c> </r>
		<r>	<c>2.4</c>	<c>1.9</c>	<c>11.8</c> </r>
		<r>	<c>-4.7</c>	<c>6.5</c>	<c>-0.6</c> </r>
	</matrix>

	<matrix>
		<r>	<c>+1.2</c>	<c>3.5</c>	<c>-3.2</c> <c>-3.2</c>	</r>
		<r>	<c>21.4</c>	<c>2.9</c>	<c>11.8</c> <c>-1.2</c>	</r>
		<r>	<c>-4.7</c>	<c>5.5</c>	<c>-8.6</c> <c>-6.2</c>	</r>
		<r>	<c>-4.7</c>	<c>5.5</c>	<c>-8.6</c> <c>-3.9</c>	</r>
	</matrix>


	<matrix>
		<r>	<c>1.2</c>	<c>3.5</c>	<c>-7.2</c> </r>
		<r>	<c>6.4</c>	<c>1.9</c>	<c>+1.8</c> </r>
		<r>	<c>-4.7</c>	<c>6.3</c>	<c>-0.6</c> </r>
		<r>	<c>1.2</c>	<c>3.5</c>	<c>-7.2</c> </r>
		<r>	<c>6.4</c>	<c>1.9</c>	<c>+1.8</c> </r>
		<r>	<c>-4.7</c>	<c>6.3</c>	<c>-0.6</c> </r>
	</matrix>


</root>
*/

//#define LIBXML_STATIC 
//#define ICONV_STATIC 
#include <libxml/tree.h>
#include <libxml/parser.h>


#include <iostream>
#include <vector>
using namespace std;


class matrix
{
	protected:
		// defines a two - dimensional array of type float
		vector< vector< float > > m;

		// xml loading helpers
		void parse_rows(xmlDocPtr doc, xmlNodePtr cur);
		void parse_cols(int a, xmlDocPtr doc, xmlNodePtr cur);
	public:
		int size(){ return m.size(); }
		// access operator to matrix data
		vector<float> & operator[](const int i){ return m[i]; }
		
		// prints matrix
		void print();

		// loads matrix data from file stream
		void load_matrix(xmlDocPtr doc, xmlNodePtr cur) { parse_rows(doc,cur); }
		int save_matrix(const char * fname);
};

// print matrix
void matrix::print()
{
	for(int a=0;a<m.size();a++)
	{
		cout << "\n";
		for(int b=0;b<m[a].size();b++)
		{
			cout << m[a][b] << "\t";
		}
	}
}

void matrix::parse_rows(xmlDocPtr doc, xmlNodePtr cur)
{
	// skip row tag <r> and walk down the xml-tree
    cur = cur->xmlChildrenNode;
    
	// now add columns to the actual matrix row number a
	int a=0;
	while (cur != NULL)
	{
		if (!xmlStrcmp(cur->name, (const xmlChar *)"r"))
		{
			// add empty row to matrix
			m.push_back(vector<float>());
			// fill row with columns
			parse_cols(a,doc,cur);
			a++;
		}
		cur = cur->next;
    }
}


void matrix::parse_cols(int a, xmlDocPtr doc, xmlNodePtr cur)
{

	// skip column tag <c> and walk down the xml-tree
    cur = cur->xmlChildrenNode;
    
	// now add columns to the actual matrix row number a
	while (cur != NULL)
	{
		if (!xmlStrcmp(cur->name, (const xmlChar *)"c"))
			m[a].push_back
			(
				atof((const char*)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1))
			);

		cur = cur->next;
    }
}

int parse_xml(const char * filename)
{
    xmlDocPtr doc;
    xmlNodePtr cur;

    
	//
    // build an XML tree from a the file;
    // 
    doc = xmlParseFile(filename);
    if (doc == NULL) return(NULL);


    //
    // Check the document is of the right kind
    //
    cur = xmlDocGetRootElement(doc);
    if (cur == NULL)
	{
        cerr << "\nempty document";
		xmlFreeDoc(doc);
		return(NULL);
    }
    if (xmlStrcmp(cur->name, (const xmlChar *) "root"))
	{
        cerr << "document of the wrong type, root node != root";
		xmlFreeDoc(doc);
		return(NULL);
    }



    //
    // Now, walk the tree.
    //

	// skip root tag <root> and walk down the xml-tree
    cur = cur->xmlChildrenNode;
    
	int a=0;
	// a list of matrixes to load
	vector< matrix > vm;

    while(cur != NULL)
	{
		if (!xmlStrcmp(cur->name, (const xmlChar *) "matrix"))
		{
			// we have found a matrix
			vm.push_back(matrix());
			vm[a].load_matrix(doc,cur);
			cout << "\n\nmatrix nr. " << a << ":"; vm[a].print();
			a++;
		}
		else
		{
			// do some error reporting
			fprintf(stderr,"document of the wrong type, was '%s', Jobs expected", cur->name);
			cerr << "xmlDocDump follows\n";
			xmlDocDump ( stderr, doc );
			cerr << "xmlDocDump finished\n";
			xmlFreeDoc(doc);
			return(NULL);
		}
		// walk next node
		cur = cur->next;
	}

	// test save
	vm[0].save_matrix("matrix_save.xml");
	return 1;
}


int main()
{
    // important: Do not genrate nodes for formatting spaces
    xmlKeepBlanksDefault(0);

	if( !parse_xml("matrix.xml") )
	{
		cerr << "error opening file.";
		return 1;
	}

    // Clean up everything else before quitting.
    xmlCleanupParser();

    return 0;
}


int matrix::save_matrix(const char * fname)
{

	xmlDocPtr doc;
	xmlNodePtr root;
	xmlNodePtr cur;
	// create new xml document with version 1.0 
	doc = xmlNewDoc((const xmlChar*)"1.0");

	// create a new root node  
	root = xmlNewDocNode(doc, NULL, (const xmlChar*)"root", NULL);
	
	xmlDocSetRootElement(doc, root);

	cur=xmlNewChild(root,NULL,(const xmlChar*)"matrix",NULL);


	xmlNodePtr rows;
	xmlNodePtr cols;

	for(int a=0;a<m.size();a++)
	{
		rows=xmlNewChild(cur,NULL,(const xmlChar*)"r",NULL);
		for(int b=0;b<m[a].size();b++)
		{
			cols=xmlNewChild(rows,NULL,(const xmlChar*)"c",(const xmlChar*)"34.3");
		}
	}
	return xmlSaveFormatFile	(fname, doc,1);

}
